/* Compares two file names according to symbolic link target. Returns standard * -1, 0, 1 for comparisons. */ static int compare_targets(const dir_entry_t *f, const dir_entry_t *s) { char full_path[PATH_MAX + 1]; char nlink[PATH_MAX + 1], plink[PATH_MAX + 1]; if((f->type == FT_LINK) != (s->type == FT_LINK)) { /* One of the entries is not a link. */ return (f->type == FT_LINK) ? 1 : -1; } if(f->type != FT_LINK) { /* Both entries are not symbolic links. */ return 0; } /* Both entries are symbolic links. */ get_full_path_of(f, sizeof(full_path), full_path); if(get_link_target(full_path, nlink, sizeof(nlink)) != 0) { return 0; } get_full_path_of(s, sizeof(full_path), full_path); if(get_link_target(full_path, plink, sizeof(plink)) != 0) { return 0; } return stroscmp(nlink, plink); }
int get_link_target_abs(const char link[], const char cwd[], char buf[], size_t buf_len) { char link_target[PATH_MAX]; if(get_link_target(link, link_target, sizeof(link_target)) != 0) { return 1; } if(is_path_absolute(link_target)) { strncpy(buf, link_target, buf_len); buf[buf_len - 1] = '\0'; } else { snprintf(buf, buf_len, "%s/%s", cwd, link_target); } return 0; }
/* Returns increment for curr_y. */ static int show_file_type(FileView *view, int curr_y) { const dir_entry_t *entry; int x; int old_curr_y = curr_y; x = getmaxx(menu_win); entry = &view->dir_entry[view->list_pos]; mvwaddstr(menu_win, curr_y, 2, "Type: "); if(entry->type == FT_LINK) { char full_path[PATH_MAX]; char linkto[PATH_MAX + NAME_MAX]; get_current_full_path(view, sizeof(full_path), full_path); mvwaddstr(menu_win, curr_y, 8, "Link"); curr_y += 2; mvwaddstr(menu_win, curr_y, 2, "Link To: "); if(get_link_target(full_path, linkto, sizeof(linkto)) == 0) { mvwaddnstr(menu_win, curr_y, 11, linkto, x - 11); if(!path_exists(linkto, DEREF)) { mvwaddstr(menu_win, curr_y - 2, 12, " (BROKEN)"); } } else { mvwaddstr(menu_win, curr_y, 11, "Couldn't Resolve Link"); } } else if(entry->type == FT_EXEC || entry->type == FT_REG) { #ifdef HAVE_FILE_PROG char full_path[PATH_MAX]; FILE *pipe; char command[1024]; char buf[NAME_MAX]; get_current_full_path(view, sizeof(full_path), full_path); /* Use the file command to get file information. */ snprintf(command, sizeof(command), "file \"%s\" -b", full_path); if((pipe = popen(command, "r")) == NULL) { mvwaddstr(menu_win, curr_y, 8, "Unable to open pipe to read file"); return 2; } if(fgets(buf, sizeof(buf), pipe) != buf) strcpy(buf, "Pipe read error"); pclose(pipe); mvwaddnstr(menu_win, curr_y, 8, buf, x - 9); if(x > 9 && strlen(buf) > (size_t)(x - 9)) { mvwaddnstr(menu_win, curr_y + 1, 8, buf + x - 9, x - 9); } #else /* #ifdef HAVE_FILE_PROG */ if(entry->type == FT_EXEC) mvwaddstr(menu_win, curr_y, 8, "Executable"); else mvwaddstr(menu_win, curr_y, 8, "Regular File"); #endif /* #ifdef HAVE_FILE_PROG */ } else if(entry->type == FT_DIR) { mvwaddstr(menu_win, curr_y, 8, "Directory"); } #ifndef _WIN32 else if(entry->type == FT_CHAR_DEV || entry->type == FT_BLOCK_DEV) { const char *const type = (entry->type == FT_CHAR_DEV) ? "Character Device" : "Block Device"; char full_path[PATH_MAX]; struct stat st; mvwaddstr(menu_win, curr_y, 8, type); get_current_full_path(view, sizeof(full_path), full_path); if(os_stat(full_path, &st) == 0) { char info[64]; snprintf(info, sizeof(info), "Device Id: 0x%x:0x%x", major(st.st_rdev), minor(st.st_rdev)); curr_y += 2; mvwaddstr(menu_win, curr_y, 2, info); } } else if(entry->type == FT_SOCK) { mvwaddstr(menu_win, curr_y, 8, "Socket"); } #endif else if(entry->type == FT_FIFO) { mvwaddstr(menu_win, curr_y, 8, "Fifo Pipe"); } else { mvwaddstr(menu_win, curr_y, 8, "Unknown"); } curr_y += 2; return curr_y - old_curr_y; }
/* Returns increment for curr_y */ static int show_file_type(FileView *view, int curr_y) { int x; int old_curr_y = curr_y; x = getmaxx(menu_win); mvwaddstr(menu_win, curr_y, 2, "Type: "); if(view->dir_entry[view->list_pos].type == LINK) { char linkto[PATH_MAX + NAME_MAX]; char *filename = view->dir_entry[view->list_pos].name; mvwaddstr(menu_win, curr_y, 8, "Link"); curr_y += 2; mvwaddstr(menu_win, curr_y, 2, "Link To: "); if(get_link_target(filename, linkto, sizeof(linkto)) == 0) { mvwaddnstr(menu_win, curr_y, 11, linkto, x - 11); if(!path_exists(linkto)) mvwaddstr(menu_win, curr_y - 2, 12, " (BROKEN)"); } else { mvwaddstr(menu_win, curr_y, 11, "Couldn't Resolve Link"); } } else if(view->dir_entry[view->list_pos].type == EXECUTABLE || view->dir_entry[view->list_pos].type == REGULAR) { #ifdef HAVE_FILE_PROG FILE *pipe; char command[1024]; char buf[NAME_MAX]; /* Use the file command to get file information */ snprintf(command, sizeof(command), "file \"%s\" -b", view->dir_entry[view->list_pos].name); if((pipe = popen(command, "r")) == NULL) { mvwaddstr(menu_win, curr_y, 8, "Unable to open pipe to read file"); return 2; } if(fgets(buf, sizeof(buf), pipe) != buf) strcpy(buf, "Pipe read error"); pclose(pipe); mvwaddnstr(menu_win, curr_y, 8, buf, x - 9); if(x > 9 && strlen(buf) > x - 9) { mvwaddnstr(menu_win, curr_y + 1, 8, buf + x - 9, x - 9); } #else /* #ifdef HAVE_FILE_PROG */ if(view->dir_entry[view->list_pos].type == EXECUTABLE) mvwaddstr(menu_win, curr_y, 8, "Executable"); else mvwaddstr(menu_win, curr_y, 8, "Regular File"); #endif /* #ifdef HAVE_FILE_PROG */ } else if(view->dir_entry[view->list_pos].type == DIRECTORY) { mvwaddstr(menu_win, curr_y, 8, "Directory"); } #ifndef _WIN32 else if(S_ISCHR(view->dir_entry[view->list_pos].mode)) { mvwaddstr(menu_win, curr_y, 8, "Character Device"); } else if(S_ISBLK(view->dir_entry[view->list_pos].mode)) { mvwaddstr(menu_win, curr_y, 8, "Block Device"); } else if(view->dir_entry[view->list_pos].type == FIFO) { mvwaddstr(menu_win, curr_y, 8, "Fifo Pipe"); } else if(S_ISSOCK(view->dir_entry[view->list_pos].mode)) { mvwaddstr(menu_win, curr_y, 8, "Socket"); } #endif else { mvwaddstr(menu_win, curr_y, 8, "Unknown"); } curr_y += 2; return curr_y - old_curr_y; }
void SocketListener::ingen_listen(Engine* engine, Raul::Socket* unix_sock, Raul::Socket* net_sock) { Ingen::World* world = engine->world(); const std::string link_path(world->conf().option("socket").ptr<char>()); const std::string unix_path(link_path + "." + std::to_string(getpid())); // Bind UNIX socket and create PID-less symbolic link const Raul::URI unix_uri(unix_scheme + unix_path); bool make_link = true; if (!unix_sock->bind(unix_uri) || !unix_sock->listen()) { world->log().error("Failed to create UNIX socket\n"); unix_sock->close(); make_link = false; } else { const std::string old_path = get_link_target(link_path.c_str()); if (!old_path.empty()) { const std::string suffix = old_path.substr(old_path.find_last_of(".") + 1); const pid_t pid = std::stoi(suffix); if (!kill(pid, 0)) { make_link = false; world->log().warn(fmt("Another Ingen instance is running at %1% => %2%\n") % link_path % old_path); } else { world->log().warn(fmt("Replacing old link %1% => %2%\n") % link_path % old_path); unlink(link_path.c_str()); } } if (make_link) { if (!symlink(unix_path.c_str(), link_path.c_str())) { world->log().info(fmt("Listening on %1%\n") % (unix_scheme + link_path)); } else { world->log().error(fmt("Failed to link %1% => %2% (%3%)\n") % link_path % unix_path % strerror(errno)); } } else { world->log().info(fmt("Listening on %1%\n") % unix_uri); } } // Bind TCP socket const int port = world->conf().option("engine-port").get<int32_t>(); std::ostringstream ss; ss << "tcp://*:" << port; if (!net_sock->bind(Raul::URI(ss.str())) || !net_sock->listen()) { world->log().error("Failed to create TCP socket\n"); net_sock->close(); } else { world->log().info(fmt("Listening on TCP port %1%\n") % port); } if (unix_sock->fd() == -1 && net_sock->fd() == -1) { return; // No sockets to listen to, exit thread } struct pollfd pfds[2]; int nfds = 0; if (unix_sock->fd() != -1) { pfds[nfds].fd = unix_sock->fd(); pfds[nfds].events = POLLIN; pfds[nfds].revents = 0; ++nfds; } if (net_sock->fd() != -1) { pfds[nfds].fd = net_sock->fd(); pfds[nfds].events = POLLIN; pfds[nfds].revents = 0; ++nfds; } while (true) { // Wait for input to arrive at a socket const int ret = poll(pfds, nfds, -1); if (ret == -1) { world->log().error(fmt("Poll error: %1%\n") % strerror(errno)); break; } else if (ret == 0) { world->log().warn("Poll returned with no data\n"); continue; } else if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) { break; } if (pfds[0].revents & POLLIN) { SPtr<Raul::Socket> conn = unix_sock->accept(); if (conn) { new SocketServer(*world, *engine, conn); } } if (pfds[1].revents & POLLIN) { SPtr<Raul::Socket> conn = net_sock->accept(); if (conn) { new SocketServer(*world, *engine, conn); } } } if (make_link) { unlink(link_path.c_str()); } }
int iop_cp(io_args_t *const args) { const char *const src = args->arg1.src; const char *const dst = args->arg2.dst; const IoCrs crs = args->arg3.crs; const io_confirm confirm = args->confirm; const int cancellable = args->cancellable; struct stat st; char block[BLOCK_SIZE]; FILE *in, *out; size_t nread; int error; struct stat src_st; const char *open_mode = "wb"; ioeta_update(args->estim, src, dst, 0, 0); #ifdef _WIN32 if(is_symlink(src) || crs != IO_CRS_APPEND_TO_FILES) { DWORD flags; int error; wchar_t *utf16_src, *utf16_dst; flags = COPY_FILE_COPY_SYMLINK; if(crs == IO_CRS_FAIL) { flags |= COPY_FILE_FAIL_IF_EXISTS; } else if(path_exists(dst, DEREF)) { /* Ask user whether to overwrite destination file. */ if(confirm != NULL && !confirm(args, src, dst)) { return 0; } } utf16_src = utf8_to_utf16(src); utf16_dst = utf8_to_utf16(dst); error = CopyFileExW(utf16_src, utf16_dst, &win_progress_cb, args, NULL, flags) == 0; if(error) { /* FIXME: use real system error message here. */ (void)ioe_errlst_append(&args->result.errors, dst, IO_ERR_UNKNOWN, "Copy file failed"); } free(utf16_src); free(utf16_dst); ioeta_update(args->estim, NULL, NULL, 1, 0); return error; } #endif /* Create symbolic link rather than copying file it points to. This check * should go before directory check as is_dir() resolves symbolic links. */ if(is_symlink(src)) { char link_target[PATH_MAX]; int error; io_args_t ln_args = { .arg1.path = link_target, .arg2.target = dst, .arg3.crs = crs, .cancellable = cancellable, .result = args->result, }; if(get_link_target(src, link_target, sizeof(link_target)) != 0) { (void)ioe_errlst_append(&args->result.errors, src, IO_ERR_UNKNOWN, "Failed to get symbolic link target"); return 1; } error = iop_ln(&ln_args); args->result = ln_args.result; if(error != 0) { (void)ioe_errlst_append(&args->result.errors, src, IO_ERR_UNKNOWN, "Failed to make symbolic link"); return 1; } return 0; } if(is_dir(src)) { (void)ioe_errlst_append(&args->result.errors, src, EISDIR, strerror(EISDIR)); return 1; } if(os_stat(src, &st) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return 1; } #ifndef _WIN32 /* Fifo/socket/device files don't need to be opened, their content is not * accessed. */ if(S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode) || S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { in = NULL; } else #endif { in = os_fopen(src, "rb"); if(in == NULL) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return 1; } } if(crs == IO_CRS_APPEND_TO_FILES) { open_mode = "ab"; } else if(crs != IO_CRS_FAIL) { int ec; if(path_exists(dst, DEREF)) { /* Ask user whether to overwrite destination file. */ if(confirm != NULL && !confirm(args, src, dst)) { if(in != NULL && fclose(in) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } return 0; } } ec = unlink(dst); if(ec != 0 && errno != ENOENT) { (void)ioe_errlst_append(&args->result.errors, dst, errno, strerror(errno)); if(in != NULL && fclose(in) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } return ec; } /* XXX: possible improvement would be to generate temporary file name in the * destination directory, write to it and then overwrite destination file, * but this approach has disadvantage of requiring more free space on * destination file system. */ } else if(path_exists(dst, DEREF)) { (void)ioe_errlst_append(&args->result.errors, src, EEXIST, strerror(EEXIST)); if(in != NULL && fclose(in) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } return 1; } #ifndef _WIN32 /* Replicate fifo without even opening it. */ if(S_ISFIFO(st.st_mode)) { if(mkfifo(dst, st.st_mode & 07777) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return 1; } return 0; } /* Replicate socket or device file without even opening it. */ if(S_ISSOCK(st.st_mode) || S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { if(mknod(dst, st.st_mode & (S_IFMT | 07777), st.st_rdev) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return 1; } return 0; } #endif out = os_fopen(dst, open_mode); if(out == NULL) { (void)ioe_errlst_append(&args->result.errors, dst, errno, strerror(errno)); if(fclose(in) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } return 1; } error = 0; if(crs == IO_CRS_APPEND_TO_FILES) { fpos_t pos; /* The following line is required for stupid Windows sometimes. Why? * Probably because it's stupid... Won't harm other systems. */ fseek(out, 0, SEEK_END); error = fgetpos(out, &pos) != 0 || fsetpos(in, &pos) != 0; if(!error) { ioeta_update(args->estim, NULL, NULL, 0, get_file_size(dst)); } } /* TODO: use sendfile() if platform supports it. */ while((nread = fread(&block, 1, sizeof(block), in)) != 0U) { if(cancellable && ui_cancellation_requested()) { error = 1; break; } if(fwrite(&block, 1, nread, out) != nread) { (void)ioe_errlst_append(&args->result.errors, dst, errno, strerror(errno)); error = 1; break; } ioeta_update(args->estim, NULL, NULL, 0, nread); } if(nread == 0U && !feof(in) && ferror(in)) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } if(fclose(in) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); } if(fclose(out) != 0) { (void)ioe_errlst_append(&args->result.errors, dst, errno, strerror(errno)); } if(error == 0 && os_lstat(src, &src_st) == 0) { error = os_chmod(dst, src_st.st_mode & 07777); if(error != 0) { (void)ioe_errlst_append(&args->result.errors, dst, errno, strerror(errno)); } } ioeta_update(args->estim, NULL, NULL, 1, 0); return error; } #ifdef _WIN32 static DWORD CALLBACK win_progress_cb(LARGE_INTEGER total, LARGE_INTEGER transferred, LARGE_INTEGER stream_size, LARGE_INTEGER stream_transfered, DWORD stream_num, DWORD reason, HANDLE src_file, HANDLE dst_file, LPVOID param) { static LONGLONG last_size; io_args_t *const args = param; const char *const src = args->arg1.src; const char *const dst = args->arg2.dst; ioeta_estim_t *const estim = args->estim; if(transferred.QuadPart < last_size) { last_size = 0; } ioeta_update(estim, src, dst, 0, transferred.QuadPart - last_size); last_size = transferred.QuadPart; if(args->cancellable && ui_cancellation_requested()) { return PROGRESS_CANCEL; } return PROGRESS_CONTINUE; } #endif /* TODO: implement iop_chown(). */ int iop_chown(io_args_t *const args); /* TODO: implement iop_chgrp(). */ int iop_chgrp(io_args_t *const args); /* TODO: implement iop_chmod(). */ int iop_chmod(io_args_t *const args); int iop_ln(io_args_t *const args) { const char *const path = args->arg1.path; const char *const target = args->arg2.target; const int overwrite = args->arg3.crs != IO_CRS_FAIL; int result; #ifdef _WIN32 char cmd[6 + PATH_MAX*2 + 1]; char *escaped_path, *escaped_target; char base_dir[PATH_MAX + 2]; #endif #ifndef _WIN32 result = symlink(path, target); if(result != 0 && errno == EEXIST && overwrite && is_symlink(target)) { result = remove(target); if(result == 0) { result = symlink(path, target); if(result != 0) { (void)ioe_errlst_append(&args->result.errors, path, errno, strerror(errno)); } } else { (void)ioe_errlst_append(&args->result.errors, target, errno, strerror(errno)); } } else if(result != 0 && errno != 0) { (void)ioe_errlst_append(&args->result.errors, target, errno, strerror(errno)); } #else if(!overwrite && path_exists(target, DEREF)) { (void)ioe_errlst_append(&args->result.errors, target, EEXIST, strerror(EEXIST)); return -1; } if(overwrite && !is_symlink(target)) { (void)ioe_errlst_append(&args->result.errors, target, IO_ERR_UNKNOWN, "Target is not a symbolic link"); return -1; } escaped_path = shell_like_escape(path, 0); escaped_target = shell_like_escape(target, 0); if(escaped_path == NULL || escaped_target == NULL) { (void)ioe_errlst_append(&args->result.errors, target, IO_ERR_UNKNOWN, "Not enough memory"); free(escaped_target); free(escaped_path); return -1; } if(GetModuleFileNameA(NULL, base_dir, ARRAY_LEN(base_dir)) == 0) { (void)ioe_errlst_append(&args->result.errors, target, IO_ERR_UNKNOWN, "Failed to find win_helper"); free(escaped_target); free(escaped_path); return -1; } break_atr(base_dir, '\\'); snprintf(cmd, sizeof(cmd), "%s\\win_helper -s %s %s", base_dir, escaped_path, escaped_target); result = os_system(cmd); if(result != 0) { (void)ioe_errlst_append(&args->result.errors, target, IO_ERR_UNKNOWN, "Running win_helper has failed"); } free(escaped_target); free(escaped_path); #endif return result; }
/* Expands macros in the *format string advancing the pointer as it goes. The * opt represents conditional expression state, should be zero for non-recursive * calls. Returns newly allocated string, which should be freed by the * caller. */ static LineWithAttrs parse_view_macros(view_t *view, const char **format, const char macros[], int opt) { const dir_entry_t *const curr = get_current_entry(view); LineWithAttrs result = { .line = strdup(""), .attrs = strdup("") }; char c; int nexpansions = 0; int has_expander = 0; if(curr == NULL) { return result; } while((c = **format) != '\0') { size_t width = 0; int left_align = 0; char buf[PATH_MAX + 1]; const char *const next = ++*format; int skip, ok; if(c != '%' || (!char_is_one_of(macros, *next) && !isdigit(*next) && (*next != '=' || has_expander))) { if(strappendch(&result.line, &result.line_len, c) != 0) { break; } continue; } if(*next == '=') { (void)sync_attrs(&result, 0); if(strappend(&result.line, &result.line_len, "%=") != 0 || strappendch(&result.attrs, &result.attrs_len, '=') != 0) { break; } ++*format; has_expander = 1; continue; } if(*next == '-') { left_align = 1; ++*format; } while(isdigit(**format)) { width = width*10 + *(*format)++ - '0'; } c = *(*format)++; skip = 0; ok = 1; buf[0] = '\0'; switch(c) { case 'a': friendly_size_notation(get_free_space(curr_view->curr_dir), sizeof(buf), buf); break; case 't': format_entry_name(curr, NF_FULL, sizeof(buf), buf); break; case 'T': if(curr->type == FT_LINK) { char full_path[PATH_MAX + 1]; char link_path[PATH_MAX + 1]; //add by sim1 get_full_path_of(curr, sizeof(full_path), full_path); //mod by sim1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if(get_link_target(full_path, link_path, sizeof(link_path)) != 0) { copy_str(buf, sizeof(buf), "Failed to resolve link"); } else { snprintf(buf, sizeof(buf), " -> %s", link_path); } //mod by sim1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } break; case 'f': get_short_path_of(view, curr, NF_FULL, 0, sizeof(buf), buf); break; case 'A': #ifndef _WIN32 get_perm_string(buf, sizeof(buf), curr->mode); #else copy_str(buf, sizeof(buf), attr_str_long(curr->attrs)); #endif break; case 'u': get_uid_string(curr, 0, sizeof(buf), buf); break; case 'g': get_gid_string(curr, 0, sizeof(buf), buf); break; case 's': friendly_size_notation(fentry_get_size(view, curr), sizeof(buf), buf); break; //add by sim1 ************************************************ case 'r': { char path[PATH_MAX] = {0}; get_full_path_at(view, view->list_pos, sizeof(path), path); (void)get_rating_string(buf, sizeof(buf), path); } break; case 'n': { int nitems = !fentry_is_dir(curr) ? 0 : (int)fentry_get_nitems(view, curr); snprintf(buf, sizeof(buf), "%d", nitems); } break; //add by sim1 ************************************************ case 'E': { uint64_t size = 0U; typedef int (*iter_f)(view_t *view, dir_entry_t **entry); /* No current element for visual mode, since it can contain truly * empty selection when cursor is on ../ directory. */ iter_f iter = vle_mode_is(VISUAL_MODE) ? &iter_selected_entries : &iter_selection_or_current; dir_entry_t *entry = NULL; while(iter(view, &entry)) { size += fentry_get_size(view, entry); } friendly_size_notation(size, sizeof(buf), buf); } break; case 'd': { struct tm *tm_ptr = localtime(&curr->mtime); strftime(buf, sizeof(buf), cfg.time_format, tm_ptr); } break; case '-': case 'x': skip = expand_num(buf, sizeof(buf), view->filtered); break; case 'l': skip = expand_num(buf, sizeof(buf), view->list_pos + 1); break; case 'L': skip = expand_num(buf, sizeof(buf), view->list_rows + view->filtered); break; case 'S': skip = expand_num(buf, sizeof(buf), view->list_rows); break; case '%': copy_str(buf, sizeof(buf), "%"); break; case 'z': copy_str(buf, sizeof(buf), get_tip()); break; case 'D': if(curr_stats.number_of_windows == 1) { view_t *const other = (view == curr_view) ? other_view : curr_view; //mod by sim1 //copy_str(buf, sizeof(buf), replace_home_part(other->curr_dir)); snprintf(buf, sizeof(buf), " ‖ %s", replace_home_part(other->curr_dir)); } break; case '[': { LineWithAttrs opt = parse_view_macros(view, format, macros, 1); copy_str(buf, sizeof(buf), opt.line); free(opt.line); char *attrs = opt.attrs; if(sync_attrs(&result, 0) && opt.attrs_len > 0U) { if(*attrs != ' ') { result.attrs[result.attrs_len - 1U] = *attrs; } ++attrs; } strappend(&result.attrs, &result.attrs_len, attrs); free(opt.attrs); break; } case ']': if(opt) { if(nexpansions == 0) { replace_string(&result.line, ""); replace_string(&result.attrs, ""); result.line_len = 0U; result.attrs_len = 0U; } if(sync_attrs(&result, 0)) { result.attrs[--result.attrs_len] = '\0'; } return result; } LOG_INFO_MSG("Unmatched %%]"); ok = 0; break; case '{': { /* Try to find matching closing bracket * TODO: implement the way to escape it, so that the expr may contain * closing brackets */ const char *e = strchr(*format, '}'); char *expr = NULL, *resstr = NULL; var_t res = var_false(); ParsingErrors parsing_error; /* If there's no matching closing bracket, just add the opening one * literally */ if(e == NULL) { ok = 0; break; } /* Create a NULL-terminated copy of the given expr. * TODO: we could temporarily use buf for that, to avoid extra * allocation, but explicitly named variable reads better. */ expr = calloc(e - (*format) + 1 /* NUL-term */, 1); memcpy(expr, *format, e - (*format)); /* Try to parse expr, and convert the res to string if succeed. */ parsing_error = parse(expr, 0, &res); if(parsing_error == PE_NO_ERROR) { resstr = var_to_str(res); } if(resstr != NULL) { copy_str(buf, sizeof(buf), resstr); } else { copy_str(buf, sizeof(buf), "<Invalid expr>"); } var_free(res); free(resstr); free(expr); *format = e + 1 /* closing bracket */; } break; case '*': if(width > 9) { snprintf(buf, sizeof(buf), "%%%d*", (int)width); width = 0; break; } (void)sync_attrs(&result, 1); result.attrs[result.attrs_len - 1] = '0' + width; width = 0; break; default: LOG_INFO_MSG("Unexpected %%-sequence: %%%c", c); ok = 0; break; } if(char_is_one_of("tTAugsEd", c) && fentry_is_fake(curr)) { buf[0] = '\0'; } if(!ok) { *format = next; if(strappendch(&result.line, &result.line_len, '%') != 0) { break; } continue; } check_expanded_str(buf, skip, &nexpansions); stralign(buf, width, ' ', left_align); if(strappend(&result.line, &result.line_len, buf) != 0) { break; } } /* Unmatched %[. */ if(opt) { (void)strprepend(&result.line, &result.line_len, "%["); } if(sync_attrs(&result, 0)) { result.attrs[--result.attrs_len] = '\0'; } return result; } /* Makes sure that result->attrs has at least as many elements as result->line * contains characters + extra_width. Returns non-zero if result->attrs has * extra characters compared to result->line. */ static int sync_attrs(LineWithAttrs *result, int extra_width) { const size_t nchars = utf8_strsw(result->line) + extra_width; if(result->attrs_len < nchars) { char *const new_attrs = format_str("%s%*s", result->attrs, (int)(nchars - result->attrs_len), ""); free(result->attrs); result->attrs = new_attrs; result->attrs_len = nchars; } return (result->attrs_len > nchars); } /* Prints number into the buffer. Returns non-zero if numeric value is * "empty" (zero). */ static int expand_num(char buf[], size_t buf_len, int val) { snprintf(buf, buf_len, "%d", val); return (val == 0); }
int flist_find_group(const FileView *view, int next) { /* TODO: refactor/simplify this function (flist_find_group()). */ const int correction = next ? -1 : 0; const int lb = correction; const int ub = view->list_rows + correction; const int inc = next ? +1 : -1; int pos = view->list_pos; dir_entry_t *pentry = &view->dir_entry[pos]; const char *ext = get_last_ext(pentry->name); size_t char_width = utf8_chrw(pentry->name); wchar_t ch = towupper(get_first_wchar(pentry->name)); const SortingKey sorting_key = flist_custom_active(view) && cv_compare(view->custom.type) ? SK_BY_ID : abs(view->sort[0]); const int is_dir = fentry_is_dir(pentry); const char *const type_str = get_type_str(pentry->type); regmatch_t pmatch = { .rm_so = 0, .rm_eo = 0 }; #ifndef _WIN32 char perms[16]; get_perm_string(perms, sizeof(perms), pentry->mode); #endif if(sorting_key == SK_BY_GROUPS) { pmatch = get_group_match(&view->primary_group, pentry->name); } while(pos > lb && pos < ub) { dir_entry_t *nentry; pos += inc; nentry = &view->dir_entry[pos]; switch(sorting_key) { case SK_BY_FILEEXT: if(fentry_is_dir(nentry)) { if(strncmp(pentry->name, nentry->name, char_width) != 0) { return pos; } } if(strcmp(get_last_ext(nentry->name), ext) != 0) { return pos; } break; case SK_BY_EXTENSION: if(strcmp(get_last_ext(nentry->name), ext) != 0) return pos; break; case SK_BY_GROUPS: { regmatch_t nmatch = get_group_match(&view->primary_group, nentry->name); if(pmatch.rm_eo - pmatch.rm_so != nmatch.rm_eo - nmatch.rm_so || (pmatch.rm_eo != pmatch.rm_so && strncmp(pentry->name + pmatch.rm_so, nentry->name + nmatch.rm_so, pmatch.rm_eo - pmatch.rm_so + 1U) != 0)) return pos; } break; case SK_BY_TARGET: if((nentry->type == FT_LINK) != (pentry->type == FT_LINK)) { /* One of the entries is not a link. */ return pos; } if(nentry->type == FT_LINK) { /* Both entries are symbolic links. */ char full_path[PATH_MAX]; char nlink[PATH_MAX], plink[PATH_MAX]; get_full_path_of(nentry, sizeof(full_path), full_path); if(get_link_target(full_path, nlink, sizeof(nlink)) != 0) { return pos; } get_full_path_of(pentry, sizeof(full_path), full_path); if(get_link_target(full_path, plink, sizeof(plink)) != 0) { return pos; } if(stroscmp(nlink, plink) != 0) { return pos; } } break; case SK_BY_NAME: if(strncmp(pentry->name, nentry->name, char_width) != 0) return pos; break; case SK_BY_INAME: if((wchar_t)towupper(get_first_wchar(nentry->name)) != ch) return pos; break; case SK_BY_SIZE: if(nentry->size != pentry->size) return pos; break; case SK_BY_NITEMS: if(entry_get_nitems(view, nentry) != entry_get_nitems(view, pentry)) return pos; break; case SK_BY_TIME_ACCESSED: if(nentry->atime != pentry->atime) return pos; break; case SK_BY_TIME_CHANGED: if(nentry->ctime != pentry->ctime) return pos; break; case SK_BY_TIME_MODIFIED: if(nentry->mtime != pentry->mtime) return pos; break; case SK_BY_DIR: if(is_dir != fentry_is_dir(nentry)) { return pos; } break; case SK_BY_TYPE: if(get_type_str(nentry->type) != type_str) { return pos; } break; #ifndef _WIN32 case SK_BY_GROUP_NAME: case SK_BY_GROUP_ID: if(nentry->gid != pentry->gid) return pos; break; case SK_BY_OWNER_NAME: case SK_BY_OWNER_ID: if(nentry->uid != pentry->uid) return pos; break; case SK_BY_MODE: if(nentry->mode != pentry->mode) return pos; break; case SK_BY_PERMISSIONS: { char nperms[16]; get_perm_string(nperms, sizeof(nperms), nentry->mode); if(strcmp(nperms, perms) != 0) { return pos; } break; } case SK_BY_NLINKS: if(nentry->nlinks != pentry->nlinks) { return pos; } break; #endif } /* Id sorting is builtin only and is defined outside SortingKey * enumeration. */ if((int)sorting_key == SK_BY_ID) { if(nentry->id != pentry->id) { return pos; } } } return pos; } /* Finds pointer to the beginning of the last extension of the file name. * Returns the pointer, which might point to the NUL byte if there are no * extensions. */ static const char * get_last_ext(const char name[]) { const char *const ext = strrchr(name, '.'); return (ext == NULL) ? (name + strlen(name)) : (ext + 1); } int flist_find_dir_group(const FileView *view, int next) { const int correction = next ? -1 : 0; const int lb = correction; const int ub = view->list_rows + correction; const int inc = next ? +1 : -1; int pos = curr_view->list_pos; dir_entry_t *pentry = &curr_view->dir_entry[pos]; const int is_dir = fentry_is_dir(pentry); while(pos > lb && pos < ub) { dir_entry_t *nentry; pos += inc; nentry = &curr_view->dir_entry[pos]; if(is_dir != fentry_is_dir(nentry)) { break; } } return pos; } int flist_first_sibling(const FileView *view) { const int parent = view->list_pos - view->dir_entry[view->list_pos].child_pos; return (parent == view->list_pos ? 0 : parent + 1); } int flist_last_sibling(const FileView *view) { int pos = view->list_pos - view->dir_entry[view->list_pos].child_pos; if(pos == view->list_pos) { /* For top-level entry, find the last top-level entry. */ pos = view->list_rows - 1; while(view->dir_entry[pos].child_pos != 0) { pos -= view->dir_entry[pos].child_pos; } } else { /* For non-top-level entry, go to last tree item and go up until our * child. */ const int parent = pos; pos = parent + view->dir_entry[parent].child_count; while(pos - view->dir_entry[pos].child_pos != parent) { pos -= view->dir_entry[pos].child_pos; } } return pos; }