/* parse an expression term. It can be one of: * "term" * ["term" <parameters>] */ w_query_expr *w_query_expr_parse(w_query *query, json_t *exp) { w_string_t *name; w_query_expr_parser parser; if (json_is_string(exp)) { name = w_string_new(json_string_value(exp)); } else if (json_is_array(exp) && json_array_size(exp) > 0) { json_t *first = json_array_get(exp, 0); if (!json_is_string(first)) { query->errmsg = strdup( "first element of an expression must be a string"); return NULL; } name = w_string_new(json_string_value(first)); } else { query->errmsg = strdup("expected array or string for an expression"); return NULL; } parser = w_ht_val_ptr(w_ht_get(term_hash, w_ht_ptr_val(name))); if (!parser) { ignore_result(asprintf(&query->errmsg, "unknown expression term '%s'", name->buf)); w_string_delref(name); return NULL; } w_string_delref(name); return parser(query, exp); }
w_string_t *w_fstype(const char *path) { #ifdef __linux__ struct statfs sfs; const char *name = "unknown"; if (statfs(path, &sfs) == 0) { switch (sfs.f_type) { #ifdef CIFS_MAGIC_NUMBER case CIFS_MAGIC_NUMBER: name = "cifs"; break; #endif #ifdef NFS_SUPER_MAGIC case NFS_SUPER_MAGIC: name = "nfs"; break; #endif #ifdef SMB_SUPER_MAGIC case SMB_SUPER_MAGIC: name = "smb"; break; #endif default: name = "unknown"; } } return w_string_new(name); #elif STATVFS_HAS_FSTYPE_AS_STRING struct statvfs sfs; if (statvfs(path, &sfs) == 0) { #ifdef HAVE_STRUCT_STATVFS_F_FSTYPENAME return w_string_new(sfs.f_fstypename); #endif #ifdef HAVE_STRUCT_STATVFS_F_BASETYPE return w_string_new(sfs.f_basetype); #endif } #elif HAVE_STATFS struct statfs sfs; if (statfs(path, &sfs) == 0) { return w_string_new(sfs.f_fstypename); } #endif return w_string_new("unknown"); }
void do_prompt (void) { // FIXME figure out some way to abstract this function WString *string; script_got_prompt (); if (prompting) return; // If we have some text to display, do it if (main_view->buffer != NULL) { warlock_view_end_line (NULL); } string = w_string_new (""); if (script_running) { w_string_append_str (string, "[script:"); w_string_append_str (string, g_strdup_printf ("%d", script_get_linenum())); w_string_append_str (string, "]"); } w_string_append_c (string, '>'); debug ("prompt: %s\n", string->string->str); view_append (main_view, string); w_string_free (string, TRUE); prompting = TRUE; }
w_query_expr *w_expr_suffix_parser(w_query *query, json_t *term) { const char *ignore, *suffix; char *arg; w_string_t *str; int i, l; if (json_unpack(term, "[s,s]", &ignore, &suffix) != 0) { query->errmsg = strdup("must use [\"suffix\", \"suffixstring\"]"); return NULL; } arg = strdup(suffix); if (!arg) { query->errmsg = strdup("out of memory"); return NULL; } l = strlen(arg); for (i = 0; i < l; i++) { arg[i] = tolower(arg[i]); } str = w_string_new(arg); free(arg); if (!str) { query->errmsg = strdup("out of memory"); return NULL; } return w_query_expr_new(eval_suffix, dispose_suffix, str); }
static bool parse_relative_root(w_root_t *root, w_query *res, json_t *query) { json_t *relative_root; w_string_t *path, *canon_path; relative_root = json_object_get(query, "relative_root"); if (!relative_root) { return true; } if (!json_is_string(relative_root)) { res->errmsg = strdup("'relative_root' must be a string"); return false; } path = w_string_new(json_string_value(relative_root)); canon_path = w_string_canon_path(path); res->relative_root = w_string_path_cat(root->root_path, canon_path); res->relative_root_slash = w_string_make_printf("%.*s%c", res->relative_root->len, res->relative_root->buf, WATCHMAN_DIR_SEP); w_string_delref(path); w_string_delref(canon_path); return true; }
bool w_capability_supported(const char *name) { bool res; w_string_t *namestr = w_string_new(name); res = w_ht_get(capabilities, w_ht_ptr_val(namestr)); w_string_delref(namestr); return res; }
bool dispatch_command(struct watchman_client *client, json_t *args) { watchman_command_func func; const char *cmd_name; w_string_t *cmd; if (!json_array_size(args)) { send_error_response(client, "invalid command (expected an array with some elements!)"); return false; } cmd_name = json_string_value(json_array_get(args, 0)); if (!cmd_name) { send_error_response(client, "invalid command: expected element 0 to be the command name"); return false; } cmd = w_string_new(cmd_name); func = (watchman_command_func)w_ht_get(command_funcs, (w_ht_val_t)cmd); w_string_delref(cmd); if (func) { func(client, args); return true; } send_error_response(client, "unknown command %s", cmd_name); return false; }
// Return the normalized (lowercase) filename suffix w_string_t *w_string_suffix(w_string_t *str) { int end; char name_buf[128]; char *buf; /* can't use libc strXXX functions because we may be operating * on a slice */ for (end = str->len - 1; end >= 0; end--) { if (str->buf[end] == '.') { if (str->len - (end + 1) >= sizeof(name_buf) - 1) { // Too long return NULL; } buf = name_buf; end++; while ((unsigned)end < str->len) { *buf = tolower(str->buf[end]); end++; buf++; } *buf = '\0'; return w_string_new(name_buf); } if (str->buf[end] == '/') { // No suffix return NULL; } } // Has no suffix return NULL; }
struct w_clockspec *w_clockspec_parse(json_t *value) { const char *str; uint64_t start_time; int pid; uint32_t root_number; uint32_t ticks; struct w_clockspec *spec; spec = calloc(1, sizeof(*spec)); if (!spec) { return NULL; } if (json_is_integer(value)) { spec->tag = w_cs_timestamp; spec->timestamp.tv_usec = 0; spec->timestamp.tv_sec = json_integer_value(value); return spec; } str = json_string_value(value); if (!str) { free(spec); return NULL; } if (str[0] == 'n' && str[1] == ':') { spec->tag = w_cs_named_cursor; // spec owns the ref to the string spec->named_cursor.cursor = w_string_new(str); return spec; } if (sscanf(str, "c:%" PRIu64 ":%" PRIu32 ":%d:%" PRIu32, &start_time, &pid, &root_number, &ticks) == 4) { spec->tag = w_cs_clock; spec->clock.start_time = start_time; spec->clock.pid = pid; spec->clock.root_number = root_number; spec->clock.ticks = ticks; return spec; } if (sscanf(str, "c:%d:%" PRIu32, &pid, &ticks) == 2) { // old-style clock value (<= 2.8.2) -- by setting clock time and root number // to 0 we guarantee that this is treated as a fresh instance spec->tag = w_cs_clock; spec->clock.start_time = 0; spec->clock.pid = pid; spec->clock.root_number = root_number; spec->clock.ticks = ticks; return spec; } free(spec); return NULL; }
// Simulate static void bench_pending(void) { // These parameters give us 262140 items to track const size_t tree_depth = 7; const size_t num_files_per_dir = 8; const size_t num_dirs_per_dir = 4; w_string_t *root_name = w_string_new("/some/path"); struct pending_list list; const size_t alloc_size = 280000; struct timeval start, end; list.pending = calloc(alloc_size, sizeof(struct watchman_pending_fs)); list.avail = list.pending; list.end = list.pending + alloc_size; // Build a list ordered from the root (top) down to the leaves. build_list(&list, root_name, tree_depth, num_files_per_dir, num_dirs_per_dir); diag("built list with %u items", list.avail - list.pending); // Benchmark insertion in top-down order. { struct watchman_pending_collection coll; struct watchman_pending_fs *item; size_t drained = 0; w_pending_coll_init(&coll); gettimeofday(&start, NULL); for (item = list.pending; item < list.avail; item++) { w_pending_coll_add(&coll, item->path, item->now, item->flags); } drained = process_items(&coll); gettimeofday(&end, NULL); diag("took %.3fs to insert %u items into pending coll", w_timeval_diff(start, end), drained); } // and now in reverse order; this is from the leaves of the filesystem // tree up to the root, or bottom-up. This simulates the workload of // a recursive delete of a filesystem tree. { struct watchman_pending_collection coll; struct watchman_pending_fs *item; size_t drained = 0; w_pending_coll_init(&coll); gettimeofday(&start, NULL); for (item = list.avail - 1; item >= list.pending; item--) { w_pending_coll_add(&coll, item->path, item->now, item->flags); } drained = process_items(&coll); gettimeofday(&end, NULL); diag("took %.3fs to reverse insert %u items into pending coll", w_timeval_diff(start, end), drained); } }
void w_capability_register(const char *name) { if (!capabilities) { capabilities = w_ht_new(128, &w_ht_string_funcs); } w_ht_set(capabilities, w_ht_ptr_val(w_string_new(name)), true); }
// Compute the basename of path, return that as a string w_string_t *w_string_new_basename(const char *path) { const char *base; base = path + strlen(path); while (base > path && base[-1] != WATCHMAN_DIR_SEP) { base--; } return w_string_new(base); }
void w_register_command(struct watchman_command_handler_def *defs) { if (!command_funcs) { command_funcs = w_ht_new(16, &w_ht_string_funcs); } w_ht_set(command_funcs, w_ht_ptr_val(w_string_new(defs->name)), w_ht_ptr_val(defs)); }
void echo (const char *str) { WString *wstr; wstr = w_string_new (str); w_string_add_tag (wstr, ECHO_TAG, 0, -1); view_append (main_view, wstr); }
void register_commands(struct watchman_command_handler_def *defs) { int i; command_funcs = w_ht_new(16, &w_ht_string_funcs); for (i = 0; defs[i].name; i++) { w_ht_set(command_funcs, (w_ht_val_t)w_string_new(defs[i].name), (w_ht_val_t)defs[i].func); } w_query_init_all(); }
w_string_t *w_string_new_wchar(WCHAR *str, int len) { char buf[WATCHMAN_NAME_MAX]; int res; if (len == 0) { return w_string_new(""); } res = WideCharToMultiByte(CP_UTF8, 0, str, len, buf, sizeof(buf), NULL, NULL); if (res == 0) { char msgbuf[1024]; DWORD err = GetLastError(); FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msgbuf, sizeof(msgbuf)-1, NULL); w_log(W_LOG_ERR, "WideCharToMultiByte failed: 0x%x %s\n", err, msgbuf); return NULL; } buf[res] = 0; return w_string_new(buf); }
void w_register_command(struct watchman_command_handler_def *defs) { char capname[128]; if (!command_funcs) { command_funcs = w_ht_new(16, &w_ht_string_funcs); } w_ht_set(command_funcs, w_ht_ptr_val(w_string_new(defs->name)), w_ht_ptr_val(defs)); snprintf(capname, sizeof(capname), "cmd-%s", defs->name); w_capability_register(capname); }
static bool parse_suffixes(w_query *res, json_t *query) { json_t *suffixes; size_t i; suffixes = json_object_get(query, "suffix"); if (!suffixes) { return true; } if (!json_is_array(suffixes)) { res->errmsg = strdup("'suffix' must be an array of strings"); return false; } res->nsuffixes = json_array_size(suffixes); res->suffixes = calloc(res->nsuffixes, sizeof(w_string_t*)); if (!res->suffixes) { return false; } for (i = 0; i < json_array_size(suffixes); i++) { json_t *ele = json_array_get(suffixes, i); char *low; int j; if (!json_is_string(ele)) { res->errmsg = strdup("'suffix' must be an array of strings"); return false; } low = strdup(json_string_value(ele)); if (!low) { res->errmsg = strdup("out of memory"); return false; } for (j = 0; low[j]; j++) { low[j] = tolower(low[j]); } res->suffixes[i] = w_string_new(low); free(low); } return true; }
static bool parse_paths(w_query *res, json_t *query) { json_t *paths; size_t i; paths = json_object_get(query, "path"); if (!paths) { return true; } if (!json_is_array(paths)) { res->errmsg = strdup("'path' must be an array"); return false; } res->npaths = json_array_size(paths); res->paths = calloc(res->npaths, sizeof(res->paths[0])); if (!res->paths) { res->errmsg = strdup("out of memory"); return false; } for (i = 0; i < json_array_size(paths); i++) { json_t *ele = json_array_get(paths, i); const char *name = NULL; w_string_t *path; res->paths[i].depth = -1; if (json_is_string(ele)) { name = json_string_value(ele); } else if (json_unpack(ele, "{s:s, s:i}", "path", &name, "depth", &res->paths[i].depth ) != 0) { res->errmsg = strdup( "expected object with 'path' and 'depth' properties" ); return false; } path = w_string_new(name); res->paths[i].name = w_string_canon_path(path); w_string_delref(path); } return true; }
w_string_t *w_string_path_cat(w_string_t *parent, w_string_t *rhs) { char name_buf[WATCHMAN_NAME_MAX]; if (rhs->len == 0) { w_string_addref(parent); return parent; } snprintf(name_buf, sizeof(name_buf), "%.*s/%.*s", parent->len, parent->buf, rhs->len, rhs->buf); return w_string_new(name_buf); }
bool w_query_register_expression_parser( const char *term, w_query_expr_parser parser) { w_string_t *name = w_string_new(term); if (!name) { return false; } if (!term_hash) { term_hash = w_ht_new(32, &w_ht_string_funcs); } return w_ht_set(term_hash, w_ht_ptr_val(name), w_ht_ptr_val(parser)); }
/* trigger-del /root triggername * Delete a trigger from a root */ static void cmd_trigger_delete(struct watchman_client *client, json_t *args) { w_root_t *root; json_t *resp; const char *name; w_string_t *tname; bool res; root = resolve_root_or_err(client, args, 1, false); if (!root) { return; } if (json_array_size(args) != 3) { send_error_response(client, "wrong number of arguments"); w_root_delref(root); return; } name = json_string_value(json_array_get(args, 2)); if (!name) { send_error_response(client, "expected 2nd parameter to be trigger name"); w_root_delref(root); return; } tname = w_string_new(name); w_root_lock(root); res = w_ht_del(root->commands, w_ht_ptr_val(tname)); w_root_unlock(root); if (res) { w_state_save(); } w_string_delref(tname); resp = make_response(); set_prop(resp, "deleted", json_boolean(res)); set_prop(resp, "trigger", json_string_nocheck(name)); send_and_dispose_response(client, resp); w_root_delref(root); }
bool w_query_register_expression_parser( const char *term, w_query_expr_parser parser) { char capname[128]; w_string_t *name = w_string_new(term); if (!name) { return false; } snprintf(capname, sizeof(capname), "term-%s", term); w_capability_register(capname); if (!term_hash) { term_hash = w_ht_new(32, &w_ht_string_funcs); } return w_ht_set(term_hash, w_ht_ptr_val(name), w_ht_ptr_val(parser)); }
static struct watchman_command_handler_def *lookup( json_t *args, char **errmsg, int mode) { struct watchman_command_handler_def *def; const char *cmd_name; w_string_t *cmd; if (!json_array_size(args)) { ignore_result(asprintf(errmsg, "invalid command (expected an array with some elements!)")); return false; } cmd_name = json_string_value(json_array_get(args, 0)); if (!cmd_name) { ignore_result(asprintf(errmsg, "invalid command: expected element 0 to be the command name")); return false; } cmd = w_string_new(cmd_name); def = w_ht_val_ptr(w_ht_get(command_funcs, w_ht_ptr_val(cmd))); w_string_delref(cmd); if (def) { if (mode && ((def->flags & mode) == 0)) { ignore_result(asprintf(errmsg, "command %s not available in this mode", cmd_name)); return NULL; } return def; } if (mode) { ignore_result(asprintf(errmsg, "unknown command %s", cmd_name)); } return NULL; }
/* unsubscribe /root subname * Cancels a subscription */ void cmd_unsubscribe(struct watchman_client *client, json_t *args) { w_root_t *root; const char *name; w_string_t *sname; bool deleted; json_t *resp; root = resolve_root_or_err(client, args, 1, false); if (!root) { return; } name = json_string_value(json_array_get(args, 2)); if (!name) { send_error_response(client, "expected 2nd parameter to be subscription name"); w_root_delref(root); return; } sname = w_string_new(name); pthread_mutex_lock(&w_client_lock); deleted = w_ht_del(client->subscriptions, w_ht_ptr_val(sname)); pthread_mutex_unlock(&w_client_lock); w_string_delref(sname); resp = make_response(); set_prop(resp, "unsubscribe", json_string_nocheck(name)); set_prop(resp, "deleted", json_boolean(deleted)); send_and_dispose_response(client, resp); w_root_delref(root); }
struct watchman_trigger_command *w_build_trigger_from_def( w_root_t *root, json_t *trig, char **errmsg) { struct watchman_trigger_command *cmd; json_t *ele, *query; json_int_t jint; const char *name = NULL; cmd = calloc(1, sizeof(*cmd)); if (!cmd) { *errmsg = strdup("no memory"); return NULL; } cmd->definition = trig; json_incref(cmd->definition); query = json_pack("{s:O}", "expression", json_object_get(cmd->definition, "expression")); cmd->query = w_query_parse(query, errmsg); json_decref(query); if (!cmd->query) { w_trigger_command_free(cmd); return NULL; } json_unpack(trig, "{s:s}", "name", &name); if (!name) { *errmsg = strdup("invalid or missing name"); w_trigger_command_free(cmd); return NULL; } cmd->triggername = w_string_new(name); cmd->command = json_object_get(trig, "command"); if (cmd->command) { json_incref(cmd->command); } if (!cmd->command || !json_is_array(cmd->command) || !json_array_size(cmd->command)) { *errmsg = strdup("invalid command array"); w_trigger_command_free(cmd); return NULL; } json_unpack(trig, "{s:b}", "append_files", &cmd->append_files); ele = json_object_get(trig, "stdin"); if (!ele) { cmd->stdin_style = input_dev_null; } else if (json_is_array(ele)) { cmd->stdin_style = input_json; if (!parse_field_list(ele, &cmd->field_list, errmsg)) { w_trigger_command_free(cmd); return NULL; } } else if (json_is_string(ele)) { const char *str = json_string_value(ele); if (!strcmp(str, "/dev/null")) { cmd->stdin_style = input_dev_null; } else if (!strcmp(str, "NAME_PER_LINE")) { cmd->stdin_style = input_name_list; } else { ignore_result(asprintf(errmsg, "invalid stdin value %s", str)); w_trigger_command_free(cmd); return NULL; } } else { *errmsg = strdup("invalid value for stdin"); w_trigger_command_free(cmd); return NULL; } jint = 0; // unlimited unless specified json_unpack(trig, "{s:I}", "max_files_stdin", &jint); if (jint < 0) { *errmsg = strdup("max_files_stdin must be >= 0"); w_trigger_command_free(cmd); return NULL; } cmd->max_files_stdin = jint; json_unpack(trig, "{s:s}", "stdout", &cmd->stdout_name); json_unpack(trig, "{s:s}", "stderr", &cmd->stderr_name); if (!parse_redirection(&cmd->stdout_name, &cmd->stdout_flags, "stdout", errmsg)) { w_trigger_command_free(cmd); return NULL; } if (!parse_redirection(&cmd->stderr_name, &cmd->stderr_flags, "stderr", errmsg)) { w_trigger_command_free(cmd); return NULL; } // Copy current environment cmd->envht = w_envp_make_ht(); // Set some standard vars w_envp_set(cmd->envht, "WATCHMAN_ROOT", root->root_path); w_envp_set_cstring(cmd->envht, "WATCHMAN_SOCK", get_sock_name()); w_envp_set(cmd->envht, "WATCHMAN_TRIGGER", cmd->triggername); return cmd; }
// ["dirname", "foo"] -> ["dirname", "foo", ["depth", "ge", 0]] static w_query_expr *dirname_parser_inner(w_query *query, json_t *term, bool caseless) { const char *which = caseless ? "idirname" : "dirname"; struct dirname_data *data; json_t *name; struct w_query_int_compare depth_comp; if (!json_is_array(term)) { ignore_result(asprintf(&query->errmsg, "Expected array for '%s' term", which)); return NULL; } if (json_array_size(term) < 2) { ignore_result(asprintf(&query->errmsg, "Invalid number of arguments for '%s' term", which)); return NULL; } if (json_array_size(term) > 3) { ignore_result(asprintf(&query->errmsg, "Invalid number of arguments for '%s' term", which)); return NULL; } name = json_array_get(term, 1); if (!json_is_string(name)) { ignore_result(asprintf(&query->errmsg, "Argument 2 to '%s' must be a string", which)); return NULL; } if (json_array_size(term) == 3) { json_t *depth; depth = json_array_get(term, 2); if (!json_is_array(depth)) { ignore_result(asprintf(&query->errmsg, "Invalid number of arguments for '%s' term", which)); return NULL; } if (!parse_int_compare(depth, &depth_comp, &query->errmsg)) { return NULL; } if (strcmp("depth", json_string_value(json_array_get(depth, 0)))) { ignore_result(asprintf(&query->errmsg, "Third parameter to '%s' should be a relational depth term", which)); return NULL; } } else { depth_comp.operand = 0; depth_comp.op = W_QUERY_ICMP_GE; } data = calloc(1, sizeof(*data)); if (!data) { ignore_result(asprintf(&query->errmsg, "out of memory")); return NULL; } data->dirname = w_string_new(json_string_value(name)); data->startswith = caseless ? w_string_startswith_caseless : w_string_startswith; data->depth = depth_comp; return w_query_expr_new(eval_dirname, dispose_dirname, data); }
/* subscribe /root subname {query} * Subscribes the client connection to the specified root. */ void cmd_subscribe(struct watchman_client *client, json_t *args) { w_root_t *root; struct watchman_client_subscription *sub; json_t *resp; const char *name; json_t *jfield_list; w_query *query; json_t *query_spec; struct w_query_field_list field_list; char *errmsg; if (json_array_size(args) != 4) { send_error_response(client, "wrong number of arguments for subscribe"); return; } root = resolve_root_or_err(client, args, 1, true); if (!root) { return; } name = json_string_value(json_array_get(args, 2)); if (!name) { send_error_response(client, "expected 2nd parameter to be subscription name"); goto done; } query_spec = json_array_get(args, 3); jfield_list = json_object_get(query_spec, "fields"); if (!parse_field_list(jfield_list, &field_list, &errmsg)) { send_error_response(client, "invalid field list: %s", errmsg); free(errmsg); goto done; } query = w_query_parse(query_spec, &errmsg); if (!query) { send_error_response(client, "failed to parse query: %s", errmsg); free(errmsg); goto done; } sub = calloc(1, sizeof(*sub)); if (!sub) { send_error_response(client, "no memory!"); goto done; } sub->name = w_string_new(name); sub->query = query; memcpy(&sub->field_list, &field_list, sizeof(field_list)); sub->root = root; pthread_mutex_lock(&w_client_lock); w_ht_replace(client->subscriptions, w_ht_ptr_val(sub->name), w_ht_ptr_val(sub)); pthread_mutex_unlock(&w_client_lock); resp = make_response(); annotate_with_clock(root, resp); set_prop(resp, "subscribe", json_string(name)); send_and_dispose_response(client, resp); resp = build_subscription_results(sub, root); if (resp) { send_and_dispose_response(client, resp); } done: w_root_delref(root); }
// Given a json array, concat the elements using a delimiter w_string_t *w_string_implode(json_t *arr, const char *delim) { uint32_t delim_len = u32_strlen(delim); uint32_t len = 0; uint32_t i; w_string_t *s; char *buf; if (json_array_size(arr) == 0) { return w_string_new(""); } if (json_array_size(arr) == 1) { return w_string_new(json_string_value(json_array_get(arr, 0))); } len = ((uint32_t)json_array_size(arr) - 1) * delim_len; for (i = 0; i < json_array_size(arr); i++) { const char *str; str = json_string_value(json_array_get(arr, i)); len += u32_strlen(str); } s = malloc(sizeof(*s) + len + 1); if (!s) { perror("no memory available"); abort(); } s->refcnt = 1; s->slice = NULL; buf = (char*)(s + 1); s->buf = buf; for (i = 0; i < json_array_size(arr); i++) { const char *str; uint32_t l; str = json_string_value(json_array_get(arr, i)); l = u32_strlen(str); memcpy(buf, str, l); // Final string doesn't want delimiter after it if (i == json_array_size(arr) - 1) { buf += l; break; } memcpy(buf + l, delim, delim_len); buf += l + delim_len; } *buf = '\0'; s->len = (uint32_t)(buf - s->buf); s->hval = w_hash_bytes(s->buf, s->len, 0); return s; }
w_string_t *w_fstype(const char *path) { #ifdef __linux__ struct statfs sfs; const char *name = "unknown"; if (statfs(path, &sfs) == 0) { switch (sfs.f_type) { #ifdef CIFS_MAGIC_NUMBER case CIFS_MAGIC_NUMBER: name = "cifs"; break; #endif #ifdef NFS_SUPER_MAGIC case NFS_SUPER_MAGIC: name = "nfs"; break; #endif #ifdef SMB_SUPER_MAGIC case SMB_SUPER_MAGIC: name = "smb"; break; #endif default: name = "unknown"; } } return w_string_new(name); #elif STATVFS_HAS_FSTYPE_AS_STRING struct statvfs sfs; if (statvfs(path, &sfs) == 0) { #ifdef HAVE_STRUCT_STATVFS_F_FSTYPENAME return w_string_new(sfs.f_fstypename); #endif #ifdef HAVE_STRUCT_STATVFS_F_BASETYPE return w_string_new(sfs.f_basetype); #endif } #elif HAVE_STATFS struct statfs sfs; if (statfs(path, &sfs) == 0) { return w_string_new(sfs.f_fstypename); } #endif #ifdef _WIN32 WCHAR *wpath = w_utf8_to_win_unc(path, -1); w_string_t *fstype_name = NULL; if (wpath) { WCHAR fstype[MAX_PATH+1]; HANDLE h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h) { if (GetVolumeInformationByHandleW(h, NULL, 0, 0, 0, 0, fstype, MAX_PATH+1)) { fstype_name = w_string_new_wchar(fstype, -1); } CloseHandle(h); } free(wpath); } if (fstype_name) { return fstype_name; } return w_string_new("unknown"); #else unused_parameter(path); return w_string_new("unknown"); #endif }