filecon 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a432bd710dcfce4c261fb29a0ae260e1114e9b47c6c191fd746542b6579282e
4
- data.tar.gz: fe87db05c3626b48bf4f7986998cec818973038bf1217f493afd2cbc342ad058
3
+ metadata.gz: 7670630693e1cef53e636d2d81dee3779c0237456647b38962c12bda4bb81274
4
+ data.tar.gz: f2b38e25d2900e372287308d0e9c25a9f1c2e214860d63bd5fffc7891675a553
5
5
  SHA512:
6
- metadata.gz: 13b3822013820db54e0aa2a3e85a132caad88448e0ee50e5a31c5e7b10ecfeabb90b0fdabbf1ce410cc503f20b2df9efa26534e867cc16758bbe3d80ada2cf40
7
- data.tar.gz: d5989090b1e00c0e9aca4dfebe27ae64c2793d22de99146d68489deac175ac433e511e9e3a53b3c7c52812e4b34e65afb02d7473d16fcaac017f683b3988e387
6
+ metadata.gz: 602b4f408a63a6783bc7dfd07131ba1f67d94233e928c5d93051d6c355817e10c83f93b797801cad0f5e083374c494deb3585ed45869c889f947e893ef7286c6
7
+ data.tar.gz: d6b91aa19de3911f71b70d3d528e50d43caa911eb03a879566f19543b9d73a8273ec93bee3e70c0ae5b8775544858f5b753211064cf47726a72f894d51e97a14
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## [vx.y.z] — __DATE__
4
4
 
5
+ ## [v0.7.2] — 2026-10-19
6
+
7
+ ### Fixed
8
+ - Recurse (and listing in general) survives filenames with invalid byte sequences — path splitting now works on byte copies and the hidden-file check on scrubbed copies, so a single latin-1 relic no longer kills the whole walk with `invalid byte sequence in UTF-8`; startup history parsing got the same one-word hardening instead of silently discarding all history
9
+ - Same-filesystem `move` renames instead of re-copying — `transfer` tries `File.rename` first (instant, `mv`-style, and it moves symlinks as links), falling back to the rsync copy only across filesystems
10
+ - The `|`-command branch no longer crashes when the command cannot launch (`run` reports the launch failure as its return value, which `dispatch` then tried to read like a stream)
11
+ - The listing refreshes right after a transfer completes, with no keypress: the viewed directory (including a destination reached via the `%dir` pick) is rescanned, the trailing repaint-and-wait steps are gone, and the status line keeps only the tally instead of an echoed command line
12
+
13
+ ## [v0.7.1] — 2026-10-13
14
+
15
+ ### Fixed
16
+ - Windowed commands with an unlaunchable terminal report on the status line (naming the binary and the `+` config key) instead of failing silently; the terminal is exec'd directly so the OS reports a missing binary
17
+ - `=cmd` output keeps split multibyte characters intact — pipe chunks are byte slices, so a UTF-8 character could arrive in two halves; the streaming loop now buffers trailing high bytes until the character completes (genuinely broken bytes still fall back to replacement), and every emitted span is valid UTF-8
18
+ - Pipe results stay on the status line — the bottom line now shows whatever the last action left until the next keypress restores the bar, so the first line of `|` output remains visible instead of being repainted over immediately
19
+
5
20
  ## [v0.7.0] — 2026-10-12
6
21
 
7
22
  ### Changed
data/bin/filecon CHANGED
@@ -22,7 +22,7 @@ begin
22
22
 
23
23
  [dh_path, ch_path].each { |f| FileUtils.touch f }
24
24
 
25
- parse = ->(p) { File.readlines(p).map { |l| s = l.chomp.split(",", 2); [s[0].to_i, s[1]] } rescue [] }
25
+ parse = ->(p) { File.readlines(p).map { |l| s = l.chomp.scrub.split(",", 2); [s[0].to_i, s[1]] } rescue [] }
26
26
 
27
27
  config = Filecon.load_config(bundled_path, "/etc/filecon/config.yaml", config_path)
28
28
 
data/filecon.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH FILECON 1 "2026-10-12" "filecon 0.7.0" "User Commands"
1
+ .TH FILECON 1 "2026-10-19" "filecon 0.7.2" "User Commands"
2
2
  .SH NAME
3
3
  filecon \- a file and command manager that lives between terminal and shell
4
4
  .SH SYNOPSIS
@@ -1,3 +1,3 @@
1
1
  module Filecon
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.2"
3
3
  end
data/lib/filecon.rb CHANGED
@@ -6,6 +6,7 @@ end
6
6
 
7
7
  require "fileutils"
8
8
  require "find"
9
+ require "shellwords"
9
10
  require "yaml"
10
11
  require "tempfile"
11
12
 
@@ -61,7 +62,7 @@ class Filecon::App < Typr::Browser
61
62
  def directory
62
63
  @directory.is_a?(Array) ? @directory.inject { |a, b|
63
64
  common = []
64
- a.split("/").zip(b.split("/")).each { |x, y|
65
+ a.b.split("/").zip(b.b.split("/")).each { |x, y|
65
66
  break if x != y
66
67
  common << x
67
68
  }
@@ -77,7 +78,7 @@ class Filecon::App < Typr::Browser
77
78
  Dir.exist?(d) && File.readable?(d) && File.executable?(d) ? d : nil }
78
79
  prefix = dirs.inject { |a, b|
79
80
  common = []
80
- a.split("/").zip(b.split("/")).each { |x, y|
81
+ a.b.split("/").zip(b.b.split("/")).each { |x, y|
81
82
  break if x != y
82
83
  common << x
83
84
  }
@@ -259,6 +260,8 @@ class Filecon::App < Typr::Browser
259
260
  if File.directory?( src ) and !File.symlink?( src )
260
261
  FileUtils.mkdir_p( dst )
261
262
  done += 1
263
+ elsif op == :move and ( File.rename(src, dst) rescue false )
264
+ done += 1
262
265
  else
263
266
  if @has_rsync.nil?
264
267
  begin
@@ -322,9 +325,8 @@ class Filecon::App < Typr::Browser
322
325
  colors: { default: @colors[:error], border: @colors[:border] },
323
326
  alternate: false).pick(:none)
324
327
  end
325
- show
326
- Typr.read_key
327
- tally
328
+ @user.show tally
329
+ nil
328
330
  end
329
331
 
330
332
  def resolve_conflict src, dst
@@ -397,9 +399,9 @@ class Filecon::App < Typr::Browser
397
399
  fmt = @prefixes&.[](cmd[0].to_s)
398
400
  if fmt
399
401
  body = cmd[1..-1]
400
- body = body.gsub(/[\\']/) { |m| "\\#{m}" } if fmt =~ /'[^']*%s/
402
+ body = body.gsub(/\\|'/, "\\" => "\\\\", "'" => %q{'"'"'}) if fmt =~ /'[^']*%s/
401
403
  result = fmt.gsub('%s') { body }
402
- result.start_with?('!') ? eval(result[1..-1]) : shell(result + " 2>/dev/null", pgroup: true)
404
+ result.start_with?('!') ? eval(result[1..-1]) : (IO.popen(Shellwords.split(result), "r", pgroup: true, err: File::NULL) rescue @user.show "#{(t = result.split.first.to_s).start_with?("$") ? "no terminal configured" : "'#{t}' cannot launch"} — set \"+\" prefix in ~/.config/filecon/config.yaml")
403
405
  else
404
406
  shell(cmd + " 2>&1", "r", in: File::NULL)
405
407
  end
@@ -427,18 +429,33 @@ class Filecon::App < Typr::Browser
427
429
  text = Text.new(top: 1, right: -1, bottom: -2, header: body, margin: ' ',
428
430
  colors: { regex: :generic, border: @colors[:border] }, input: "")
429
431
  io = shell("#{body} 2>&1", "r", in: File::NULL)
432
+ pending = +"".b
430
433
  begin
431
434
  loop do
432
- chunk = io.readpartial(4096)
433
- text << chunk
435
+ pending << io.readpartial(4096)
436
+ s = pending.dup.force_encoding(Encoding::UTF_8)
437
+ unless s.valid_encoding?
438
+ cut = pending.bytesize
439
+ cut -= 1 while cut > 0 and pending.getbyte(cut - 1) >= 0x80
440
+ tail, head = pending.byteslice(cut..-1), pending.byteslice(0...cut).force_encoding(Encoding::UTF_8)
441
+ if !tail.empty? and head.valid_encoding?
442
+ s, pending = head, tail
443
+ else
444
+ s, pending = s.scrub, +"".b
445
+ end
446
+ else
447
+ pending = +"".b
448
+ end
449
+ text << s
434
450
  text.show
435
451
  end
436
452
  rescue EOFError
453
+ text << pending.dup.force_encoding(Encoding::UTF_8).scrub unless pending.empty?
437
454
  end
438
455
  io.close
439
456
  text.pick(:none)
440
457
  elsif cmd[0] == ?|
441
- line = io.gets
458
+ line = io&.gets
442
459
  if line
443
460
  nc, ac = @colors[:commands]
444
461
  head = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : color_code(nc)
@@ -453,7 +470,6 @@ class Filecon::App < Typr::Browser
453
470
  head = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : color_code(nc)
454
471
  @user.show "> #{head}#{cmd[1..-1]}#{color_code(@colors[:default])} : #{output.lines.last}"
455
472
  show
456
- Typr.read_key
457
473
  end
458
474
  end
459
475
  if io.is_a?(IO) and !io.closed?
@@ -467,6 +483,10 @@ class Filecon::App < Typr::Browser
467
483
  end
468
484
  Array(id).sort.reverse.each { |i| refresh_row i } if id && cmd[0..2] != '!cd' && @directory == dir
469
485
  @selected[:rows] = [] if id && @directory != dir
486
+ if id && cmd[0..2] != '!cd' && @directory != dir
487
+ cd @directory
488
+ sort
489
+ end
470
490
  show
471
491
  io
472
492
  end
@@ -588,7 +608,7 @@ class Filecon::App < Typr::Browser
588
608
  return if @directory.is_a?(Array)
589
609
  root = File.expand_path(@directory)
590
610
  dirs = [root] + Dir.glob("**/*", File::FNM_DOTMATCH, base: root).sort.filter_map { |p|
591
- next if !@show_hidden && p.split(?/).any? { |c| c.start_with?(?.) }
611
+ next if !@show_hidden && p.scrub.split(?/).any? { |c| c.start_with?(?.) }
592
612
  d = File.expand_path(p, root)
593
613
  d if File.directory?(d)
594
614
  }
@@ -698,6 +718,7 @@ class Filecon::App < Typr::Browser
698
718
  show
699
719
  draw_hints :rows
700
720
  key = Typr.read_key
721
+ @user.reset
701
722
  next unless key
702
723
  if act = prefixed_binding( key )
703
724
  cmd = build act, nil
@@ -716,14 +737,12 @@ class Filecon::App < Typr::Browser
716
737
  value = send( key )
717
738
  open_file( value, !( key.right? or alt ) ) if value.is_a?( Integer )
718
739
  end
719
- @user.reset
720
740
  else
721
741
  alt = key.is_a?(String) && key[/\A\e(\d)\z/]
722
742
  key = key[1] if alt
723
743
  id = send( key )
724
744
  id = send( id ) while id.is_a? String
725
745
  open_file( id, !alt ) if id.is_a? Integer
726
- @user.reset
727
746
  end
728
747
  end
729
748
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filecon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kilian Reitmayr
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-12 00:00:00.000000000 Z
10
+ date: 2026-10-19 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: typr