TFMAN

Maciej Helminiak <tfman.project@opmbx.org>
modified: 2014-07-15



NAME

tfman - text file manager

SYNOPSIS

  tfman [-M scan] [DIR...] [OPTION...]
  tfman [-M parse] [FILE...] [OPTION...]
  
  tfm DIR... [OPTION...]
  tfm
  tfm -h

DESCRIPTION

General

Tfman is text file manager managing files via textual representation of file system (being ordinary text). It can scan directories to create textual representation or parse textual representation to retrieve actions specified by user.

. Written in C for Unix-like systems.
. Does not depend on external libraries.
. Can cooperate with command-line tools, like Sed, via standard input and output streams.
. Can be integrated with text editing tools, like Vim.

Tfman has two working modes: scan mode and parse mode.

Scan mode

The scan mode performs scanning on specified part of file system (directory or whole tree of directories) and composes textual representation of it. Result is send to standard output (this is default) or written to a file.

Created textual representation is formatted in a manner allowing direct use in parse mode. User can edit it as normal text and add action tags.

Parse mode

The parse mode retrieves actions specified in textual representation and performs them on file system. Textual representation is read from standard input (default) or from file. Multiple actions of same or different types can be specified on multiple entries, whole directories and groups.

The textual representation must be in specific format (but it is still ordinary text) and actions are named by tags.

tfm

Tfm is convenience tool that combines scan mode and parse mode to make Tfman use easier. It does scanning of directories, editing textual representation and parsing result in one step.

OPTIONS

GENERAL OPTIONS

-h --help
Print help and exit.

-v, --verbose
Run in verbose mode.

-q, --quiet
Run in quiet mode, suppress all information about execution, overrides '-v'.

-M, --mode=MODE
Choose program working mode: scan or parse. There is no need to use this option as mode is deduced from type of file given as command-line argument.

SCAN OPTIONS

-f, --file=FILE
Write output to FILE (overwrite) instead of stdout.

-a, --append=FILE
Append output to FILE instead of stdout.

-R, --recursive[=LVL]
Scan directories recursively. Stop scanning at subdirectory level LVL (default 0). Level 0 - all the way to last subdirectory level, Level 1 - top directory only (no recursion), 2 and more - first and more subdirectory levels

-S, --sort=FIELD,FIELD...
Sort entries using specified fields. Default sort order is: type, name, size. Values of other fields: atime,mtime,ctime,user,group and corresponding values with added 'b', like nameb, mtimeb, used for reverse sorting.

-F, --format=FIELD,FIELD...
Compose entries from specifed fields in given order. Default fields order in entry is: name,type,perms,entrs,user,group,lntar,separator,size,size_sparse, atime,mtime,ctime,proper. Those are all possible values, where perms is permissions, entrs is number of entries if entry is directory, lntar is target name if entry is symlink, size_sparse is size with sparse file data, proper is properties like bitrate, length, version, compression etc.

-I, --indent=N
Set indentation step to N. Default is 2.

-C, --clone-names
Clone name for each entry. Additional field with name is inserted to make adding action tags easier.

PARSE OPTIONS

-s, --show-only
Show actions deduced from parsed file and quit.

-n, --no-ask
Execute actions without asking for confirmation.

-N, --names-only
Print only prepared names for each entry, do not perform any actions.

-l, --lines-only=L1[,L2]
Perform actions only for lines from L1 to L2, if L2 is not specified it defaults to last line.

TFM

Tfm performs scanning of specified directory, opens resulting textual representation in editor retrieved from EDITOR shell variable and performs parsing after editor is closed. It takes the same options as Tfman. Invoked without command-line arguments opens previously edited textual representation.

Scan, edit and parse Dir with recursion and names cloning:

  tfm Dir -R -C

TFMAN INSIDE VIM

Key mappings for commands are in single quotes, assuming that your 'mapleader' is '\'.

Scan commands:

  :Tsr DIR RLVL       - open new or goto first existing Tfman edit window and san DIR with recursion level RLVL
  :Tsa DIR RLVL       - same but append result to buffer
  :Tsw DIR RLVL       - same but always open new window
  :Tst DIR RLVL       - same in new tabpage
  
  :To        'ENTER'  - scan entry under cursor
  :Toa                - scan entry under cursor, append to buffer
  :Tow                - scan entry under cursor, new window
  :Tot                - scan entry under cursor, new tabpage
  
  :Tpar N    '-'      - scan parent of current directory, N levels up in hierarchy
  :Tpara N            - scan parent of current directory, append to buffer
  :Tparw N            - scan parent of current directory, new window
  :Tpart N            - scan parent of current directory, new tabpage

History commands:

  :Tr        '\}'     - rescan current directory
  :Tn        '\]'     - next directory in history
  :Tp        '\['     - previous directory in history
  :Th N               - Nth directory in history
  :The                - show history

Parse commands:

  :Tsh       '\S'     - show actions to perform in existing or new message window
  :T         '\P'     - parse current or first edit window in tabpage, show result in edit window
  :Te                 - mark current window as edit window

Other commands:

  :Tins ...           - insert command arguments under cursor, utilizes tab completion
  
  :Temv [L1 L2]    '\m'   - add move action tag to current line or to lines from L1 to L2 (defaults to last line)
  :Temvo [L1 L2]   '\M'   - add move-overwrite action tag to current line or to lines from L1 to L2 (defaults to last line)
  :Tecp [L1 L2]    '\c'   - add copy action tag to current line or to lines from L1 to L2 (defaults to last line)
  :Tecpo [L1 L2]   '\C'   - add copy-overwrite action tag to current line or to lines from L1 to L2 (defaults to last line)
  :Term [L1 L2]    '\r'   - add remove action tag to current line or to lines from L1 to L2 (defaults to last line)
  :Tecreat [L1 L2] '\t'   - add create action tag to current line or to lines from L1 to L2 (defaults to last line)
  
  :Tarm [L1 L2]    '\R'   - remove immedaiately current line or lines from L1 to L2 (defaults to last line)
  
  '<C-F>'                 - ctrl-F works only in insert mode, it will complete file path inserted after '>' character
                          (it is intended to work with copy/move/links tags)

TEXTUAL REPRESENTATION FORMAT

Textual representation's basic element is line which represents one entry. Line can be inactive or active. Inactive lines are ignored, active lines are parsed for actions. Line is active when its first non-blank character is line type tag (also called line tag). Line tag determines line type. Type determines how entry name is prepared and what is affected by actions specified in line.

Parsing process is accompanied by two important concepts - current working directory and current parent directory. They are prepended to each entry name and to each operand being file name.

Current working directory is used only when name or operand begins with './'. It never changes and is ofcourse the same as directory where Tfman was invoked.

Current parent directory is used for each name and operand which name do not begin with './' or '/'. It is set to current working directory when parsing begins. Later, during parsing process, when directory entry is encountered, its value changes according to indentation. If directory is not indented at all, it becomes new value of current parent directory. If it is less indented then previous directory, it is appended to one of parent's ancestors, as many levels up the hierarchy as many indentation steps it is retracted. If it is more indented then it is simply appended to parent directory.

'# DIR'
directory, change parent directory to DIR or to current_parent_dir/DIR or to current_parent_dir_ancestor/DIR - depends on indentation. Perform actions specified in this line
'| ENTRY'
normal entry, prepare its name by prepending parent directory, perform actions specified in this line
'$ ENTRY'
priority entry, prepare its name by prepending parent directory, perform actions specified in this line, all actions in this line are priority actions
'{' '}'
group lines, do not include directories and subdirectories, actions specified in this line apply to all grouped entries
'{{' '}}'
group lines, include directories and subdirectories, actions specified in this line apply to all grouped entries
'END'
end parsing now

Line is made of beforementioned line tag (mandatory), name field (mandatory), action tags and other fields. Fields are separated by spaces. Name field must be first non-action field after line tag or be enclosed in brackets '[name]'. Order of action tags and other fields is arbitrary. Some action tags take one operand, which can be file or non-file string. There can not be space between tag and operand.

  LINE TAG    NAME FIELD     ACTION TAGS       OTHER FIELDS
  #           Dirname        ~                 user group
  |           [entry_name]   ~ :rwxrwxrwx      user group

Actions are performed in fixed order, type by type. Same type actions are performed in order of appearance in text. Priority actions (those in priority lines) are performed before all normal actions and not in fixed order but in order of appearance.

'~'
create empty file or directory (to create directory end its name with '/')
'->TARGET'
create entry as symlink pointing to TARGET
'->*TARGET'
create entry as symlink pointing to TARGET, overwrite if entry exists
'=>TARGET'
create entry as hard link pointing to TARGET
'=>*TARGET'
create entry as hard link pointing to TARGET, overwrite if entry exists
'USER:GROUP:PERMS'
change file properties: owning user, group and permissions (in format 'rwxrwxrwx'). USER and GROUP may be replaced by PERMS, for ex: USER:PERMS or PERMS:GROUP. You can also omit any part, for ex: USER:GROUP or USER: or :GROUP.
'>ENTRY_NAME'
copy file to ENTRY_NAME, if ENTRY_NAME exist 'Tfman' terminates, unless ENTRY_NAME is directory, then entry is copied into it
'>*ENTRY_NAME'
same as above but overwrite if ENTRY_NAME exists and is not directory
'>>ENTRY_NAME'
copy file to ENTRY_NAME, if ENTRY_NAME exist 'Tfman' terminates, unless ENTRY_NAME is directory, then entry is moved into it
'>>*ENTRY_NAME'
same as above but overwrite if ENTRY_NAME exists and is not directory
'*'
remove entry - file or directory (watch out for non-empty directories)

Indentation is important when dealing with directories. If directory ('#') is more indented then previous directory, it is considered its subdirectory. If it is equally or less indented, then it is considered subdirectory of one of its ancestors. Indentation relate directly to which ancestor it is - each indentation step means one level up in hierarchy.

For example:

  # Dir                NAME IS: Dir
    # subdirX          NAME IS: Dir/subdirX
  # Dir2               NAME IS: Dir2
    # subdirB          NAME IS: Dir2/subdirB
      # subdirC        NAME IS: Dir2/subdirB/subdirC
    # subdirD          NAME IS: Dir/subdirD
  # Dir3               NAME IS: Dir3

Entry names are prepared by prepending current parent directory to them, with exception of not indented directory entries - their names are left untouched. Other exceptions are names begining with '/' (absolute path) and names begining with './' (path relative to current working directory) - their names are left untouched too.

Operands undergo exactly the same preparation procedure.

Entry names (but not operands) are subject to brace expansion and globbing. Order is: brace expansion, name preparation, globbing.

  # /Dir
  | [  name] >>operand          BECOMES /Dir/name >>/Dir/operand
  | [  /name] >>/operand        BECOMES /name >>/operand
  | [  ./name] >>./operand      BECOMES ./name >>./operand
  | [ f{1,2}*] >>operand        BECOMES /Dir/f1* >>/Dir/operand AND /Dir/f2* >>/Dir/operand
                                AND GLOBBING IS PERFORMED AS LAST

EXAMPLES

Print text representation of /home and /root to stdout:

  tfman /home /root

Write text representation of /home and /root to file txtrep.tfman

  tfman /home /root > txtrep.tfman

Result of scanning file system by 'tfman' may look like this:

  | [  this entry is rem.]*
  | [           no_action]
  . [this line is ignored]
  # /parentdir
  | [  move_it]>>here
  . [  ignored]->.
  | [ /copy_it]))./there

or like this:

  # ./
  |  [            ./]  d  rwxr-sr-x  11  mh  magazyn                --   4096    4096    a.2014-04-27 11:40:16  m.2014-04-27 11:40:16  c.2014-04-27 11:40:16  (none)
  |  [           ../]  d  rwxr-sr-x  9   mh  magazyn                --   4096    4096    a.2014-04-26 23:38:57  m.2014-01-31 07:59:21  c.2014-01-31 07:59:21  (none)
  |  [         .bzr/]  d  rwxr-sr-x  6   mh  magazyn                --   4096    4096    a.2014-04-26 23:38:57  m.2013-12-05 19:09:37  c.2013-12-05 19:09:37  (none)
  |  [     trunk.br/]  d  rwxr-sr-x  20  mh  magazyn                --   4096    4096    a.2014-04-26 20:56:32  m.2014-04-26 20:56:32  c.2014-04-26 20:56:32  (none)
  |  [    CHANGELOG]   -  rw-r--r--      mh  magazyn                --   1767    4096    a.2014-04-26 23:38:57  m.2014-04-26 20:55:44  c.2014-04-26 20:55:44  (none)
  
  # ./dir
  |  [ Design.notes]   -  rw-r--r--      mh  magazyn                --     13kB    16kB  a.2014-04-26 23:38:57  m.2014-04-26 12:19:41  c.2014-04-26 12:19:41  (none)
  |  [     FEATURES]   -  rw-r--r--      mh  magazyn                --   3632    4096    a.2014-04-28 07:35:03  m.2014-04-28 07:34:28  c.2014-04-28 07:34:28  (none)
  |  [FEATURES.html]   -  rw-r--r--      mh  magazyn                --   4164    8192    a.2014-04-27 20:13:44  m.2014-04-27 20:13:43  c.2014-04-27 20:13:43  (none)
  |  [ FEATURES.man]   -  rw-r--r--      mh  magazyn                --   3547    4096    a.2014-04-28 07:35:04  m.2014-04-28 07:35:03  c.2014-04-28 07:35:03  (none)
  |  [      curr.br]   l  rwxrwxrwx      mh  magazyn  _>trunk.br/   --      9       0    a.2014-04-27 19:55:46  m.2013-12-05 19:10:07  c.2013-12-05 19:10:07  (none)
  |  [    stable.br]   l  rwxrwxrwx      mh  magazyn  _>trunk.br/   --      9       0    a.2014-04-27 19:55:46  m.2014-01-31 07:59:45  c.2014-01-31 07:59:45  (none)

Deduce operations from txtrep1.tfman and txtrep2.tfman files and perform them on file system (both examples do the same):

  tfman txtrep1.tfman txtrep2.tfman

Scan directory recursively, open text editor to edit resulting textual representation and parse it after editor is closed:

  tfm /Dir -R

This will move all files (and only files, not directories) containig 'tfm' anywhere in the name to common directory. Files may lay anywhere in the given directory tree:

  $ tfman DIR -R -F=name,type | sed 's,\(^|.*tfm.*[^d]$\),\1 >>/tmp/common,' | tfman

AUTHOR

author: Maciej Helminiak <tfman.project@opmbx.org>

copyright: (C) 2014 Maciej Helminiak, GNU GPL v3