vfu-5.09/0000755000175000017500000000000014444676573010645 5ustar cadecadevfu-5.09/HISTORY0000644000175000017500000010705314444676573011737 0ustar cadecade ---------------------------------------------------------------------- VFU File Manager 1996-2023 (c) Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/projects/vfu http://github.com/cade-vs/vfu-dist http://github.com/cade-vs/vfu ---------------------------------------------------------------------- HISTORY (CHANGE.LOG) + feature add - feature changed x feature removed ! bugfix % note 5.09: 22.Jun.2023 ! 1. fixed problem with some tools (key "T", tools menu) when current directory has no entries. ! 2. fixed shell escaping for pipe char (and % jtbs) 5.08: 06.Mar.2023 ! 1. fixed problem with directories with leading spaces in the name. ! 2. fixed problem with full-name placehoders %F and %G in shell externals. + 3. added new shell place-holder %h and %H, which equals to either %f and %F if no files are selected or %g and %G otherwise. the reason to add as separate placeholder is to allow using both %f and %g in the same shell line but avoid the problem that %g may point to %f, which is not always true. for example you may want to have shell external which adds selected files (%g) to pointed archive (%f) but do not want the archive itself to be added to the same archive (i.e. %g is equal to %f if no selection.) in all other cases where you would want to follow common behaviour to use selected files or if no selection, just the current one, use %h and %H. ! 4. fixed %e + 5. added time hint. if file time is in the last 23 hours, time will be appended with an '*' and time will show day name and seconds also. if time is in the future, "!" will be appended. 5.07: 12.Feb.2023 (yeah...) 5.05: 12.Feb.2023 (11 minutes later) + 1. Added rx support for *.tar.zst archives. 5.04: 12.Feb.2023 (includes 5.03 changes) ! 1. Fixed filename matching for corner cases. ! 2. Fixed rx_auto searching. 5.02: 11.Feb.2023 (includes 5.01 changes) ! 1. proper unicode padding for uid/gid/hostname ! 2. refresh views cleanup ! 3. init data setup cleanup + 4. dir sizes menu: added "missing dirs size calc" (keys Z, M) ! 5. load settings loading optimized ! 6. added getdirname helper menu ctrl-o + 7. dir menu allows negative-xy ! 8. get dir name glob and complete are fixed + 9. added option in dir sizes menu (key Z) to delete cache 5.00: 30.Jan.2023 + 1. Added full wide char (unicode) support. VFU now implements Level 1 support. Filenames and text file contents are now assumed UTF8 and internally VFU is using UTF32 for all string operations. + 2. Added option for list scrolling paging. options are: * 1 line (smooth scrolling with 1 line when on border of pagesize) * 30% of the page size (default) * 50% of the page size ! 3. %f, %F, %g and %G do not require quotes anymore. VFU will escape all special characters in filenames. + 4. Added extended info when calculating directory size. VFU now reports files/dirs count in the traversed path and total size in short/human form and in bytes also. + 5. Added option in the dir size menu (key Z) to calculate dir sizes only for directories without calculated size (missing sizes ones). 4.23: 20.Feb.2022 + 1. Added scroller feedback in main files list. ! 2. Fixed possible overflow in tab expansion in See. - 3. Review of all FIXMEs left :) 4.22: 01.Dec.2021 x 1. Removed useless ascending/descending Unsorted order :) - 2. Removed compatibility arrange mode 'D' (used to be modify time) - 3. Arrange by mode key is changed to 'D'. + 4. Added new quick arrange swap key 'A' between NAME and CHANGE TIME. ! 5. fixed panelizers (ALT+R) bug and remove sort order to keep incoming. + 6. added 'Panelizee from current file' panelizer in file read menu (ALT+R). 4.21: 07.Aug.2020 + 1. Added ALT+- and ALT+BACKSPACE to exit current archive. + 2. Reposition larger menus and update menu labels. + 3. Fixed (finally) terminal resize logic for ncurses and yascreen. + 4. Added tool for cloning or updaing to latest git snapshot: vfu-get-latest-git-snapshot.pl + 5. VFU exit path file is now created exclusively, owner RW only. 4.20: 30.Jul.2020 + 1. Added estimate finish time reporting for copy/move operations. + 2. Added Autotools configure and compile (thanks to Andy Alt!) ! 3. Report available space instead of free space (root reserved). + 4. Added copy speed reporting. ! 5. Fixed directory size cache for larger than 999GB directories. 4.17: 12.June.2018 + 1. Directory size calculations menu (key "Z") has new options: * follow symlinks (WARNING: may cause loops!) * stay on the same device/filesystem + 2. Dynamic files list allocation: i.e. low memory consumption and unlimited files list count (up to the available memory) + 3. Added new rename tools: * prefix selected file names with current date * prefix selected file names with current date+time Tools (key "T") -> Rename tools (key "A") 4.16: 05.May.2015 % 1. Verified that VFU (and vstring/vslib) compiles clean with clang :) + 2. Added option to show file/dir sizes either in IEC or SI units. (cheers Larry :)) 4.15: 17.Jan.2014 ! 1. Fixed bug with directory name completion which freezes VFU when case insensitive completion options is enabled. 4.14: 04.Jan.2014 % 0. Version 4.13 was skipped to match the year :) + 1. Added Alt+S to find next entry matching last incremental search (key "S"). + 2. Added missing incremental search commands in the HELP :) (keys "S" and "Alt+S"). + 3. VFU (and VF before it) was designed to keep directories on top of the file list. There is flag for disabling this inside the code, but it was never visible. Now this long lost option is exported to the options menu. :) + 4. Added "Smart" Home/End keys navigation. When this option is enabled HOME and END keys will toggle between top and bottom of the files list and firs/last directory or file. Example: * first HOME will go to the beginning of the file list * next HOME will go to the first file (not dir) in the list * first END will go to the bottom of the list * next END will go to the last directory in the list Actually "next HOME" will be assumed when current position is already top of the list and also "next END" will be triggered when position is at the bottom entry in the list. 4.12: 21.Dec.2011 ! 1. Fixed problem with strcpy/memmove inside vstring lib. (thanks to Boyan Anastasov) % 2. VString support library was revised to avoid possible buffer overlaps on different platforms and libc implementations. + 3. Added BACKSPACE as alternative dismissal key for all menu boxes along with ESCAPE key. (requested for use on Nokia N900 hardware keyboard) - 4. Removed restriction for the color of directories. Now can be selected in the config file as the rest of the file/directory types. + 5. Added "Case insensitive file/dir names matching" option. Will discard case on directory completion and filenames incremental search 's' key. 4.11: 19.Aug.2010 pre-release + 1. Directory history is larger on larger terminals. (limit raised from 14 to 128 entries) ! 2. Fixed bug causing wrong recognition of files time inside ZIP arcives. % 3. Included PCRE upgraded. 4.10: 16.Dec.2009 ! 1. Source cleanup for recent gcc/g++/glibc. For Debian Sid. (Greetings Billy! :)) pre-4.10: 29.Jul.2009 - 1. removed 'bookmark1' to 'bookmark9' config file options. now only (repeated) 'bookmark' can be used. ! 2. fixed ALT+1 to ALT+9 bookmarks hotkeys. ! 3. fixed several problems with rx_* tools and some archives filenames. 4.09: 14 Apr 2009 ! 1. fixed 64-bit offset formats under 32-bit platform (SEE). thanks to svilen ! 2. Initially this release was timestamped 05 Aug 2006, but it is not correct. Actually released now :) 14 Apr 2009 + 3. Added option for default target directory to be current working dir. % 4. Moved to git :) cheers! % 5. Cygwin package for vfu is now available! Thanks to Jari Aalto! http://www.cygwin.com/ http://cygwin.com/packages/ http://cygwin.com/packages/vfu/ % 6. Applied patches several from Debian. 4.08: 26 Oct 2005 + 1. %g and %G shell line options added: %g -- same as %f but for each selected filename %G -- same as %F but for each selected filename %g and %G produce list of filenames. each filename is surrounded by ' if filename contains ' then " is used. example: 'file1' "file2's" 'file"3"' ... + 2. %! shell line option added: %! -- request shell line to be shown before execution (debug mode) 4.07: 28.Aug.2005 + 1. Tools/Rename_Tools/Replace_SymLink_w._Original tool added. You can select symlink (file only) then call this tool. It will remove the symlink and rename original file to the place of the symlink: ls sym: sym -> org rm sym mv org sym + 2. Tools/Go_To_Symlink_Target tool added. It can change directory to symlink target's one and go to target name. It is useful if you want to inspect symlink target. + 3. Tools/Go_Back_To_Last_Target tool added. This can bring you back to the last entry you were before using Tools/Go_To_Symlink_Target tool. This can be used multiple times and will lead to toggling between symlink and its target. 4.06: 06.Jun.2005 ! 1. Sizes above 4GB are now correctly shown/calculated. (needs 'make CCDEF=-D_FILE_OFFSET_BITS=64') 4.05: 05.Jun.2005 ! 1. update of dir size cache is optimized for multiple dirs. + 2. GlobalSelect/Extended/SelectToBegin/End of list added. ! 3. Sizes above 2GB are now correctly shown/calculated. ! 4. Docs updated to offer way to handle ctrl+z, ctrl+c. 4.04: 07.Jun.2003 + 1. "View differences" menu option added for overwrite file (copy). This can be used to preview files before overwriting. 4.03: 15.Jun.2003 ! 1. Various gcc version warnings and problems were solved. ! 2. Old (pre 4.xx version) directories size cache problem fixed. ( ASSERT("|") issue ) - 3. Credits are removed from the console. 4.02: 06.May.2003 ! 1. Compilation problem fixed. 4.01: 28.Apr.2003 % 0. VFU is now recompiled with the new vslib which features strings, arrays and tries (hashes) with referenced data (shared). ! 1. Various small fixes (logic and less memory alloc/dealloc). ! 2. SeeViewer is now fixed for screens larger than 80 columns. ! 3. Fixed Lynx-style navigation. ! 4. New faster directory size cache! - 5. Directory tree is 2 lines bigger and has dir sizes back. :) + 6. VFU now uses pcre library so it can support Perl-like regular. expressions (i.e. in regexp searches, see viewer, etc.). ! 7. Now SeeViewer highlights found strings in the current visible page. ! 8. Hex mode of SeeViewer was rewritten. Now it supports several 8-byte columns for different width terminals. + 9. Clipboard is now functional (key P). -10. bookmark1 .. bookmark9 options in config file are now replaced with single one: `bookmark' which can be used multiple times (note that for some time old ones will work as well). +11. Directory bookmarks are no attached to a key (`) and new option to temporary run-time bookmarking has been added (`A' inside the bookmarks menu). 4.00: 27.Nov.2002 % 0. Hope this is the new next stage in the VFU development :) ! 1. Fixed rxvt emulation BS handling. (actually found why is that... to fix this issue add `Rxvt*backspacekey: ^H' line to your .Xdefaults) + 2. Ctrl+A/E in file list are Home/End. + 3. Ctrl+A/E/D in all input lines are Home/End/Del. + 4. Added octal mode change (key TAB, edit entry menu). + 5. Fixed global select masking for recursive file lists. - 6. Now `Lowercase extensions for configs' is on by default. ! 7. Settings are saved immediately after options change. ! 8. Fixed directory sizes cache cleaning. ! 9. Fixed all input lines to allow full ascii range (32..255). !10. rx_* tools are completely rewritten! -11. rx_ftp now uses Net::FTP instead of external ftparc utility. 3.04: 24.May.2002 % 0. This version should be named over `04' as result of large number of changes (most internal ones)... Still I'll wait next release to be able receive any bug reports before major version change... (there is about 1.5 years since 3.03:)) + 1. Added FastSizeCache option -- turns off directory resolving for directory size cache which means that the cache will act faster but you won't get sizes for symlinked dirs. + 2. Added `Randomize' function to file list arrange menu. ! 3. Rename tools now work on filename only, not on the full pathname (usual bug in recursive scanning -- Ctrl+R) ! 4. Fixed loops on incremental searches in the files list and directory tree. + 5. Actual files size is now reported before copy/move. + 6. Size cache is now auto-cleaned on dir size recalculation. + 7. Now errors on copy/move/symlink/erase can be ignored (always skipped quietly). ! 8. Temporary files used for view filters are now removed. ! 9. Now all temporary files and directories are set owner read/write/traverse permission only. !10. Fixed problem with browsing/user commands for files inside archive. !11. Fixed problem when quit unsaved file that cannot be saved from the internal editor. !12. Fixed `Name###' arrange mode. +13. Now file list can be sorted separately by modify, change and access time. !14. Fixed directory size cache for symlink-ed dirs. !15. Now VFU accepts trigger `.automount' just like `automount'. !16. Now extension colors loaded from DIR_COLORS are added to those from vfu.conf (not overwritten) !17. Internal: changed all PSZCluster+StrSplitter to VArray+VTrie !18. Fixed configuration file problems (missing archives etc.) !19. SIGWINCH finally works, i.e. VFU redraws on terminal resize (please report any problems regarding this one!) 3.03: 31.Dec.2000 ! 1. Documentation fixed to address directory bookmarks in the vfu.conf correctly. ( bookmark#=path ) ! 2. Added option for VFU to handle Ctrl+Z, Ctrl+C, Ctrl+\ as it is expected (suspend,interrupt,quit). To use it you have to export UNICON_NO_RAW environment var with any value. ( for bash: `export UNICON_NO_RAW=1' ) ! 3. User menu is fixed. + 4. Change directory completion menu is now sorted. + 5. Sequential rename function added (Tools/Rename menu) + 6. Now directory sizes cache is removed from the directory tree, so you can have directory sizes saved even if you don't have directory tree built. 3.02: 01.Aug.2000 ! 1. Several DJGPP (DOS/Win9x) portability fixups. ! 2. Fixed problem with input line for long directory names. ! 3. Fixed directory tree sizes calculation. + 4. Added inode size cache for directories that are new to the currently saved directory tree. (unix version only) ! 5. Fixed major bug in file move procedure. ! 6. Fixed extension masking `for all' in user externals. - 7. Now all path lists in vfu.conf (like TrimTree) are `:' separated (or `;' for dos version) 3.01: 07.Jun.2000 ! 1. Fixed SeeEditor bug when editing new files (that does not exist) ! 2. Fixed install script to se correct permissions. ! 3. Fixed build.netbsd to se correct global configuration files locations. + 4. GlobalSelect/HideDotFiles added. ! 5. Fixed build.netbsd. - 6. Now directory tree automation (rescanning etc.) is disabled by default (see Options/AutoDirTree). ! 7. Fixed zero-sized files bug in SeeViewer. ! 8. Alt+B browse/view current file w/o filters. + 9. Now install script set sample config file into /usr/local/etc (to get personal config file you have to copy /usr/local/etc/vfu.conf to ~/.vfu/vfu.conf or ~/$RC_PREFIX/vfu/vfu.conf if you have set $RC_PREFIX ) !10. Ctrl+L refresh/redraw page problem is now fixed. !11. Several little fixups. 3.00: 20.May.2000 % 0. Hello again! :) The VFU development was suspended nearly an year ago. Now it is resumed and a lot of changes took place! First of all -- VFU was completely rewritten/revised(!), so many (internal?) features were added, some removed, other changed. Below I will list some key features in this version, but probably I'll forget some things or else... % 1. There are no big changes in the user interface and feel. - 2. Now reading/extracting archives is removed from inside VFU to external (perl) utilities. This will help adding new archives and debugging existing ones. - 3. Now archives' structure is followed as the local file system, i.e. you will browse directory by directory but not the whole archive content as before. Perhaps option for recursive (whole content) browsing will be added in the future. + 4. Now you can cancel copy/move procedures at stage you want even during single file copy process! (It was not possible in older versions) ! 5. The rare problem of showing total percentages over 100% during copy/move is finally (and hopefully:)) fixed. + 6. Now files in archive can be masked and files mask can be changed inside archive. ! 7. Some access/terminal emulation problems are fixed. ( i.e. some `difficult' key functions are accessible and from menus etc. ) 1.51: 31.May.1999 + 1. Debian `.deb' packages are now supported as archives! ! 2. All paths containing `dir/..' are reduced. ! 3. Now arcive files are recognized by longer extensions like `.tar.gz'. Result is proper handling of `.tar.gz' and `.tar.bz2' archives. + 4. Added SEE Viewer filters. Now you can view gzipped files and or add filters for viewing man pages etc. + 5. Added ready `panelizers' command to the RescanMenu ( key Ctrl+R ) Can be used as menu alternative to external panelize option. ! 6. SEE screen draws improved and `< >' bug is fixed. ! 7. GetDirName function now allows dirs only. + 8. Added UserMenu ( key `U' ). User external commands can be attached not to key but to this menu. + 9. Bash/Unix-style filename completion added! -10. Bash/Unix-style completion is now default one! +11. Now entries in the filelist can be moved ( see ArrangeMenu/MoveEntry ) !12. Now VFU keeps current file position after change of sort order. !13. Better preserve/copy mode/protection when copy/move directory subtrees. ( from RO media for example ) !14. Fixed erase of own directories without `write' permission/mode. !15. More examples and comments added to the sampe configuration file: vfu.conf. 1.50: 27.Mar.1999 % 0. This is primarily bugfix release, supposed to form final stable release with all planned features. :) ! 1. CR_LF problem in Seed is now solved. ! 2. Total/Free space calculation problem is fixed. ! 3. GlobalSelect/Grep file search problem (finally!) fixed. ! 4. EditEntry/OwnerGroup now accepts `user' format instead of `user.' (i.e. no trailing dot required) ! 5. Fixed problem with Seed and new files (create). ! 6. `/etc/DIR_COLORS' loads properly now, even if vfu.conf doesn't exist. ! 7. Items (files/dirs) colors are changed properly now after mode/protection change. + 8. Now VFU shows and current hostname. + 9. AutoIndent option added to Seed ( key: Ctrl+T ) !10. Fixed problem with free space check before Copy/Move. -11. VFU will not resort files after Ctrl+Z on directory and sort order is `size'. +12. Preliminary man page added. ( vfu.1 ) 1.46: 19.Mar.1999 + 1. Support for /etc/DIR_COLORS (for file-type colorization) See Option/UseEtcDirColors. + 2. Small internal Text editor added! (can be used as emergency editor if no other available) See Option/UseInternalEditor. + 3. Added Lynx-like arrow keys navigation: UpArrow/DownArrow -- scroll list LeftArrow/RightArrow -- enter/exit/cdup See Options/AltArrowsNavigate. + 4. Separate histories to all input lines. Use PageUp/PageDown to recall. + 5. Now SymLink references can be edited in-place. See EditEntry(key TAB)/EditSymLinkReference(key L). + 6. File Find results can be panelized now. ! 7. The usual bugfixes ( not important really ). - 8. Changed location for personal config and other related files, now default place is `$HOME/.vfu'. Changed VFU config file name from `vfurc' to `vfu.conf'. Please read CONFIG file! + 9. Now the internal Viewer and Editor (See/Seed) have separate options files. The `see(d).options' location is `$HOME/$RC_PREFIX/.see(d)/see(d).options'. 1.45: 03.Mar.1999 + 1. `%i' and `%n' macros added. (See above for details) ! 2. Now VFU won't expand masks if you enter external scan/panelize command in the file mask field. ! 3. Now VFU will remember internal file viewer options during the same session. + 4. GlobalSelect/SelectSame/Type(TP) function added. + 5. HexEditor added! See the help in the internal file viewer ( press `I' in the HEX mode of the internal file viewer ) ! 6. Fixed pattern searching function ( it didn't work before with patterns that contains char codes >128 sometimes ) + 7. FTP support! See above for details. ! 8. Few bugfixes as usual... 1.44: 14.Feb.1999 + 1. Auto mounting on change directory added. ( see Options/AutoMount ) If you chdir to a directory which contains only one file named `automount', then VFU will try to mount this directory automatically. After mounting `automount' file won't be visible. You can create file with `echo > automount' command. + 2. Unmount feature added to the `JumpToMountpoint' menu ( key `j' ). + 3. PreserveSelection option added. If this option is enabled VFU will preserve selected files after rescanning files list. ( see Options menu ) + 4. Added Ctrl+Z key to the Directory Tree View for update the current (under cursor) directory size. ! 5. Fixed tilde `~' expansion -- now standalone `~' or `~username' are expanded properly. + 6. RecursiveRescanning can be canceled with ESC now. ! 7. Options(Toggles) separators bug fixed. ! 8. User External Utilities are enabled in InArchive mode as it should be. (considered bug) ! 9. Dotfiles `.filename' colorization fixed. 1.43: 12.Feb.1999 ! 1. Now VFU supports properly screens >80x25 (fixed bug with 80-columns filename view) - 2. ENTER has priority now for entering into archives than executing user external program. + 3. Added option for handling .TGZ equally to .tgz, i.e. case insensitive archive extension detection. + 4. RenameTools added to the tools menu (key `T') + 5. Added one more location for global vfurc file: `/etc/' it is searched first. Can be changed through VFU_RCPATH0 define. ! 6. Now $HOME/.vfurc is primary if exists ( before global vfurc's as it should be ) considered as bug :) - 7. ENTER key behavior changed: If not defined for user external utility, ENTER works as browse/view. It also assumed equal to '+' or '=' for archives ( i.e. cannot be redefined for archives, there's INSERT and Fx alternatives, however if someone needs ENTER for this I probably can change it ). + 8. MenuBorders option added. It can improve menu visibility on mono/colorless terminals. 1.42: 03.Feb.1999 + 1. External scanning ( panelize ). Just enter `command |' in the files masks input line to use it ( i.e. instead of "*.txt" enter "find / -name '*.txt'" ). ! 2. The problem with files/dirs' sizes >2GB is now fixed! 1.41: 04.Dec.1998 + 1. Added Options/FileCopyPreserveOwner/Group option. If enabled VFU will try to preserve files owner and group id's on copy/move if possible. - 2. Now `dir1' is equal to `presetdir1' etc. in the vfurc meaning. + 3. BZip2 archives now supported NOTE!: you have to get tar-1.2+ patched to support -I option which is BZip2 support. required extensions for such files are `bz2' or `tbz' (second one is mine:)) + 4. `Time/Touch' entry added to the `EditEntry' function (TAB key), i.e. change file(s)'s modify and access times. + 5. Now VFU expands ~ with username (~cade/boo) but only if target directory is started with ~. + 6. Added `Show Real Free Space' toggle to the options. + 7. Recursive rescanning added ( Ctrl+R key menu ). Now you can operate over all files from a tree branch! + 8. A lot of cleanup work done. 0.40 ... 1.41: xx.xxx.xx % 0. FAQ: Why is this? You have skipped these version numbers? Well the answer is something like: The versions 0.xx were meant to be kind of alpha/beta versions and are not supposed to be released to the public in the beginning ( didn't happen :)). Well, the versions after 0.40 are considered `stable' and `official' now, so I've added `1' to them... ( there's 1 year since BETA status was removed :)) 0.40: 31.Sep.1998 + 1. Internal Text/Hex file viewer/browser added! See Options/InternalViewer. Hit `H' to get help inside viewer. (The viewer is available and as standalone utility called `See') + 2. Extended filename completion: now you can enter for example: `/level/next/one[mM]o?r*' and hit TAB to complete! + 3. Everywhere you enter directory path you can use: Ctrl+X -- expand to real path Ctrl+A -- delete back one dir level Ctrl+S -- show list with matching directories (kind of visual tab-completion) + 4. Key-names for `user external commands' are shorter: KEY_F1 is now F1 KEY_SH_F1 is now #F1 (Shift+F1) KEY_ALT_F1 is now @F1 (not available under Linux) KEY_CTRL_F1 is now ^F1 (not available under Linux) KEY_IC is now either INSERT, INS or IC KEY_ENTER is now ENTER + 5. `Tools/Classify move' tool added. + 6. `GlobalSelect/Extended/Find;Scan;Hex;Regexp;String' added! + 7. Extended FileFind menu added (Ctrl+N). FileFind with Find/Scan/Hex/Regexp string added. - 8. New Options/Toggles system (Now it is more flexible for adding/displaying new toggles -- really sorry if you liked old one :( ) + 9. Now file types: `** [] <> () etc...' can be used instead of file extensions in the user external command: ux=EXECUTE,INS,.**.,%w +10. Now VFU has option to zap/erase READ-ONLY files. (see options) +11. Many command line switches added! (run `VFU -h' for help) +12. Find/Scan/Hex/Regexp string search added to the internal file viewer/browser! +13. User External Commands are now available and in InArchive mode! +14. FileMasks expanding added to `files mask' filter and file find tool. ( for more details see `FAQ/What are the mask expansion rules' ) +15. GlobalSelect/Different function added. 0.30: 25.Sep.1998 + 1. Archives support! Now VFU supports the following archives: zip, tar, tgz, uc2, arj, lha, rar (for more details see `Usage notes' section) + 2. Now VFU follows sym-links. 0.22: 30.Aug.1998 % 1. Support VFUSHELL environment variable -- it is used to override SHELL variable (to use login shell for example) + 2. Added `GlobalSelect/All+Dirs' function. + 3. Preserve timestamps on copy. + 4. Report target file status on overwrite (copy/move/etc.). + 5. `Insert/Overstrike' modes in all input lines. + 6. Added `History' to most of the input lines. (try PageUp/PageDown keys in input lines) + 7. Now incremental search can work and with patterns. (for example if you enter `>*.cpp' VFU will track on all *.cpp files -- please note the leading `>'!) - 8. Now options(keywords) in the vfurc file are NOT case sensitive. (for example you can type `BrOwSeR=less %f') + 9. `TrimTree' option added to vfurc. example: TrimTree=/mnt/cdrom/ /proc/ /tmp/ You can use these separators: UNIX: [,:] space and tab. DOS: [,;] space and tab. DOS users note: TrimTree doesn't support driveletters! I mean if you want to trim `c:/tmp/' you should add `TrimTree=/tmp/' but this will cut also `d:/tmp/' -- This is NOT bug but WAD. +10. Now you can use file type identifiers for filename colorization (also `dotfiles' for `.name' dotfiles). For example: cGREEN=.cpp.h.**.(). cRED=.[].txt.dotfiles. cBLUE=.<>. all .cpp, .h, executable files and pipes are GREEN all .txt, dotfiles and directories are RED all links to directories are BLUE 0.21: 02.Aug.1998 % 0. Oops... I forgot -- If someone want to know why I'm trying to support DOS platform: the reason is that I use DJGPP+RHIDE for development environment -- it is more comfortable than gcc+joe :) Well I know that there is RHIDE for Linux but I haven't tried it yet... + 1. New file attributes/mode set/get engine. Now VFU supports different attrib's under different OS-es transparently: Linux/UNIX: `drwxrwxrwx' DOS: `DV----RHSA' + 2. `VFFilenames' style added for DOS *only*! This means all dirs' names are upper case and all file names are lower case (but this is only for the screen!) + 3. Added IncrementalSearch to the file list and the directory tree ( key Ctrl+S, and TAB to advance ). + 4. Copy/Move/Erase now work on directories (w. entire substructures)! + 5. `CDTree' option added. This is similar to bash's CDPATH env.variable but the needed path is searched in the directory tree. For example: if you are in the HOME directory and try to ChDir to `rc.d' -- VFU will check for HOME/rc.d, which not exist, then will try to find such dir in the DirTree (if the tree is not built, VFU will try to just to load it from disk), most probably `/etc/rc.d/' will be found and VFU will ChDir to it. + 6. New shell-macros: %e, %E, %s, %R, %c, %C + 7. Added `JumptToMountpoint' function ( key `J' ) + 6. Added `MakeDirectory' to `Tools' menu ( key `T' ) 0.20: 4.Jul.1998 % 0. Changes are too much to list (will try to describe most important ones). The main change is that -- VFU goes portable -- Currently it supports Linux, Solaris, DOS(!). (I mean that I have compiled it for these OS-es myself) Win32 version is on the way... Note that DOS version supports long filenames (LFN's)! To use this support you have to `set LFN=Y' in the environment. + 1. `Options/Toggles' screen added (key `O' or Alt+O) Options menu (key `o') still exists but is empty for now. + 2. Now you can switch off parts of file information (i.e. mode/attr, owner, group, time, size... see Options/Toggles). + 3. DirectoryTree added (key Ctrl+D) incl. hot key search `a'-`z'. + 4. `DynamicScroll' option added -- it switch all lists scrolling between `page-by-page' or `line-by-line'. 0.15: 15.Mar.1998 + 1. FileFind function added. (key `n') ! 2. Screen redraw after shell fixed. 0.14: 17.Oct.1997 + 1. SymLinks -- `l' key. ! 2. RmDir/Links bug is now fixed. ! 3. Other minor bugfixes. % 4. Since VF/U passed large enough test period without any major problems or any kind of data loss etc., `BETA' status has been removed. 0.13BETA: 17.Oct.1997 There's not such version really. :) trust me... :) 0.12BETA: 06.Oct.1997 + 1. GlobalSelect/+/-/= -- select/deselect by mask. + 2. UserExternals commands added (see vfurc description). + 3. Now vfu will search for environment variables 'EDITOR' and 'PAGER' (or 'BROWSER') to set editor and pager/browser. (if not given in vfurc file) + 4. TAB/chown now accepts 'username.groupname' instead of 'uid.gid'. + 5. GlobalSelect/Same..name/ext/size/owner/group added. + 6. Inline editing caps added. + 7. File masks added. + 8. VFU or VF screen/view styles added. 0.11BETA: 20.Aug.1997 ! 1. Some bugfixes. 0.10BETA: 07.Aug.1997 ! 1. bug with following directory links when calculating directory(ies) size is now removed. ! 2. now vfu shows file type correctly. (links,dir.links) + 3. link realpaths are now shown: "linkname -> realpath". + 4. "Tools/r-realpath" added, shows real path for a link. + 5. "Options" added -- key "o"/"O". ! 6. problem with unknown uid/gid is now fixed. + 7. "ChDirHistory" added -- key "D". 0.09BETA: 01.Apr.1997 + 1. now vfu prompts you before overwriting existing file. (while copy or move files) 0.08BETA: 30.Mar.1997 ! 1. get dir name logic fixed. ( don't worry if you don't know what that means, however it's better now :)) 0.07BETA: 28.Mar.1997 % 0. elf version! :) - 1. now tab-completion procedure adds '/' at the end. + 2. octal mode chmod (TAB+A+\). + 3. now vfu remembers last copy/move paths. + 4. chdir shows last path you were in. 0.06BETA: history lost... ---------------------------------------------------------------------- vfu-5.09/vfu.pod0000644000175000017500000000643614204533152012135 0ustar cadecade=head1 NAME vfu - VFU is console (text-mode) file manager for UNIX/Linux =head1 SYNOPSIS vfu [options] =head1 DESCRIPTION B has following features: - Fast one-key commands - Extended completion and wildcard expansion - Directory tree with sizes (incl. sizes cache) - File-type colorization (extension and type) - Archives support (TAR, TGZ, BZ2, and many more) - Simple FTP support through archive-like interface - Internal text/hex file viewer and editor (optional) - Extensive user-defined external support/utils - Regular expressions selection and search - Multiple file masks - and much more... =head1 OPTIONS -h prints help for command line options -i runs vfu in interactive mode (can be used with \-d) -d path changes working directory to `path' -r rebuild directory tree -t view directory tree only These options are for using vfu in partial non-interactive mode. example: vfu -d /usr/local -i =head1 CONFIGURATION B configuration is divided in two parts (files): B This file contains configuration for external editor and pager, favorite directories, file-type colorization. This is plain text file which format is described below. vfu only reads vfu.conf, it never writes in it! B This file contains all Options/Toggles which can be changed from inside vfu -- Options menu. vfu.options is binary file and is overwritten on vfu exit or on change option event! Deleting this file will reset vfu toggles to default values. vfu.options is not portable between vfu versions! =head2 Location of vfu.conf B can be placed as any of: $HOME/.vfu/vfu.conf $HOME/$RC_PREFIX/vfu/vfu.conf (if $RC_PREFIX is exported) /etc/vfu.conf /usr/local/etc/vfu.conf /usr/local/vfu.conf =head2 Other files location All other files including vfu.options are placed in: $HOME/.vfu/ $HOME/$RC_PREFIX/vfu/ What is B> and why to use it? Some more information for this can be found in the B file which is supplied with B package. =head1 VFU.CONF This is a sample copy of vfu.conf with appropriate comments: =begin text #include "vfu.conf" =end text =head1 DISTRIBUTION It is supposed that only source packages will be distributed. However sometimes binary packages will be released, but not often. VFU packages are named in this way: vfu-X.xx.src.tgz source package for version X.xx vfu-X.xx.bin.platform.tgz binary package for version X.xx for `platform' platform examples: vfu-4.22.src.tgz vfu-4.22.bin.linux.glibc.tgz vfu-4.22.bin.linux.libc5.tgz vfu-4.22.bin.dos.tgz All packages are TAR+GZIP .tgz, bz2 and zip are available on request. B: Always check HISTORY document -- it often contains some useful notes! =head1 FILES $HOME/$RC_PREFIX/vfu/vfu.conf configuration, explained above $HOME/$RC_PREFIX/vfu/vfu.options options, explained above $HOME/$RC_PREFIX/vfu/vfu.history contains history lines $HOME/$RC_PREFIX/vfu/vfu.tree contains directory tree If you don't set $RC_PREFIX configuration files are: $HOME/.vfu/vfu.conf $HOME/.vfu/vfu.options $HOME/.vfu/vfu.history $HOME/.vfu/vfu.tree =head1 TODO see the TODO file =head1 BUGS unknown =head1 AUTHOR 1996-2022 (c) Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/projects/vfu vfu-5.09/vfu.10000644000175000017500000001754614204533152011517 0ustar cadecade.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "VFU 1" .TH VFU 1 "2022-02-20" "perl v5.32.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" vfu \- VFU is console (text\-mode) file manager for UNIX/Linux .SH "SYNOPSIS" .IX Header "SYNOPSIS" vfu [options] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBvfu\fR has following features: .PP .Vb 11 \& \- Fast one\-key commands \& \- Extended completion and wildcard expansion \& \- Directory tree with sizes (incl. sizes cache) \& \- File\-type colorization (extension and type) \& \- Archives support (TAR, TGZ, BZ2, and many more) \& \- Simple FTP support through archive\-like interface \& \- Internal text/hex file viewer and editor (optional) \& \- Extensive user\-defined external support/utils \& \- Regular expressions selection and search \& \- Multiple file masks \& \- and much more... .Ve .SH "OPTIONS" .IX Header "OPTIONS" .Vb 2 \& \-h \&prints help for command line options \& \& \-i \&runs vfu in interactive mode (can be used with \e\-d) \& \& \-d path \&changes working directory to \`path\*(Aq \& \& \-r \&rebuild directory tree \& \& \-t \&view directory tree only .Ve .PP These options are for using vfu in partial non-interactive mode. .PP .Vb 1 \& example: vfu \-d /usr/local \-i .Ve .SH "CONFIGURATION" .IX Header "CONFIGURATION" \&\fBvfu\fR configuration is divided in two parts (files): .PP \&\fBvfu.conf\fR .PP This file contains configuration for external editor and pager, favorite directories, file-type colorization. This is plain text file which format is described below. vfu only reads vfu.conf, it never writes in it! .PP \&\fBvfu.options\fR .PP This file contains all Options/Toggles which can be changed from inside vfu \*(-- Options menu. vfu.options is binary file and is overwritten on vfu exit or on change option event! Deleting this file will reset vfu toggles to default values. vfu.options is not portable between vfu versions! .SS "Location of vfu.conf" .IX Subsection "Location of vfu.conf" \&\fBvfu.conf\fR can be placed as any of: .PP .Vb 1 \& $HOME/.vfu/vfu.conf \& \& $HOME/$RC_PREFIX/vfu/vfu.conf (if $RC_PREFIX is exported) \& \& /etc/vfu.conf \& \& /usr/local/etc/vfu.conf \& \& /usr/local/vfu.conf .Ve .SS "Other files location" .IX Subsection "Other files location" All other files including vfu.options are placed in: .PP .Vb 1 \& $HOME/.vfu/ \& \& $HOME/$RC_PREFIX/vfu/ .Ve .PP What is \fB\f(CB$RC_PREFIX\fB\fR and why to use it? Some more information for this can be found in the \fB\s-1CONFIG\s0\fR file which is supplied with \&\fBvfu\fR package. .SH "VFU.CONF" .IX Header "VFU.CONF" This is a sample copy of vfu.conf with appropriate comments: .SH "DISTRIBUTION" .IX Header "DISTRIBUTION" It is supposed that only source packages will be distributed. However sometimes binary packages will be released, but not often. \&\s-1VFU\s0 packages are named in this way: .PP .Vb 1 \& vfu\-X.xx.src.tgz .Ve .PP source package for version X.xx .PP .Vb 1 \& vfu\-X.xx.bin.platform.tgz .Ve .PP binary package for version X.xx for `platform' platform .PP examples: .PP .Vb 1 \& vfu\-4.22.src.tgz \& \& vfu\-4.22.bin.linux.glibc.tgz \& \& vfu\-4.22.bin.linux.libc5.tgz \& \& vfu\-4.22.bin.dos.tgz .Ve .PP All packages are \s-1TAR+GZIP\s0 .tgz, bz2 and zip are available on request. .PP \&\fB\s-1NOTE\s0\fR: Always check \s-1HISTORY\s0 document \*(-- it often contains some useful notes! .SH "FILES" .IX Header "FILES" .Vb 1 \& $HOME/$RC_PREFIX/vfu/vfu.conf .Ve .PP configuration, explained above .PP .Vb 1 \& $HOME/$RC_PREFIX/vfu/vfu.options .Ve .PP options, explained above .PP .Vb 1 \& $HOME/$RC_PREFIX/vfu/vfu.history .Ve .PP contains history lines .PP .Vb 1 \& $HOME/$RC_PREFIX/vfu/vfu.tree .Ve .PP contains directory tree .PP If you don't set \f(CW$RC_PREFIX\fR configuration files are: .PP .Vb 4 \& $HOME/.vfu/vfu.conf \& $HOME/.vfu/vfu.options \& $HOME/.vfu/vfu.history \& $HOME/.vfu/vfu.tree .Ve .SH "TODO" .IX Header "TODO" see the \s-1TODO\s0 file .SH "BUGS" .IX Header "BUGS" unknown .SH "AUTHOR" .IX Header "AUTHOR" .Vb 3 \& 1996\-2022 (c) Vladi Belperchinov\-Shabanski "Cade" \& \& http://cade.noxrun.com/projects/vfu .Ve vfu-5.09/vfu.conf0000644000175000017500000002506214444676573012321 0ustar cadecade############################################################################################### # # vfu.conf # VFU File Manager config file # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # # http://cade.noxrun.com/projects/vfu # http://github.com/cade-vs/vfu-dist # http://github.com/cade-vs/vfu # ############################################################################################### # # All lines with first character # or ; are considered comments! # Comments may start on a separated new line only! (i.e. comment chars must be first in line) # This file is read-only! vfu never writes to it. # # Possible locations for this file are: # # $HOME/.vfu/vfu.conf # $HOME/$RC_PREFIX/vfu/vfu.conf ( please read CONFIG file ) # /etc/vfu.conf # /usr/local/etc/vfu.conf # /usr/local/vfu.conf # # comment out following lines to use vfu's internal editor and viewer # or you can toggle em run-time if you wish so Browser=less %f Editor=joe %f Diff=diff -u # alternative ones: #Editor=vi %f #Editor=~/apps/zed/zed %f # list of known archive types, these are just for recognizing # handling is done via rx_* scripts! but not by VFU itself Archive=*.zip Archive=*.pk3 Archive=*.maff Archive=*.jar Archive=*.tar.gz Archive=*.tgz Archive=*.tar.xz Archive=*.txz Archive=*.tar.Z Archive=*.tar.bz2 Archive=*.tar Archive=*.rar Archive=*.deb Archive=*.ftp Archive=*.rpm # # if you'd like to restrict VFU when walking the directory tree -- list paths # here. Note that you usually should add at least '/tmp/' and '/proc/' # format is ':' separated list of directory prefixes # TrimTree=/dev/:/proc/:/tmp/:/dos/:/a/ ############################################################################################### # user external commands (handlers), format is: # description,keyname,ext.mask,command # # to execute a command VFU will try to match both # key pressed and current file's extension! # # 1. 'description' is just free text, keep it small, first letter can be used as menu hotkey # 2. 'keyname' is key you want to bind # 3. 'ext.mask' is dot-separated list of required extensions and/or file type strings # or '*' to discard file type and run command for all files # (don't be confused with '.**.' which stands for 'executable' files) # 4. 'command' is the shell line you want to execute (as on the command prompt for example) # # Available keys (keynames) are: # ENTER, INSERT, F1..F10, @F1..@F10, #F1..#F10, ^F1..^F10 # (#=shift, @=alt, ^=ctrl, note: ^KEY and @KEY are not available under Linux) # # NOTE: You can use keyname 'MENU' to attach this command to the 'UserMenu' (key U in vfu) # NOTE: 'file type strings' are the strings that VFU shows in the 'TP' column in the file, # list. Here is a list of the file type strings: # ** -- executable file # [] -- directory # -> -- symbolic link # <> -- symbolic link to directory # == -- block device # ++ -- character device # () -- fifo (pipe) # ## -- socket # You can mix file extensions with file type strings in the same mask. # There is a special mask called 'dotfiles' which will match dotfiles (wiles named # with leading dot -- '.dotname' ) # NOTE: You cannot mask longer extensions like '.tar.gz' for example. # # 'Command' string (shell line) can contain following macros: # # %f -- replaced w. current filename (w/o path) # %F -- replaced w. full pathname of the current file # %g -- same as %f but for each selected filename # %G -- same as %F but for each selected filename # %g and %G produce list of filenames separated with whitespace. # %h -- assumed to be %f if no selected files or %g if selection exists # %H -- assumed to be %F if no selected files or %G if selection exists # %e -- current file name without path and without extension # %E -- current file extension # # all %f, %F, %g, %G, %h, %H will be shell-escaped. # # %s -- current file size # %c -- current path ( with slash at the end ) # %C -- startup path ( with slash at the end ) # %R -- re-read directory content after shell # %? -- prompt for user input and replace it in # %i -- simulates DownArrow after execution # %n -- don't clear and redraw screen on user external command # %w -- wait a key after shell. replaced w. '' (i.e. empty string) # %x -- replaced w. 'x'. # %_ -- use short file names (SFN) for %f and %F (DOS only) # %\ -- use backslashes for %f and %F (DOS only) # %! -- request shell line to be shown before execution (debug mode) # # view JPEGs and GIFs -- you can move this to the SEE filters below #ux=SEE JPEG,INSERT,.jpg.jpeg.gif.,seejpeg -w -F G640x480x256 -c %f ux=SEE JPEG,ENTER,.JPG.jpg.JPEG.jpeg.gif.xpm.png.,qvv %f 2> /dev/null & ux=SEE GNUMERIC,ENTER,.xls.gnumeric.,gnumeric %f 2> /dev/null & # view HTML documents -- now moved to SEE filters below ux=SEE HTML,ENTER,.htm.html.shtml.,lynx %F ux=SEE HTML,INSERT,.htm.html.shtml.,lynx %F #ux=PLAY WAV,ENTER ,.au.wav.WAV.,killall mpg123 play 2> /dev/null; play %f %i %n 1> /dev/null 2> /dev/null & #ux=PLAY WAV,INSERT,.au.wav.WAV.,play %f %i%n 1> /dev/null 2> /dev/null & #ux=PLAYMP3,ENTER,.ogg.mp3.wav.,killall mpg123 play 2> /dev/null; mpg123 -b 1024 %f %i 1> /dev/null 2> /dev/null & # run xmms with all selected files ux=PLAY MP3,ENTER,.ogg.mp3.wav.,xmms %f %i 1> /dev/null 2> /dev/null & # if you want to run only pointed one, then replace %g with %f like in the next line: # ux=PLAY MP3,ENTER,.ogg.mp3.wav.,xmms %f %i 1> /dev/null 2> /dev/null & # run xmms with all mp3/wav files in the current directory ux=PLAY MP3,INSERT,*,xmms *.mp3 *.wav 1> /dev/null 2> /dev/null & # view PDF and PS document ux=VIEW PDF,ENTER,.pdf.PDF.,acroread %f 1> /dev/null 2> /dev/null & ux=VIEW PS,ENTER,.ps.,gv %f & ux=VIEW TTF,ENTER,.ttf.,kfontview %f & # ux=VIEW TAR,INS,.gz.,gunzip -c %f | tar tvf - | less # view man pages -- note you can add and see filter for this ux=VIEW MAN,ENTER,.1.2.3.4.5.6.7.8.,man %F # play mpeg's #ux=PLAY MPEG,ENTER,.mpg.MPG.mpeg.,plaympeg %f 1> /dev/null & #ux=PLAY MPEG,INS,.mpg.MPG.mpeg.,plaympeg -2 %f 1> /dev/null & ux=PLAY MPEG,ENTER,.mpg.MPG.mpeg.asf.avi.mov.wmv.,mplayer %f 1> /dev/null 2> /dev/null & ux=PLAY MPEG,INS,.mpg.mpeg.asf.avi.mov.wmv.,mplayer %f 1> /dev/null 2> /dev/null ux=PLAY REAL,ENTER,.rm.,realplay %f 1> /dev/null 2> /dev/null & # other applications ux=EDIT GNUMERIC,ENTER,.gnumeric.,gnumeric %f 1> /dev/null 2> /dev/null & # # following user commands are bound to the UserMenu -- key 'u' # note that instead of keyname there's 'menu' string! # first letter is hotkey! # ux=lLocate file,menu,*,locate %? %w ux=---,menu,*, ux=ompg123: Stop,menu,*,killall -TERM mpg123 1> /dev/null 2> /dev/null & ux=smpg123: Suspend,menu,*,killall -STOP mpg123 1> /dev/null 2> /dev/null & ux=cmpg123: Continue,menu,*,killall -CONT mpg123 1> /dev/null 2> /dev/null & ux=vmpg123: View running/queue,menu,*,ps xeo "%%p %%a" | grep mpg123 | grep -v grep | less ux=---,menu,*, ux=GGQView Here,menu,*,gqview . 1> /dev/null 2> /dev/null & ux=---,menu,*, ux=MSend e-Mail,menu,*,sylpheed --compose --attach %f # # aditional examples: # # edit with kwrite ( > /dev/null -- all os text messages ) # NOTE: '*' means for any file (regardless type) ux=KWRITE EDIT,F6,*,kwrite %f 2> /dev/null 1> /dev/null %n & ux=GIMP EDIT,F7,.gif.jpg.png.xcf.,gimp-remote %f 2> /dev/null 1> /dev/null %n & # ux=SLICK EDIT,F7,*,vs %f 2> /dev/null 1> /dev/null %n & ux=KATE EDIT,F7,*,e %f 2> /dev/null 1> /dev/null %n & # execute all files that have type '**' with ENTER #ux=EXEC,ENTER,.**.,%f # same as the one before but executes command in background #ux=EXEC,INSERT,.**.,%f & ############################################################################################### # the 'see' file browser/viewer filters # the format is: # file-mask,command # 1. 'file-mask' tells which files should be filtered # 2. 'command' is executed and it's output is piped to temporary file which is # viewed by the viewer (You have to specify %f in the command) # see=*.html.gz,(gzip -dc %f > /tmp/vfu.temp.000.html; lynx -dump /tmp/vfu.temp.000.html; rm /tmp/vfu.temp.000.html ) see=*.[1234567890].gz,man %f see=*.gz,gzip -dc %f see=*.bz2,bzip2 -dc %f see=*.Z,gzip -dc %f see=*.[1234567890],man %f see=*.htm,lynx -dump %f see=*.html,lynx -dump %f see=*.shtml,lynx -dump %f see=*.dbf,dbfdump -2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 %f see=*.jpg,exiftool %f see=*.JPG,exiftool %f # this is a hack to view Qt man pages see=*.[1234567890][a-zA-Z]t?,man %f see=*.sf_n,dump_storable.pl %f see=*.mp4,mplayer -vo null -ao null -identify -frames 0 %f see=*.mkv,mplayer -vo null -ao null -identify -frames 0 %f ############################################################################################### # external panelizers -- added to RescanMenu ( key ALT+R ) # first letter is hotkey! # format is: # description,command # 1. 'description' is free text describing panelizer command # 2. 'command' is processed just like any other external command, so # you can use the shell-macros described above. Note that # first letter of description is used for menu hotkey! # panelize=cPanelize from current file,cat %f panelize=xExternal panelize command,%? panelize=yFind all symlink files...,find . -type l panelize=lLocate file,locate %? ############################################################################################### # directory bookmarks -- press ALT+2 to change current dir to '/tmp' etc... # there are 10 available keys: 1, 2, 3 ... 9, 0 # bookmark=/etc/ bookmark=/home/cade/ bookmark=/tmp/ bookmark=/usr/src/ bookmark=/usr/local/lib/X11/icewm/ # :) ############################################################################################### # file extensions colors, format is .ext.ext.ext....ext. # NOTE: this is extensions list, use dots to separate and at the end # cMAGENTA=.**.txt.rc. cGREEN=.jpeg.jpg.lbm.xpm.tif.gif.png. cCYAN=.[].<>.h.c.cpp.cc.cxx.pas.pl. cRED=.dotfiles. cYELLOW=.uc2.zip.arj.tgz.tar.rar.lzh.j.ha.lim.gz.Z.bz2.deb. cBLUE=.==.++.().##. ############################################################################################### # high colors # NOTE: this mode currently is not available! # #chMAGENTA= #chGREEN= #chCYAN= #chYELLOW=.1.2.3.4.5.6.7.8. #chBLUE= ############################################################################################### # EOF vfu.conf ############################################################################################### vfu-5.09/vstring/0000755000175000017500000000000014444676576012344 5ustar cadecadevfu-5.09/vstring/vstring_internal.h0000644000175000017500000010036014444676576016105 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ /*************************************************************************** ** ** GLOBALS ** ****************************************************************************/ #define VARRAY_DEFAULT_BLOCK_SIZE 1024 #define VSTRING_DEFAULT_BLOCK_SIZE 256 /* forward */ class VS_STRING_CLASS; class VS_STRING_CLASS_R; class VS_ARRAY_CLASS; class VS_TRIE_CLASS; /* using casual names... */ #define VHash VS_TRIE_CLASS /**************************************************************************** ** ** aux functions ** ****************************************************************************/ ssize_t str_len( const VS_CHAR *s ); /**************************************************************************** ** ** VSTRING BOX ** ****************************************************************************/ class VS_STRING_BOX: public VRef { public: int sl; // string buffer length int size; // internal buffer size VS_CHAR* s; // internal buffer int block_size; // current block size int compact; VS_STRING_BOX() { s = NULL; sl = size = compact = 0; block_size = VSTRING_DEFAULT_BLOCK_SIZE; resize_buf( 0 ); }; virtual ~VS_STRING_BOX(); VS_STRING_BOX* clone(); void resize_buf( int new_size ); void undef() { resize_buf( 0 ); sl = 0; }; void set_block_size( int new_block_size ); }; /**************************************************************************** ** ** VSTRING ** ****************************************************************************/ VS_STRING_CLASS& str_copy ( VS_STRING_CLASS& target, const VS_CHAR* source, int pos = 0, int len = -1 ); // returns `len' VS_CHARs from `pos' VS_STRING_CLASS& str_pad ( VS_STRING_CLASS& target, int len, VS_CHAR ch = VS_CHAR_L(' ') ); VS_STRING_CLASS& str_comma( VS_STRING_CLASS& target, VS_CHAR delim = VS_CHAR_L('\'') ); class VS_STRING_CLASS { VS_STRING_BOX* box; VS_CHAR retch; // used to return VS_CHAR& for off-range VS_CHAR index void detach(); public: VS_STRING_CLASS( const VS_STRING_CLASS& str ) { box = str.box; box->ref(); }; VS_STRING_CLASS() { box = new VS_STRING_BOX(); }; VS_STRING_CLASS( const void* nu ) { box = new VS_STRING_BOX(); nu = nu; }; VS_STRING_CLASS( const VS_CHAR* ps ) { box = new VS_STRING_BOX(); set( ps); }; VS_STRING_CLASS( const int n ) { box = new VS_STRING_BOX(); i(n); }; VS_STRING_CLASS( const long n ) { box = new VS_STRING_BOX(); l(n); }; VS_STRING_CLASS( const long long n ) { box = new VS_STRING_BOX(); ll(n); }; VS_STRING_CLASS( const double n ) { box = new VS_STRING_BOX(); f(n); }; VS_STRING_CLASS( VS_STRING_CLASS_R rs ); ~VS_STRING_CLASS() { box->unref(); }; void compact( int a_compact ) // set this != 0 for compact (memory preserving) behaviour { box->compact = a_compact; }; //FIXME: detach() first? void set_block_size( int new_block_size ) { if ( box ) box->set_block_size( new_block_size ); }; void resize( int new_size ) { detach(); box->resize_buf( new_size ); }; void undef() { box->unref(); box = new VS_STRING_BOX(); }; const VS_STRING_CLASS& operator = ( const VS_STRING_CLASS& str ) { box->unref(); box = str.box; box->ref(); return *this; }; const VS_STRING_CLASS& operator = ( const void* nu ) { nu = nu; undef(); return *this; }; const VS_STRING_CLASS& operator = ( const VS_CHAR* ps ) { set(ps); return *this; }; const VS_STRING_CLASS& operator = ( const int n ) { i(n); return *this; }; const VS_STRING_CLASS& operator = ( const long n ) { l(n); return *this; }; const VS_STRING_CLASS& operator = ( const long long n ) { ll(n); return *this; }; const VS_STRING_CLASS& operator = ( const double n ) { f(n); return *this; }; const VS_STRING_CLASS& operator += ( const VS_STRING_CLASS& str ) { cat( str.box->s ); return *this; }; const VS_STRING_CLASS& operator += ( const VS_CHAR* ps ) { cat( ps ); return *this; }; const VS_STRING_CLASS& operator += ( const int n ) { VS_STRING_CLASS tmp = n; cat(tmp); return *this; }; const VS_STRING_CLASS& operator += ( const long n ) { VS_STRING_CLASS tmp = n; cat(tmp); return *this; }; const VS_STRING_CLASS& operator += ( const long long n ) { VS_STRING_CLASS tmp = n; cat(tmp); return *this; }; const VS_STRING_CLASS& operator += ( const double n ) { VS_STRING_CLASS tmp = n; cat(tmp); return *this; }; const VS_STRING_CLASS& operator *= ( const int n ) { return str_mul( *this, n ); }; friend VS_STRING_CLASS operator + ( const VS_STRING_CLASS& str1, const VS_STRING_CLASS& str2 ) { VS_STRING_CLASS res = str1; res += str2; return res; }; friend VS_STRING_CLASS operator + ( const VS_STRING_CLASS& str1, const VS_CHAR* ps ) { VS_STRING_CLASS res = str1; res += ps; return res; }; friend VS_STRING_CLASS operator + ( const VS_CHAR* ps, const VS_STRING_CLASS& str2 ) { VS_STRING_CLASS res = ps; res += str2; return res; }; friend VS_STRING_CLASS operator + ( const VS_STRING_CLASS& str1, const int n ) { VS_STRING_CLASS res = str1; res += n; return res; }; friend VS_STRING_CLASS operator + ( const int n, const VS_STRING_CLASS& str2 ) { VS_STRING_CLASS res = n; res += str2; return res; }; friend VS_STRING_CLASS operator + ( const VS_STRING_CLASS& str1, const long n ) { VS_STRING_CLASS res = str1; res += n; return res; }; friend VS_STRING_CLASS operator + ( const long n, const VS_STRING_CLASS& str2 ) { VS_STRING_CLASS res = n; res += str2; return res; }; friend VS_STRING_CLASS operator + ( const VS_STRING_CLASS& str1, const double n ) { VS_STRING_CLASS res = str1; res += n; return res; }; friend VS_STRING_CLASS operator + ( const double n, const VS_STRING_CLASS& str2 ) { VS_STRING_CLASS res = n; res += str2; return res; }; friend int operator == ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) == 0; }; friend int operator == ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) == 0; }; friend int operator == ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) == 0; }; friend int operator != ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) != 0; }; friend int operator != ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) != 0; }; friend int operator != ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) != 0; }; friend int operator > ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) > 0; }; friend int operator > ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) > 0; }; friend int operator > ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) > 0; }; friend int operator >= ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) >= 0; }; friend int operator >= ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) >= 0; }; friend int operator >= ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) >= 0; }; friend int operator < ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) < 0; }; friend int operator < ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) < 0; }; friend int operator < ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) < 0; }; friend int operator <= ( const VS_STRING_CLASS& s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) <= 0; }; friend int operator <= ( const VS_CHAR* s1, const VS_STRING_CLASS& s2 ) { return VS_FN_STRCMP( s1, s2 ) <= 0; }; friend int operator <= ( const VS_STRING_CLASS& s1, const VS_CHAR* s2 ) { return VS_FN_STRCMP( s1, s2 ) <= 0; }; operator const VS_CHAR* ( ) const { return (const VS_CHAR*)box->s; } const VS_CHAR* data() const { return (const VS_CHAR*)box->s; } VS_CHAR& operator [] ( int n ) { if ( n < 0 ) n = box->sl + n; if ( n < 0 || n >= box->sl ) { retch = 0; return retch; } detach(); return box->s[n]; } void fixlen() { box->sl = str_len(box->s); ASSERT( box->sl < box->size ); } void fix() { box->sl = str_len(box->s); box->resize_buf(box->sl); ASSERT( box->sl < box->size ); } void fixbuf() { box->resize_buf(box->sl); ASSERT( box->sl < box->size ); } void i( const int n ); void l( const long n ); void ll( const long long n ); void f( const double d ); void fi( const double d ); // sets double as int (w/o frac) int i() { return VS_FN_STRTOL( box->s ); } long l() { return VS_FN_STRTOL( box->s ); } long long ll() { return VS_FN_STRTOLL( box->s ); } double f() { return VS_FN_STRTOD( box->s ); } double fi() { return VS_FN_STRTOD( box->s ); } void set( const VS_CHAR* ps ); void cat( const VS_CHAR* ps ); void setn( const VS_CHAR* ps, int len ); void catn( const VS_CHAR* ps, int len ); /* for debugging only */ int check() { int len = str_len(box->s); return ((len == box->sl)&&(lensize)); } /**************************************************************************** ** VS_STRING_CLASS Friend Functions (for class VS_STRING_CLASS) ****************************************************************************/ inline friend ssize_t str_len( VS_STRING_CLASS& target ) { return target.box->sl; }; inline friend VS_STRING_CLASS& str_set( VS_STRING_CLASS& target, const VS_CHAR* ps ) { target.set( ps ); return target; }; friend VS_STRING_CLASS& str_mul ( VS_STRING_CLASS& target, int n ); // multiplies the VS_STRING_CLASS n times, i.e. "1"*5 = "11111" friend VS_STRING_CLASS& str_del ( VS_STRING_CLASS& target, int pos, int len ); // deletes `len' VS_CHARs starting from `pos' friend VS_STRING_CLASS& str_ins ( VS_STRING_CLASS& target, int pos, const VS_CHAR* s ); // inserts `s' in position `pos' friend VS_STRING_CLASS& str_ins_ch ( VS_STRING_CLASS& target, int pos, VS_CHAR ch ); // inserts `ch' in position `pos' friend VS_STRING_CLASS& str_replace( VS_STRING_CLASS& target, const VS_CHAR* out, const VS_CHAR* in ); // replace `out' w. `in' friend VS_STRING_CLASS& str_copy ( VS_STRING_CLASS& target, const VS_CHAR* source, int pos, int len ); // returns `len' VS_CHARs from `pos' friend VS_STRING_CLASS& str_left ( VS_STRING_CLASS& target, const VS_CHAR* source, int len ); // returns `len' VS_CHARs from the left friend VS_STRING_CLASS& str_right ( VS_STRING_CLASS& target, const VS_CHAR* source, int len ); // returns `len' VS_CHARs from the right friend VS_STRING_CLASS& str_sleft ( VS_STRING_CLASS& target, int len ); // self-left -- just as 'str_left()' but works on `target' friend VS_STRING_CLASS& str_sright( VS_STRING_CLASS& target, int len ); // self-right -- just as 'str_right()' but works on `target' friend VS_STRING_CLASS& str_trim_left ( VS_STRING_CLASS& target, int len ); // trims `len' VS_CHARs from the beginning (left) friend VS_STRING_CLASS& str_trim_right( VS_STRING_CLASS& target, int len ); // trim `len' VS_CHARs from the end (right) friend VS_STRING_CLASS& str_cut_left ( VS_STRING_CLASS& target, const VS_CHAR* charlist ); // remove all VS_CHARs `charlist' from the beginning (i.e. from the left) friend VS_STRING_CLASS& str_cut_right( VS_STRING_CLASS& target, const VS_CHAR* charlist ); // remove all VS_CHARs `charlist' from the end (i.e. from the right) friend VS_STRING_CLASS& str_cut ( VS_STRING_CLASS& target, const VS_CHAR* charlist ); // does `str_cut_right(charlist);str_cut_left(charlist);' friend VS_STRING_CLASS& str_cut_spc ( VS_STRING_CLASS& target ); // does `str_cut(" ");' friend VS_STRING_CLASS& str_pad ( VS_STRING_CLASS& target, int len, VS_CHAR ch ); friend VS_STRING_CLASS& str_comma( VS_STRING_CLASS& target, VS_CHAR delim ); // next 3 functions are safe! so if you get/set out of the VS_STRING_CLASS range! friend void str_set_ch( VS_STRING_CLASS& target, int pos, const VS_CHAR ch ); // sets `ch' VS_CHAR at position `pos' friend VS_CHAR str_get_ch( VS_STRING_CLASS& target, int pos ); // return VS_CHAR at position `pos', -1 for the last VS_CHAR etc... friend void str_add_ch( VS_STRING_CLASS& target, const VS_CHAR ch ); // adds `ch' at the end friend void str_add_ch_range( VS_STRING_CLASS &target, const VS_CHAR fr, const VS_CHAR to ); // adds all from `fr' to 'to' at the end friend VS_CHAR* str_word( VS_STRING_CLASS& target, const VS_CHAR* delimiters, VS_CHAR* result ); friend VS_CHAR* str_rword( VS_STRING_CLASS& target, const VS_CHAR* delimiters, VS_CHAR* result ); // check VS_ARRAY_CLASS::split() instead of word() funtions... //FIXME: TODO: str_sprintf() should return VS_STRING_CLASS! // this `sprintf'-like function works as follows: // 1. set `this.VS_STRING_CLASS' length to `init_size' // 2. call `sprintf' with `format' and `...' // NOTE: You have to supply enough `init_size'! sorry... friend int sprintf( int init_size, VS_STRING_CLASS& target, const VS_CHAR *format, ... ); // this is equal to `printf( 1024, format, ... )', i.e. `init_size=1024' friend int sprintf( VS_STRING_CLASS& target, const VS_CHAR *format, ... ); friend VS_STRING_CLASS& str_tr ( VS_STRING_CLASS& target, const VS_CHAR *from, const VS_CHAR *to ); friend VS_STRING_CLASS& str_up ( VS_STRING_CLASS& target ); friend VS_STRING_CLASS& str_low( VS_STRING_CLASS& target ); friend VS_STRING_CLASS& str_flip_case( VS_STRING_CLASS& target ); friend VS_STRING_CLASS& str_reverse( VS_STRING_CLASS& target ); // reverse the VS_STRING_CLASS: `abcde' becomes `edcba' friend VS_STRING_CLASS& str_squeeze( VS_STRING_CLASS& target, const VS_CHAR* sq_VS_CHARs ); // squeeze encountered repeating VS_CHARs to one only /* utilities */ void print(); // print string data to stdout (console) /* conversions/reversed char type functions */ VS_STRING_CLASS( const VS_CHAR_R* prs ) { box = new VS_STRING_BOX(); set( prs ); }; const VS_STRING_CLASS& operator = ( const VS_STRING_CLASS_R& rs ); const VS_STRING_CLASS& operator = ( const VS_CHAR_R* prs ); void set( const VS_CHAR_R* prs ); #ifdef _VSTRING_WIDE_ int set_failsafe( const char* mbs ); // convert from multi-byte string to wide string, returns error chars count #endif }; /* end of VS_STRING_CLASS class */ /**************************************************************************** ** ** VS_STRING_CLASS Functions (for class VS_STRING_CLASS) ** ****************************************************************************/ /**************************************************************************** ** ** VS_STRING_CLASS Functions (for VS_CHAR*) ** ****************************************************************************/ VS_CHAR* str_set( VS_CHAR* target, const VS_CHAR* ps ); VS_CHAR* str_mul( VS_CHAR* target, int n ); // multiplies the VS_STRING_CLASS n times, i.e. "1"*5 = "11111" VS_CHAR* str_del ( VS_CHAR* target, int pos, int len ); // deletes `len' VS_CHARs starting from `pos' VS_CHAR* str_ins ( VS_CHAR* target, int pos, const VS_CHAR* s ); // inserts `s' string in position `pos' VS_CHAR* str_ins_ch ( VS_CHAR* target, int pos, VS_CHAR ch ); // inserts `ch' VS_CHAR in position `pos' VS_CHAR* str_replace( VS_CHAR* target, const VS_CHAR* out, const VS_CHAR* in ); // replace `out' w. `in' inline int __str_copy_calc_offsets( const VS_CHAR* source, int& pos, int& len ); VS_CHAR* str_copy ( VS_CHAR* target, const VS_CHAR* source, int pos = 0, int len = -1 ); // returns `len' VS_CHARs from `pos' VS_CHAR* str_left ( VS_CHAR* target, const VS_CHAR* source, int len ); // returns `len' VS_CHARs from the left VS_CHAR* str_right ( VS_CHAR* target, const VS_CHAR* source, int len ); // returns `len' VS_CHARs from the right VS_CHAR* str_sleft ( VS_CHAR* target, int len ); // "self-left" i.e. just as 'left' but works on `target' VS_CHAR* str_sright( VS_CHAR* target, int len ); // "self-right" i.e. just as 'right' but works on `target' VS_CHAR* str_trim_left ( VS_CHAR* target, int len ); // trims `len' VS_CHARs from the beginning (left) VS_CHAR* str_trim_right( VS_CHAR* target, int len ); // trim `len' VS_CHARs from the end (right) // next 2 functions are safe. so if you get/set out of the VS_CHAR* length range. // note: that `VS_CHAR*' funcs are slower because of initial strlen() check void str_set_ch( VS_CHAR* target, int pos, const VS_CHAR ch ); // sets `ch' VS_CHAR at position `pos' VS_CHAR str_get_ch( VS_CHAR* target, int pos ); // return VS_CHAR at position `pos', -1 for the last VS_CHAR etc... void str_add_ch( VS_CHAR* target, const VS_CHAR ch ); // adds `ch' at the end void str_add_ch_range( VS_CHAR* target, const VS_CHAR fr, const VS_CHAR to ); // adds all from `fr' to 'to' at the end // return first `word' (will be stored to `resolt'), // i.e. from pos 0 to first found delimiter VS_CHAR // after that deletes this `word' from the `target' string. // returns NULL when no words left VS_CHAR* str_word ( VS_CHAR* target, const VS_CHAR* delimiters, VS_CHAR* result ); // ...same but `last' word reverse/rear VS_CHAR* str_rword( VS_CHAR* target, const VS_CHAR* delimiters, VS_CHAR* result ); VS_CHAR* str_cut_left ( VS_CHAR* target, const VS_CHAR* charlist ); // remove all VS_CHARs `charlist' from the beginning (i.e. from the left) VS_CHAR* str_cut_right( VS_CHAR* target, const VS_CHAR* charlist ); // remove all VS_CHARs `charlist' from the end (i.e. from the right) VS_CHAR* str_cut ( VS_CHAR* target, const VS_CHAR* charlist ); // does `CutR(charlist);CutL(charlist);' VS_CHAR* str_cut_spc ( VS_CHAR* target ); // does `str_cut(" ");' // expand align in a field, filled w. `ch', if len > 0 then right, else left VS_CHAR* str_pad( VS_CHAR* target, int len, VS_CHAR ch = VS_CHAR_L(' ') ); // insert `commas' for 1000's delimiter or use another delimiter // VS_STRING_CLASS supposed to be a integer or real w/o `e' format VS_CHAR* str_comma( VS_CHAR* target, VS_CHAR delim = VS_CHAR_L('\'') ); // translate VS_CHARs from `from' to `to' // length of `from' MUST be equal to length of `to' VS_CHAR* str_tr( VS_CHAR* target, const VS_CHAR *from, const VS_CHAR *to ); VS_CHAR* str_up ( VS_CHAR* target ); VS_CHAR* str_low( VS_CHAR* target ); VS_CHAR* str_flip_case( VS_CHAR* target ); VS_CHAR* str_reverse( VS_CHAR* target ); // reverse the VS_STRING_CLASS: `abcde' becomes `edcba' VS_CHAR* str_squeeze( VS_CHAR* target, const VS_CHAR* sq_VS_CHARs ); // squeeze repeating VS_CHARs to one only /**************************************************************************** ** ** VS_STRING_CLASS Functions (for const VS_CHAR*) ** ****************************************************************************/ VS_STRING_CLASS str_up ( const VS_CHAR* src ); VS_STRING_CLASS str_low( const VS_CHAR* src ); VS_STRING_CLASS str_flip_case( const VS_CHAR* src ); /**************************************************************************** ** ** VS_STRING_CLASS Functions -- common (VS_STRING_CLASS class will pass transparently here) ** ****************************************************************************/ int str_find ( const VS_CHAR* target, const VS_CHAR c, int startpos = 0 ); // returns first zero-based position of VS_CHAR, or -1 if not found int str_rfind( const VS_CHAR* target, const VS_CHAR c, int startpos = 0 ); // returns last zero-based position of VS_CHAR, or -1 if not found. if startpos is negative, will be skipped from the end, if positive will be absolute post to start from. int str_find ( const VS_CHAR* target, const VS_CHAR* s, int startpos = 0 ); // returns first zero-based position of VS_STRING_CLASS, or -1 if not found int str_rfind( const VS_CHAR* target, const VS_CHAR* s, int startpos = 0 ); // returns last zero-based position of VS_STRING_CLASS, or -1 if not found. if startpos is negative, will be skipped from the end, if positive will be absolute post to start from. int str_count( const VS_CHAR* target, const VS_CHAR* charlist, int startpos = 0 ); // returns match count of all VS_CHARs from `charlist' int str_str_count( const VS_CHAR* target, const VS_CHAR* s, int startpos = 0 ); // returns match count of `s' VS_STRING_CLASS into target int str_is_int ( const VS_CHAR* target ); // check if VS_STRING_CLASS is correct int value int str_is_double( const VS_CHAR* target ); // check if VS_STRING_CLASS is correct double (w/o `e' format :( ) /*************************************************************************** ** ** VARRAYBOX ** ****************************************************************************/ class VS_ARRAY_BOX : public VRef { public: VS_STRING_CLASS** _data; int _size; int _count; int block_size; // current block size VS_ARRAY_BOX(); ~VS_ARRAY_BOX(); VS_ARRAY_BOX* clone(); void resize( int new_size ); void undef(); void set_block_size( int new_block_size ); }; /*************************************************************************** ** ** VARRAY ** ****************************************************************************/ class VS_ARRAY_CLASS { VS_ARRAY_BOX *box; int _fe; // foreach element index const VS_STRING_CLASS _ret_empty; // return-empty-container VS_STRING_CLASS _ret_str; // return-container void detach(); void q_sort( int lo, int hi, int (*q_strcmp)(const VS_CHAR *, const VS_CHAR *) ); void new_pos( int n ); void del_pos( int n ); public: int compact; VS_ARRAY_CLASS(); VS_ARRAY_CLASS( const VS_ARRAY_CLASS& arr ); VS_ARRAY_CLASS( const VS_TRIE_CLASS& tr ); ~VS_ARRAY_CLASS(); int count() { return box->_count; } // return element count void set_block_size( int new_block_size ) { if ( box ) box->set_block_size( new_block_size ); }; void ins( int n, const VS_CHAR* s ); // insert at position `n' void set( int n, const VS_CHAR* s ); // set/replace at position `n' void del( int n ); // delete at position `n' const VS_CHAR* get( int n ); // get at position `n' void undef() // clear the array (frees all elements) { box->unref(); box = new VS_ARRAY_BOX(); _ret_str = VS_CHAR_L(""); } int push( const VS_CHAR* s ); // add to the end of the array int push( VS_TRIE_CLASS *tr ); // add to the end of the array int push( VS_ARRAY_CLASS *arr ); // add to the end of the array const VS_CHAR* pop(); // get and remove the last element int unshift( const VS_CHAR* s ); // add to the beginning of the array int unshift( VS_TRIE_CLASS *tr ); // add to the beginning of the array int unshift( VS_ARRAY_CLASS *arr ); // add to the beginning of the array const VS_CHAR* shift(); // get and remove the first element void ins( int n, const VS_STRING_CLASS& vs ); // insert at position `n' void set( int n, const VS_STRING_CLASS& vs ); // set/replace at position `n' int push( const VS_STRING_CLASS& vs ); // add to the end of the array int unshift( const VS_STRING_CLASS& vs ); // add to the beginning of the array void sort( int rev = 0, int (*q_strcmp)(const VS_CHAR *, const VS_CHAR *) = NULL ); // sort (optional reverse order) void reverse(); // reverse elements order void shuffle(); // randomize element order with Fisher-Yates shuffle VS_STRING_CLASS& operator []( int n ) { if ( n < 0 ) { _ret_str = VS_CHAR_L(""); return _ret_str; } if ( n >= box->_count ) set( n, VS_CHAR_L("") ); else detach(); // I don't know if user will change returned VS_STRING_CLASS?! return *box->_data[n]; } // FIXME: TODO: verify behaviour! const VS_STRING_CLASS& operator []( int n ) const { if ( n < 0 || n >= box->_count ) { return _ret_empty; } return *box->_data[n]; } const VS_ARRAY_CLASS& operator = ( const VS_ARRAY_CLASS& arr ) { box->unref(); box = arr.box; box->ref(); return *this; }; const VS_ARRAY_CLASS& operator = ( const VS_TRIE_CLASS& tr ) { undef(); push( (VS_TRIE_CLASS*)&tr ); return *this; }; const VS_ARRAY_CLASS& operator = ( const VS_STRING_CLASS& str ) { undef(); push( str ); return *this; }; const VS_ARRAY_CLASS& operator += ( const VS_ARRAY_CLASS& arr ) { push( (VS_ARRAY_CLASS*)&arr ); return *this; }; const VS_ARRAY_CLASS& operator += ( const VS_TRIE_CLASS& tr ) { push( (VS_TRIE_CLASS*)&tr ); return *this; }; const VS_ARRAY_CLASS& operator += ( const VS_STRING_CLASS& str ) { push( str ); return *this; }; /* utilities */ void print(); // print array data to stdout (console) int fload( const char* fname ); // return 0 for ok int fsave( const char* fname ); // return 0 for ok int fload( FILE* f ); // return 0 for ok int fsave( FILE* f ); // return 0 for ok /* implement `foreach'-like interface */ void reset() // reset position to beginning { _fe = -1; }; const VS_CHAR* next() // get next item or NULL for the end { _fe++; return _fe < box->_count ? box->_data[_fe]->data() : NULL; }; const VS_CHAR* current() // get latest item got from next() -- current one { return _fe < box->_count ? box->_data[_fe]->data() : NULL; }; int current_index() // current index { return _fe < box->_count ? _fe : -1; }; int max_len(); // return the length of the longest string in the array int min_len(); // return the length of the shortest string in the array }; /*************************************************************************** ** ** VTRIENODE -- INTERNAL! ** ****************************************************************************/ class VS_TRIE_NODE { public: VS_TRIE_NODE(); ~VS_TRIE_NODE(); VS_TRIE_NODE *next; VS_TRIE_NODE *down; VS_CHAR c; VS_STRING_CLASS *data; void detach() { next = down = NULL; } VS_TRIE_NODE *clone(); void print(); }; /*************************************************************************** ** ** VTRIEBOX -- INTERNAL! ** ****************************************************************************/ class VS_TRIE_BOX : public VRef { public: VS_TRIE_NODE *root; VS_TRIE_BOX() { root = new VS_TRIE_NODE(); } ~VS_TRIE_BOX() { ASSERT( root ); delete root; } VS_TRIE_NODE* find_node( VS_TRIE_NODE* node, const VS_CHAR* key, int create = 0 ); void del_node( VS_TRIE_NODE* node, const VS_CHAR *key, int branch = 0 ); int count_data_nodes( VS_TRIE_NODE* node ); VS_TRIE_BOX* clone(); void undef() { ASSERT( root ); delete root; root = new VS_TRIE_NODE(); }; int vacuum_node( VS_TRIE_NODE* node ); int vacuum(); }; /*************************************************************************** ** ** VTRIE ** ****************************************************************************/ class VS_TRIE_CLASS { VS_TRIE_BOX *box; void detach(); void trace_node( VS_TRIE_NODE *node, VS_ARRAY_CLASS* keys, VS_ARRAY_CLASS *vals ); void print_trace_node( VS_TRIE_NODE *node, int level ); VS_STRING_CLASS temp_key; public: int compact; VS_TRIE_CLASS(); VS_TRIE_CLASS( const VS_ARRAY_CLASS& arr ); VS_TRIE_CLASS( const VS_TRIE_CLASS& tr ); ~VS_TRIE_CLASS(); int vacuum() { return box->vacuum(); }; int count( const VS_CHAR* key = NULL ); void set( const VS_CHAR* key, const VS_CHAR* data ); // set data, same as []= void del( const VS_CHAR* key, int branch = 0 ); // remove data associated with `key' or all data below this branch const VS_CHAR* get( const VS_CHAR* key ); // get data by `key', same as [] int exists( const VS_CHAR* key ); // return != 0 if key exist (i.e. is used) void undef() // delete all key+data pairs { box->unref(); box = new VS_TRIE_BOX(); } void keys_and_values( VS_ARRAY_CLASS *keys, VS_ARRAY_CLASS *values ); VS_ARRAY_CLASS keys(); // returns VS_ARRAY_CLASS with keys currently used VS_ARRAY_CLASS values(); // returns VS_ARRAY_CLASS with keys' values void reverse(); // reverse keys <-> values void merge( VS_TRIE_CLASS *tr ); // adds keys+values (or modify existing keys) void merge( VS_ARRAY_CLASS *arr ); // adds keys+values (by VS_ARRAY_CLASS pair values) //void print_nodes() { print_node( root ); }; // for debug only void print(); // print trie data to stdout (console) void print_trace(); // print trie data structure details int fload( const char* fname ); // return 0 for ok int fsave( const char* fname ); // return 0 for ok int fload( FILE* f ); // return 0 for ok int fsave( FILE* f ); // return 0 for ok VS_STRING_CLASS& operator []( const VS_CHAR* key ) { detach(); // I don't know if user will change returned VS_STRING_CLASS?! VS_TRIE_NODE *node = box->find_node( box->root, key, 1 ); ASSERT( node ); if ( ! node->data ) node->data = new VS_STRING_CLASS(); return *(node->data); } const VS_TRIE_CLASS& operator = ( const VS_TRIE_CLASS& tr ) { box->unref(); box = tr.box; box->ref(); return *this; }; const VS_TRIE_CLASS& operator = ( const VS_ARRAY_CLASS& arr ) { undef(); merge( (VS_ARRAY_CLASS*)&arr ); return *this; }; const VS_TRIE_CLASS& operator += ( const VS_ARRAY_CLASS& arr ) { merge( (VS_ARRAY_CLASS*)&arr ); return *this; }; const VS_TRIE_CLASS& operator += ( const VS_TRIE_CLASS& tr ) { merge( (VS_TRIE_CLASS*)&tr ); return *this; }; }; /**************************************************************************** ** ** VS_STRING_CLASS Utility functions ** ****************************************************************************/ // str_chop() removes last VS_CHAR from a VS_STRING_CLASS (perl-like) inline VS_CHAR* str_chop( VS_CHAR* target ) { return str_trim_right( target, 1 ); } inline VS_STRING_CLASS& str_chop( VS_STRING_CLASS& target ) { return str_trim_right( target, 1 ); } /* reduces VS_STRING_CLASS to the given width using dots: "this is long line" -> "this...ine" `s' can be NULL, then target will be reduced */ VS_STRING_CLASS str_dot_reduce( const VS_CHAR* s, int width ); /**************************************************************************** ** ** VS_STRING_CLASS file names utilities -- functions and classes ** NOTE: does not use any external (outside this library) function calls! ** ****************************************************************************/ // adds trailing '/' if not exist VS_CHAR* str_fix_path( VS_CHAR* s, int slashtype = VS_CHAR_L('/') ); const VS_STRING_CLASS& str_fix_path( VS_STRING_CLASS& s, int slashtype = VS_CHAR_L('/') ); VS_STRING_CLASS str_file_ext( const VS_CHAR *ps ); // `ext' VS_STRING_CLASS str_file_name( const VS_CHAR *ps ); // `filename' VS_STRING_CLASS str_file_name_ext( const VS_CHAR *ps ); // `filename.ext' VS_STRING_CLASS str_file_path( const VS_CHAR *ps ); // `/path/' /* removes "/../"s, `path' can be NULL, then dest is fixed */ VS_STRING_CLASS str_reduce_path( const VS_CHAR* path ); /**************************************************************************** ** ** VS_STRING_CLASS Conversions ** ****************************************************************************/ long hex2long( const VS_CHAR* s ); // hex to long /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vstruti.cpp0000644000175000017500000000226214367022256014552 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vstruti.h" VString& str_padw( VString& target, int len, char ch ) { WString str; str = target; str_pad( str, len, wchar_t( ch ) ); target = str; return target; } char* str_padw( char* target, int len, char ch ) { WString str; VString vvv; str = target; str_pad( str, len, wchar_t( ch ) ); vvv = str; strcpy( target, vvv.data() ); return target; } vfu-5.09/vstring/vstring.h0000644000175000017500000000215414367022256014173 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _VSTRING_H_ #define _VSTRING_H_ #undef _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstring_internal.h" #endif /* TOP */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/wstrlib.h0000644000175000017500000000216014367022256014162 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _WSTRLIB_H_ #define _WSTRLIB_H_ #define _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstrlib_internal.h" #endif /* *TOP* */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/wstring.cpp0000644000175000017500000000217214367022256014527 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vref.h" #undef _VSTRING_WIDE_ #include "vdef.h" #include "vstring_internal.h" #define _VSTRING_WIDE_ #include "vdef.h" #include "vstring_internal.cpp" /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vstruti.h0000644000175000017500000000201414367022256014212 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _VSTRUTI_H_ #define _VSTRUTI_H_ #include "vstring.h" #include "wstring.h" VString& str_padw( VString& target, int len, char ch = ' ' ); char* str_padw( char* target, int len, char ch = ' ' ); #endif /* TOP */ vfu-5.09/vstring/wstrlib.cpp0000644000175000017500000000165214367022256014522 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #define _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstring_internal.h" #include "vstrlib_internal.cpp" vfu-5.09/vstring/TODO0000644000175000017500000000020714365574237013024 0ustar cadecade-- get_prematch() $` -- get_match() $& -- get_postmatch() $' -- s() replace() $1 $2 ... $` $& $' ... vfu-5.09/vstring/test.cpp0000644000175000017500000003303714444676576014035 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include #include "vstring.h" #include "vstrlib.h" void test1() { VString str = "Hello"; str += " World"; // str is `Hello World' now str_reverse( str ); // str is `dlroW olleH' now str_low( str ); // lower case VArray va = str_split( " +", str ); // array contains `dlroW' at pos 0 and `olleH' at 1 va.reverse(); // array reversed: `dlroW' at pos 1 and `olleH' at 0 int z; for( z = 0; z < va.count(); z++ ) { str_reverse( va[z] ); // reverses each string element } str = str_join( va, " " ); // joins into temporary string printf( "************************ test 1 result is: %s\n", str.data() ); // this should print `hello world' } void test2() { VArray va; va.push( "hello" ); // pos 0 va.push( "world" ); // pos 1 va.ins( 1, "your" ); // pos 1 shifted va[1] = "my"; // replaces `your' va[3] = "!"; // set outside the size, array is extended VString str = va.pop(); // pops last element, str is now `!' str = str_join( va, "-" ); // joins to given string str_tr( str, "-", " " ); // replaces dashes with spaces str_replace( str, " my ", " " ); // removes ` my ' printf( "************************ test 2 result is: %s\n", str.data() ); // this should print `hello world' } void test3() { VTrie tr; // hash-like VArray va; // inserting keys and values tr[ "tralala" ] = "data1"; tr[ "opala" ] = "data2"; tr[ "keynext" ] = "data3"; printf( "VTrie items count = %d\n", tr.count() ); ASSERT( tr.count() == 3 ); // inserting elements into the array va.push( "this" ); va.push( "just" ); va.push( "test" ); va.push( "simple" ); VString zzss = va[1]; // adding string to the first element of the array va[1] += " x2"; // the array is converted to trie (hash) and merged into `tr' tr += va; // same as: tr.merge( &va ); ASSERT( tr.count() == 5 ); // clear the array--remove all elements va.undef(); // take keys from `tr' as array and store them into va, returns count // i.e. i = tr.count(); int i; va = tr.keys(); printf( "keys count = %d\n", va.count() ); tr.del( "test" ); ASSERT( tr.count() == 4 ); tr.del( "t", 1 ); printf( "--------------------\n" ); tr.print(); printf( "--------------------\n" ); ASSERT( tr.count() == 2 ); tr["thisistest"] = "123"; tr["thisisnottest"] = "456"; ASSERT( tr.count( "this" ) == 2 ); ASSERT( tr.count( "that" ) == 0 ); ASSERT( tr.count( "opa" ) == 1 ); ASSERT( tr.count( ) == 4 ); tr.undef(); tr["thisisnottest"] = "456"; tr["thisistest"] = "456"; tr.del("thisistest"); int vc = tr.vacuum(); printf( "vacuum count = %d\n", vc ); // printing the array and trie data for( i = 0; i < va.count(); i++ ) { printf( "%d -> %s (%s)\n", i, va[i].data(), tr[ va[i] ].data() ); } VArray v1; printf( "--------------------\n" ); v1 = tr; // same as: v1.undef; v1.push( &tr ); v1.print(); // print array data VRegexp re( "a([0-9]+)" ); // compiling new regexp printf( "regexp error: %s\n", re.error_str() ); if( re.m( "tralala85.zz" ) ) // match against regexp { printf( "sub 0 = %s\n", re[0].data() ); // re[1] returns `85' printf( "sub 1 = %s\n", re[1].data() ); // re[1] returns `85' } VString vs; if( re.m( "tralala85.", "a(la)+" ) ) // match against regexp { printf( "sub 0 = %s\n", re[0].data() ); // `lala' printf( "sub 1 = %s\n", re[1].data() ); // `la' } printf( "--------------------\n" ); v1 = str_split( ",", "*.tralala,opala and another one" ); // splits on spaces v1.print(); printf( "joined: %s\n", (const char*)str_join( v1, "---" ) ); // join the same data back VString m1 = v1[0]; VString m2 = v1[1]; printf( "1[%s] 2[%s]\n", m1.data(), m2.data() ); printf( "--------------------\n" ); v1 = str_split( " +", "tralala opala and another one", 3 ); // splits data on spaces up to 3 elements v1.print(); //exit(1); printf( "--------------------\n" ); v1[1] = "hack this one here"; // set (overwrite) element 1 str_sleft( v1[2], 11 ); // reset element 2 to the left 11 chars only v1[0] = 12345; // convert integer into string v1.print(); printf( "--------------------\n" ); VArray aa[3]; // array of arrays aa[0] = str_split( " ", "this is just a simple test" ); aa[1] = str_split( " ", "never ending story" ); aa[2] = str_split( " ", "star-wars rulez" ); aa[0][1] = "was"; // first array, second element, replaces `is' with `was' aa[2][0] = "slackware"; // third array, first element, `star-wars' is now `slackware' // expands the array from 3 to 11 elements aa[1][10] = "king of the hill"; for( i = 0; i < 3; i++ ) { printf("---\n"); aa[i].print(); } printf( "---box test-----------------------------\n" ); i = 20; while( i-- ) { v1.push( "this" ); v1.push( "just" ); v1.push( "test" ); v1.push( "simple" ); } v1.print(); VArray vv = v1; // this makes vv data aliased to the data of v1 vv.print(); // actually print the v1's data which is shared right now vv.set( 0, "---" ); // vv makes own copy of the array data vv.print(); // vv's data is no more aliased to v1's VRegexp re_see( "^\\s*see\\s*=\\s*([^, \011]*)\\s*,(.*)$", "i" ); if( re_see.m( "see=*.tgz,tralala" ) ) { VString str; str = str + re_see[1] + re_see[2]; printf( "VRegexp[1+2]=[%s]\n", str.data() ); } printf( "************************ test 3 ends here\n" ); } void test4() { // this is regression test, please ignore it... int i; int ii; VArray va; ii = 20; i = ii; while( i-- ) { va = str_split( ",", "this is, just a simple. but fixed, nonsense test, voila :)" ); printf( "%d%% va count = %d\n", (100*i)/ii, va.count() ); } VString set; VString cat; VString setn; VString catn; VString sete; VString setp; i = 2000; while( i-- ) { set.set( "this is, just a simple. but fixed, nonsense test, voila :)" ); cat.cat( "this is, just a simple. but fixed, nonsense test, voila :)" ); setn.setn( "this is, just a simple. but fixed, nonsense test, voila :)", 20 ); catn.catn( "this is, just a simple. but fixed, nonsense test, voila :)", 20 ); sete = "this is, just a simple. but fixed, nonsense test, voila :)"; setp += "this is, just a simple. but fixed, nonsense test, voila :)"; } printf( "set = %d\n", (int)str_len( set ) ); printf( "cat = %d\n", (int)str_len( cat ) ); printf( "setn = %d\n", (int)str_len( setn ) ); printf( "catn = %d\n", (int)str_len( catn ) ); printf( "sete = %d\n", (int)str_len( sete ) ); printf( "setp = %d\n", (int)str_len( setp ) ); printf( "--------------------\n" ); i = 2000; while( i-- ) { set = "this is, just a simple. but fixed, nonsense test, voila :)"; setn = set; str_del( set, 20, 10 ); str_ins( set, 30, "***opa***" ); str_replace( setn, "i", "[I]" ); } printf( "set = %s\n", set.data() ); printf( "setn = %s\n", setn.data() ); printf( "---array sort-------\n" ); va.undef(); va = str_split( "[, \t]+", "this is, just a simple. but fixed, nonsense test, voila :)" ); va.sort(); va.print(); printf( "--------------------\n" ); va.sort( 1 ); va.print(); printf( "--------------------\n" ); } void test5() { VTrie tr; // hash-like VArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr.print(); tr.reverse(); tr.print(); tr.reverse(); tr.print(); VCharSet cs; cs.push( 'a' ); printf( "char_set: %d, %d\n", cs.in( 'a' ), cs.in( 'z' ) ); cs.undef( 'a' ); printf( "char_set: %d, %d\n", cs.in( 'a' ), cs.in( 'z' ) ); cs.undef(); int i = 2000; while( i-- ) { cs.push( i ); } cs.undef(); printf( "************************ test 5 ends here\n" ); } void test6() { VRegexp re; VArray va; re.comp( "^([^!]+)!(.+)=apquxz(.+)$" ); int i = re.m( "abc!pqr=apquxz.ixr.zzz.ac.uk" ); i--; while( i >= 0 ) { va.push( re[i] ); i--; } va.print(); va.undef(); va += "/this/is/samle/file.tail"; va += "/file.tail"; va += "/this/is/./samle/file.tail/"; va += "/this/..../is/../samle/.file.tail"; va += "/.file.tail"; va += "/"; const char* ps; va.reset(); while( ( ps = va.next() ) ) { printf( "------------------------------------\n" ); printf( "file is: %s\n", ps ); printf( "path is: %s\n", (const char*)str_file_path( ps ) ); printf( "name is: %s\n", (const char*)str_file_name( ps ) ); printf( "ext is: %s\n", (const char*)str_file_ext( ps ) ); printf( "n+ex is: %s\n", (const char*)str_file_name_ext( ps ) ); printf( "reduced path is: %s\n", (const char*)str_reduce_path( ps ) ); printf( "dot reduce sample is: %s\n", (const char*)str_dot_reduce( ps, 10 ) ); } va.fsave( "/tmp/a.aaa" ); va.fload( "/tmp/a.aaa" ); va.print(); } void test7() { VTrie tr; // hash-like VTrie tr2; // hash-like VArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr.print(); printf( "---------------------------------1---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------2---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------3---\n" ); tr2 = str_split( " ", "this is simple one way test" ); tr2.print(); printf( "---------------------------------4---\n" ); tr2 += tr; tr2.print(); printf( "---------------------------------5---\n" ); va = tr2; va.print(); printf( "---------------------------------6---\n" ); } void test8() { VString v1; VString v2; v1 = "this is simple test "; v1 *= 1024; printf( "v1 len: %d\n", (int)str_len( v1 ) ); v2.compact( 1 ); // makes v2 compact, i.e. it will get as much memory as it // needs. otherwise it will get fixed amount of blocks v2 = v1; // data is shared between v1 and v2. any change to v1 or v2 will // detach this data and both will get own copy v2[0] = ' '; // this will create own data for v2 str_tr( v2, "ti", "TI" ); // capitalize T and I v2 = ""; // this will free all data allocated by v2 printf( "copy 7,6: [%s]", (const char*)str_copy( v2, v1, 8, 6 ) ); printf( "copy 10: [%s]", (const char*)str_copy( v2, v1, -10 ) ); printf( "************************ test 5 ends here\n" ); } void test9() { VArray va; VTrie tr; printf( "---9---------------------------------------------------\n" ); va.push( "one" ); va.push( "two" ); va.push( "tri" ); va.push( "pet" ); tr = va; tr.print(); va.push( &tr ); va.print(); VArray va2; va2.push( "1" ); va2.push( "2" ); va2.push( "3" ); va2.push( "4" ); va2.unshift( "0" ); va2.unshift( &va ); va2.push( &va ); va2.print(); } void test0() { VTrie tr; tr["testone"] = "1"; tr["testwo"] = "2"; tr["tree"] = "3"; tr.print_trace(); printf( "\n-------------------------------------------------------------------\n\n" ); tr.del( "test", 1 ); tr.print(); printf( "\n-------------------------------------------------------------------\n\n" ); tr.print_trace(); } int main( void ) { test0(); #if 0 char t[256] = "123456----------------------------------------9999999999999"; char T[256] = "123456----------------------------------------9999999999999"; str_trim_left( t, 3 ); printf( "%s\n", t ); for( long z; z < 300000000; z++ ) { //str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str //str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str //memmove( T, t, 222 ); //memcpy( T, t, 222 ); //str_copy( T, t, 0, 222 ); // check for overlapping borders, begin of str } #endif /* VString nu = "is not null"; nu = NULL; ASSERT( nu == "" ); */ char t[92] = "this is simple test"; char r[92] = "1111111111111111111"; str_word( t, " ", r ); ASSERT( strcmp( t, "is simple test" ) == 0 ); ASSERT( strcmp( r, "this" ) == 0 ); strcpy( t, " opa" ); str_cut_left( t, " " ); ASSERT( strcmp( t, "opa" ) == 0 ); strcpy( t, "opa " ); str_cut_right( t, " " ); ASSERT( strcmp( t, "opa" ) == 0 ); strcpy( t, "this is good" ); str_ins( t, 8, "not " ); ASSERT( strcmp( t, "this is not good" ) == 0 ); str_del( t, 8, 4 ); ASSERT( strcmp( t, "this is good" ) == 0 ); strcpy( t, "more" ); str_mul( t, 3 ); ASSERT( strcmp( t, "moremoremore" ) == 0 ); str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str strcpy( t, "despicable me" ); str_word( t, " ", r ); ASSERT( strcmp( r, "despicable" ) == 0 ); str_word( t, " ", r ); ASSERT( strcmp( r, "me" ) == 0 ); ASSERT( t[0] == 0 ); /**/ test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); //*/ return 0; } vfu-5.09/vstring/vstrlib.cpp0000644000175000017500000000165014367022256014517 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #undef _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstring_internal.h" #include "vstrlib_internal.cpp" vfu-5.09/vstring/vstrlib_internal.cpp0000644000175000017500000006541514372204612016416 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vstrlib_internal.h" /**************************************************************************** ** ** VString aditional functions ** ****************************************************************************/ #ifdef _VSTRING_WIDE_ VS_CHAR time2wstr_wchar_t_return[128]; const VS_CHAR* time2wstr( const time_t tim ) { time_t t = tim; mbstowcs( time2wstr_wchar_t_return, ctime( &t ), LENOF_VS_CHAR(time2wstr_wchar_t_return) ); return time2wstr_wchar_t_return; } #else const VS_CHAR* time2str( const time_t tim ) { time_t t = tim; return ctime( &t ); } #endif time_t str2time( const VS_CHAR* timstr ) { if ( str_len( timstr ) < 24) return 0; VS_CHAR ts[32]; struct tm m; memset( &m, 0, sizeof(m) ); VS_FN_STRCPY( ts, timstr ); str_up( ts ); // 0 5 10 5 20 4 // "Wed Jun 30 21:49:08 1993\n" ts[24] = 0; m.tm_year = VS_FN_STRTOL( ts + 20 ) - 1900; ts[19] = 0; m.tm_sec = VS_FN_STRTOL( ts + 17 ); ts[16] = 0; m.tm_min = VS_FN_STRTOL( ts + 14 ); ts[13] = 0; m.tm_hour = VS_FN_STRTOL( ts + 11 ); ts[10] = 0; m.tm_mday = VS_FN_STRTOL( ts + 8 ); ts[ 7] = 0; m.tm_mon = str_find( VS_CHAR_L("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"), ts+4 ) / 3; m.tm_yday = 0; m.tm_wday = 0; m.tm_isdst = -1; time_t tim = mktime( &m ); return tim; } /***************************************************************************** ** ** sfn_match function provides simplified pattern matching for the common ** *****************************************************************************/ int __sfn_eq( const VS_CHAR c1, const VS_CHAR c2, int flags ) { int cc1 = flags & SFN_CASEFOLD ? VS_FN_TOUPPER( c1 ) : c1; int cc2 = flags & SFN_CASEFOLD ? VS_FN_TOUPPER( c2 ) : c2; return cc1 == cc2; } int __sfn_match_charset( const VS_CHAR* charset, const VS_CHAR c, int flags, int *advance = NULL ) { VS_STRING_CLASS charset_str; const VS_CHAR* cs = charset; int r = 0; if( *cs == VS_CHAR_L('!') || *cs == VS_CHAR_L('^') ) { r = 1; cs++; } while( *cs && ( *cs != VS_CHAR_L(']') ) ) { if( ! ( flags & SFN_NOESCAPE ) && *cs == VS_CHAR_L('\\') ) { if( ! *++cs ) return 4; str_add_ch( charset_str, *cs ); } if( cs[1] == VS_CHAR_L('-') ) { if( ! cs[2] ) return 3; str_add_ch_range( charset_str, cs[0], cs[2] ); cs += 2; } else { str_add_ch( charset_str, cs[0] ); } cs++; } if( advance ) *advance = cs - charset; if( str_len( charset_str ) == 0 ) return 2; VS_CHAR cc = c; if( flags & SFN_CASEFOLD ) { str_up( charset_str ); cc = VS_FN_TOUPPER( cc ); } if( str_find( charset_str, cc ) >= 0 ) return r ? 1 : 0; return 1; } int sfn_match( const VS_CHAR* pattern, const VS_CHAR* string, int flags ) { const VS_CHAR* ps = pattern; const VS_CHAR* ss = string; if( ! *ps ) return 1; // empty pattern, will not match if( ! *ss ) return 1; // empty string, will not match while(4) { if( ! ( flags & SFN_NOESCAPE ) && *ps == VS_CHAR_L('\\') ) { if( ! *++ps ) return 6; if( *ps != *ss ) return 7; } else if( *ps == VS_CHAR_L('?') ) { if( ! *ss ) return 2; } else if( *ps == VS_CHAR_L('*') ) { while( *ps == VS_CHAR_L('*') ) ps++; if( ! *ps ) return 0; // pattern ends with *, will match anything while( *ss ) { if( sfn_match( ps, ss++, flags ) ) continue; return 0; } return 11; } else if( *ps == VS_CHAR_L('[') ) { ps++; int a = 0; int r = __sfn_match_charset( ps, *ss, flags, &a ); ps += a; // advance data position if( r != 0 ) return 4; } else { if( ! __sfn_eq( *ps, *ss, flags ) ) return 5; } if( ! *ps && ! *ss ) return 0; // end of pattern and string reached, matched so far, return ok ps++; ss++; } return 0; } /***************************************************************************** ** ** Knuth-Morris-Pratt search ** *****************************************************************************/ void __kmp_preprocess( const VS_CHAR* p, int ps, int* next ) { int i = 0; int j = next[0] = -1; while (i < ps) { while ((j > -1) && (p[i] != p[j])) j=next[j]; i++; j++; next[i] = p[i] == p[j] ? next[j] : j; } } #define MAX_KMP_PATTERN_SIZE 1024 int mem_kmp_search( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) { int i; int j; int next[MAX_KMP_PATTERN_SIZE]; if ( ps > MAX_KMP_PATTERN_SIZE ) return -1; __kmp_preprocess( p, ps, next ); i = j = 0; while (j < ds) { while (i > -1 && p[i] != d[j]) i = next[i]; i++; j++; if (i >= ps) return j - i; } return -1; } /***************************************************************************** ** ** Quick Search (simplified Boyer-Moore) ** ** Note: currently only 256-symbol alphabet supported (ASCII) ** ** The difference from Boyer-Moore is that good-suffix shift is not used. ** Actually this is quick search with Horspool hint which is that rightmost ** char is examined first. ** *****************************************************************************/ #define QS_ASIZE 256 void __qs_preprocess( const VS_CHAR* p, int ps, int* badc ) { int i; for (i = 0; i < QS_ASIZE; i++) badc[i] = ps + 1; for (i = 0; i < ps; i++) badc[(unsigned)(VS_CHAR)p[i]] = ps - i; } int mem_quick_search( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) { int badc[QS_ASIZE]; __qs_preprocess( p, ps, badc); int j = 0; while (j <= ds - ps) { int i; for ( i = ps - 1; i >= 0 && p[i] == d[i + j]; --i ); if ( i < 0 ) return j; j += badc[(unsigned)(VS_CHAR)d[j + ps]]; } return -1; } /* no-case version */ void __qs_preprocess_nc( const VS_CHAR* p, int ps, int* badc ) { int i; for (i = 0; i < QS_ASIZE; i++) badc[i] = ps + 1; for (i = 0; i < ps; i++) badc[toupper((VS_CHAR)p[i])] = ps - i; } int mem_quick_search_nc( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) { int badc[QS_ASIZE]; __qs_preprocess_nc( p, ps, badc); int j = 0; while (j <= ds - ps) { int i; for ( i = ps - 1; i >= 0 && toupper(p[i]) == toupper(d[i + j]); --i ); if ( i < 0 ) return j; j += badc[toupper((VS_CHAR)d[j + ps])]; } return -1; } /***************************************************************************** ** ** Sum search ** ** It is variation of Karp-Rabin idea of searching `similar' pattern and ** if found check the actual one. The hash function here is simple sum of ** bytes in the range of pattern size. ** ** Not much useful since Quick search performs better in almost all cases. ** Written for benchmarking purposes. ** *****************************************************************************/ int mem_sum_search( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) { int psum = 0; int i; for( i = 0; i < ps; i++ ) psum += p[i]; int j = 0; int sum = 0; while( j <= ds - ps ) { if ( sum == psum && memcmp( p, d + j, ps ) == 0 ) return j; sum -= d[j]; j++; sum += d[j+ps]; } return -1; } /***************************************************************************** ** ** mem_*_search benchmarks: ** ** (NOTE: These results are circa 2000 ** ** For simple benchmark I used ~700MB file and tried to find 10- and 70-chars ** patterns. Results in seconds for both cases (similar to 1-2 seconds) were: ** ** Quick 26 ** KMP 42 ** Sum 39 ** ** Even though KMP returns right result I prefer Quick search as default :)) ** ** Case insensitive search is 2 times slower due to the simple implementation. ** *****************************************************************************/ /***************************************************************************** ** ** file search frontend ** *****************************************************************************/ long file_pattern_search( const VS_CHAR *p, int ps, FILE* f, const VS_CHAR* opt, int (*mem_search)( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) ) { #define BUFSIZE (1024*1024) VS_CHAR* buff = new VS_CHAR[BUFSIZE]; int nocase = str_find( opt, 'i' ) > -1; VS_CHAR* np = new VS_CHAR[ps+1]; ASSERT(np); memcpy( np, p, ps ); np[ps] = 0; if ( ! mem_search ) mem_search = mem_quick_search; if ( nocase ) mem_search = mem_quick_search_nc; off_t pos = -1; while(4) { int bs = fread( buff, 1, BUFSIZE, f ); int cpos = mem_search( np, ps, buff, bs ); if ( cpos > -1 ) { pos = ftello(f) - bs + cpos; break; } else { fseeko( f, -ps, SEEK_CUR ); } if ( bs < BUFSIZE ) break; } delete [] np; delete [] buff; return pos; } long file_pattern_search( const VS_CHAR *p, int ps, const char* fn, const VS_CHAR* opt, int (*mem_search)( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) ) { FILE *f = fopen( fn, "r" ); if ( ! f ) return -1; int res = file_pattern_search( p, ps, f, opt, mem_search ); fclose( f ); return res; } /***************************************************************************** ** ** Grep -- regular expression search ** *****************************************************************************/ /* FGrep -- regular expression search (I know `G' here stands for :)) */ long file_grep( const VS_CHAR *re_string, const char* file_name, int nocase, off_t spos ) { FILE *f = fopen( file_name, "rb" ); if (!f) return -1; long pos = file_grep( re_string, f, nocase, spos ); fclose(f); return pos; } /* int file_grep_max_line = MAX_GREP_LINE; int file_grep_lines_read = 0; */ long file_grep( const VS_CHAR *re_string __attribute__((unused)), FILE* f __attribute__((unused)), int nocase __attribute__((unused)), off_t spos __attribute__((unused))) { /* if ( str_len(re_string) >= (size_t)file_grep_max_line ) return -2; // just in case, and for now... VS_CHAR newpat[MAX_PATTERN+1]; VS_FN_STRCPY( newpat, re_string ); if ( nocase ) str_up( newpat ); VS_REGEXP_CLASS re; if ( ! re.comp( newpat ) ) return -2; VS_CHAR *line = (VS_CHAR*)malloc( ( file_grep_max_line+1 ) * sizeof( VS_CHAR ) ); //TODO: FIXME: malloc func off_t opos = ftello( f ); ASSERT( spos >= -1 ); if (spos != -1) fseeko( f, spos, SEEK_SET ); off_t cpos = ftello( f ); file_grep_lines_read = 0; int found = 0; while( fgets( line, file_grep_max_line, f ) ) { if ( nocase ) str_up( line ); if ( re.m( line ) ) { found = 1; break; } cpos = ftello( f ); file_grep_lines_read++; if (feof(f)) break; } fseeko( f, opos, SEEK_SET ); if (found) cpos += ( re.sub_sp( 0 ) ); free(line); file_grep_max_line = MAX_GREP_LINE; return found ? cpos : -1; */ return -111; } /***************************************************************************** ** ** Search interface functions ** ** options are: ** ** i -- ignore case ** r -- regular expression (grep) ** h -- hex pattern search ** *****************************************************************************/ long file_string_search( const VS_CHAR *p, const char* fn, const VS_CHAR* opt ) { FILE *f = fopen( fn, "rb" ); if (!f) return -1; long pos = file_string_search( p, f, opt ); fclose(f); return pos; } long file_string_search( const VS_CHAR *p, FILE *f, const VS_CHAR* opt ) { int ps = str_len(p); ASSERT( ps < MAX_PATTERN ); int nocase = str_find( opt, VS_CHAR_L('i') ) > -1; long pos = -1; if( str_find( opt, VS_CHAR_L('r') ) > -1 ) { pos = file_grep( p, f, 0, -1 ); } else if( str_find( opt, VS_CHAR_L('h') ) > -1 ) { VS_CHAR new_p[MAX_PATTERN+1]; int pl = hex_string_to_pattern( p, new_p ); if (pl > 0) pos = file_pattern_search( new_p, pl, f, nocase ? VS_CHAR_L("i") : VS_CHAR_L("") ); } else { pos = file_pattern_search( p, str_len(p), f, nocase ? VS_CHAR_L("i") : VS_CHAR_L("") ); } return pos; } int mem_string_search( const VS_CHAR *p, const VS_CHAR* d, const VS_CHAR* opt ) { int ps = str_len(p); ASSERT( ps < MAX_PATTERN ); int nocase = str_find( opt, VS_CHAR_L('i') ) > -1; long pos = -1; if( str_find( opt, VS_CHAR_L('r') ) > -1 ) { VS_REGEXP_CLASS re; if ( ! re.comp( p ) ) return -1; if ( ! re.m( d ) ) return -1; pos = re.sub_sp( 0 ); } else if( str_find( opt, VS_CHAR_L('h') ) > -1 ) { VS_CHAR new_p[MAX_PATTERN+1]; int pl = hex_string_to_pattern( p, new_p ); if (pl > 0) { if ( nocase ) pos = mem_quick_search_nc( new_p, pl, d, str_len(d) ); else pos = mem_quick_search( new_p, pl, d, str_len(d) ); } } else { if ( nocase ) pos = mem_quick_search_nc( p, ps, d, str_len(d) ); else pos = mem_quick_search( p, ps, d, str_len(d) ); } return pos; } /*************************************************************************** ** ** VCHARSET ** ****************************************************************************/ VS_CHARSET_CLASS::VS_CHARSET_CLASS() { _data = NULL; _size = 0; } VS_CHARSET_CLASS::~VS_CHARSET_CLASS() { _data = NULL; _size = 0; } void VS_CHARSET_CLASS::resize( int new_size ) { if ( new_size < 1 ) { if ( _data ) delete [] _data; _data = NULL; _size = 0; return; } // calc required mem size in chars (bytes?) new_size = new_size / sizeof(VS_CHAR) + (new_size % sizeof(VS_CHAR) != 0); // pad mem size in blocks of VCHARSET_BLOCK_SIZE new_size = new_size / VCHARSET_BLOCK_SIZE + (new_size % VCHARSET_BLOCK_SIZE != 0); // calc size back to chars (bytes?) new_size *= VCHARSET_BLOCK_SIZE; VS_CHAR *new_data = new VS_CHAR[ new_size ]; memset( new_data, 0, new_size ); if ( _data ) { memcpy( new_data, _data, _size < new_size ? _size : new_size ); delete [] _data; } _data = new_data; _size = new_size; } void VS_CHARSET_CLASS::push( VS_CHAR n, int val ) { if ( n < 0 ) return; if ( n >= _size * (int)sizeof(VS_CHAR) ) resize( n + 1 ); if ( val ) _data[ n / sizeof(VS_CHAR) ] |= 1 << (n % sizeof(VS_CHAR)); else _data[ n / sizeof(VS_CHAR) ] &= ~(1 << (n % sizeof(VS_CHAR))); } void VS_CHARSET_CLASS::undef( VS_CHAR n ) { push( n, 0 ); } void VS_CHARSET_CLASS::undef() { resize( 0 ); } int VS_CHARSET_CLASS::in( VS_CHAR n ) { if ( n < 0 || n >= _size * (int)sizeof(VS_CHAR) ) return 0; return ( _data[ n / sizeof(VS_CHAR) ] & ( 1 << ( n % sizeof(VS_CHAR) ) ) ) != 0; } /* int VCharSet::get( int pn ) { if ( pn < 0 || pn >= size ) return 0; return (data[pn / 8] & (1 << (pn % 8))) != 0; } void VCharSet::set_range1( int start, int end ) // set range { char s = ( start < end ) ? start : end; char e = ( start > end ) ? start : end; for( int z = s; z <= e; z++) set1( z ); } void VCharSet::set_range0( int start, int end ) // set range { char s = ( start < end ) ? start : end; char e = ( start > end ) ? start : end; for( int z = s; z <= e; z++) set0( z ); } void VCharSet::set_str1( const char* str ) { int sl = str_len( str ); for( int z = 0; z < sl; z++ ) set1( str[z] ); } void VCharSet::set_str0( const char* str ) { int sl = str_len( str ); for( int z = 0; z < sl; z++ ) set0( str[z] ); } int VCharSet::in( const char *str ) { int sl = str_len( str ); for( int z = 0; z < sl; z++ ) if ( !in( str[z] ) ) return 0; return 1; } int VCharSet::resize( int p_size ) { ASSERT( p_size > 0 ); int new_size = p_size; int new_datasize = p_size / 8 + (p_size % 8 != 0); char *new_data = (char*)malloc( new_datasize ); if (new_data == NULL) return CE_MEM; memset( new_data, 0, new_datasize ); if (data) { memcpy( new_data, data, datasize < new_datasize ? datasize : new_datasize ); free( data ); data = NULL; } data = new_data; size = new_size; datasize = new_datasize; return CE_OK; } VCharSet& VCharSet::operator = ( const VCharSet &b1 ) { resize( b1.size ); memcpy( data, b1.data, datasize ); return *this; } VCharSet& VCharSet::operator &= ( const VCharSet &b1 ) { int z; for(z = 0; z < (datasize < b1.datasize ? datasize : b1.datasize ); z++) data[z] &= b1.data[z]; return *this; } VCharSet& VCharSet::operator |= ( const VCharSet &b1 ) { int z; for(z = 0; z < (datasize < b1.datasize ? datasize : b1.datasize ); z++) data[z] |= b1.data[z]; return *this; } VCharSet VCharSet::operator ~ () { VCharSet b; b = *this; int z; for(z = 0; z < b.datasize; z++) b.data[z] = ~b.data[z]; return b; } VCharSet operator & ( const VCharSet &b1, const VCharSet &b2 ) { VCharSet b; b = b1; b &= b2; return b; } VCharSet operator | ( const VCharSet &b1, const VCharSet &b2 ) { VCharSet b; b = b1; b |= b2; return b; } */ /*************************************************************************** ** ** VREGEXP ** ****************************************************************************/ VS_REGEXP_CLASS::VS_REGEXP_CLASS() { re = NULL; md = NULL; rc = 0; lp = NULL; pt = NULL; pl = 0; } VS_REGEXP_CLASS::VS_REGEXP_CLASS( const VS_CHAR* rs, const VS_CHAR* opt ) { re = NULL; md = NULL; rc = 0; lp = NULL; pt = NULL; pl = 0; opt_mode = MODE_REGEXP; comp( rs, opt ); } VS_REGEXP_CLASS::~VS_REGEXP_CLASS() { if ( re ) pcre2_code_free( re ); if ( md ) pcre2_match_data_free( md ); if ( pt ) delete pt; } int VS_REGEXP_CLASS::get_options( const VS_CHAR* opt ) { opt_mode = MODE_REGEXP; opt_nocase = 0; if ( ! opt ) return 0; if ( ! opt[0] ) return 0; int options = 0; int sl = str_len( opt ); int z; for( z = 0; z < sl; z++ ) { switch( opt[z] ) { case VS_CHAR_L('i'): options |= PCRE2_CASELESS; opt_nocase = 1; break; case VS_CHAR_L('m'): options |= PCRE2_MULTILINE; break; case VS_CHAR_L('s'): options |= PCRE2_DOTALL; break; case VS_CHAR_L('x'): options |= PCRE2_EXTENDED; break; case VS_CHAR_L('f'): opt_mode = MODE_FIND; break; case VS_CHAR_L('h'): opt_mode = MODE_HEX; break; case VS_CHAR_L('r'): opt_mode = MODE_REGEXP; break; default: errstr = VS_CHAR_L("invalid option, expected are: imsxfhr"); return -1; } } return options; } int VS_REGEXP_CLASS::comp( const VS_CHAR* pattern, const VS_CHAR *opt ) { if ( re ) pcre2_code_free( re ); if ( pt ) delete [] pt; re = NULL; pt = NULL; pl = 0; int options = get_options( opt ); if( options == -1 ) return 0; if ( opt_mode == MODE_REGEXP ) { int errorcode; PCRE2_SIZE erroffset; re = pcre2_compile( (PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options, &errorcode, &erroffset, NULL ); if ( re ) { errstr = VS_CHAR_L("OK"); return 1; } else { VS_CHAR errbuf[256]; pcre2_get_error_message( errorcode, (PCRE2_UCHAR*)errbuf, sizeof(errbuf)/2 ); errstr = errbuf; errstr += VS_CHAR_L(", at pos "); errstr += (int)erroffset; return 0; } } else { pl = str_len( pattern ); pt = new VS_CHAR[pl+1]; if ( opt_mode == MODE_HEX ) pl = hex_string_to_pattern( pattern, pt ); else VS_FN_STRCPY( pt, pattern ); pt[pl] = 0; return pl; } } int VS_REGEXP_CLASS::study() { return 1; // not implemented } int VS_REGEXP_CLASS::ok() { if ( opt_mode == MODE_REGEXP ) return re != NULL; else return pt != NULL && pl > 0; } int VS_REGEXP_CLASS::m( const VS_CHAR* line ) { if ( ! ok() ) { errstr = VS_CHAR_L("no pattern compiled"); return 0; } if ( ! line ) { errstr = VS_CHAR_L("no data to search into"); return 0; } errstr = VS_CHAR_L(""); lp = line; if ( opt_mode == MODE_REGEXP ) { if( md ) pcre2_match_data_free( md ); md = pcre2_match_data_create_from_pattern( re, NULL ); unsigned int options = 0; rc = pcre2_match( re, (PCRE2_SPTR)lp, str_len(lp), 0, options, md, NULL); if ( rc < 1 ) rc = 0; return rc; } else { if ( opt_nocase ) pos = mem_quick_search_nc( pt, pl, line, str_len(lp) ); else pos = mem_quick_search( pt, pl, line, str_len(lp) ); return pos >= 0; } } int VS_REGEXP_CLASS::m( const VS_CHAR* line, const VS_CHAR* pattern, const VS_CHAR *opt ) { comp( pattern, opt ); return m( line ); } VS_STRING_CLASS VS_REGEXP_CLASS::sub( int n ) { VS_STRING_CLASS substr; if ( ! ok() ) return substr; if ( ! lp ) return substr; if ( opt_mode == MODE_REGEXP ) { if ( n < 0 || n >= rc ) return substr; PCRE2_SIZE *ovector = pcre2_get_ovector_pointer( md ); size_t s = ovector[n*2]; size_t e = ovector[n*2+1]; if ( s == PCRE2_UNSET || e == PCRE2_UNSET ) return substr; size_t l = e - s; substr.setn( lp + s, l ); } else { if ( n != 0 ) return substr; substr.setn( lp + pos, pl ); } return substr; } int VS_REGEXP_CLASS::sub_sp( int n ) { if ( opt_mode == MODE_REGEXP ) { if ( n < 0 || n >= rc ) return -1; PCRE2_SIZE *ovector = pcre2_get_ovector_pointer( md ); return ovector[n*2] == PCRE2_UNSET ? -1 : ovector[n*2]; } else { if ( n != 0 ) return -1; return pos; } } int VS_REGEXP_CLASS::sub_ep( int n ) { if ( opt_mode == MODE_REGEXP ) { if ( n < 0 || n >= rc ) return -1; PCRE2_SIZE *ovector = pcre2_get_ovector_pointer( md ); return ovector[n*2+1] == PCRE2_UNSET ? -1 : ovector[n*2+1]; } else { if ( n != 0 ) return -1; return pos+pl; } } /*************************************************************************** ** ** UTILITIES ** ****************************************************************************/ // split `source' with `regexp_str' regexp VS_ARRAY_CLASS str_split( const VS_CHAR* regexp_str, const VS_CHAR* source, int maxcount ) { VS_ARRAY_CLASS arr; VS_REGEXP_CLASS re; int z = re.comp( regexp_str ); ASSERT( z ); if ( ! z ) return arr; const VS_CHAR* ps = source; while( ps && ps[0] && re.m( ps ) ) { if ( maxcount != -1 ) { maxcount--; if ( maxcount == 0 ) break; } VS_STRING_CLASS s; s.setn( ps, re.sub_sp( 0 ) ); arr.push( s ); ps += re.sub_ep( 0 ); } if ( ps && ps[0] ) arr.push( ps ); return arr; } // split `source' with exact string `delimiter_str' VS_ARRAY_CLASS str_split_simple( const VS_CHAR* delimiter_str, const VS_CHAR* source, int maxcount ) { VS_ARRAY_CLASS arr; const VS_CHAR* ps = source; const VS_CHAR* fs; int rl = str_len( delimiter_str ); VS_STRING_CLASS s; while( (fs = VS_FN_STRSTR( ps, delimiter_str )) ) { if ( maxcount != -1 ) { maxcount--; if ( maxcount == 0 ) break; } int l = fs - ps; s.setn( ps, l ); arr.push( s ); ps = (const VS_CHAR *)(ps + l + rl); } if ( ps && ps[0] ) arr.push( ps ); return arr; } // join array data to single string with `glue' string // returns the result string or store to optional `dest' VS_STRING_CLASS str_join( VS_ARRAY_CLASS array, const VS_CHAR* glue ) { VS_STRING_CLASS str; for( int z = 0; z < array.count()-1; z++ ) { str += array.get( z ); str += glue; } str += array.get( array.count()-1 ); return str; } /***************************************************************************** ** ** find/rfind versions for regexp separators ** *****************************************************************************/ int str_find_regexp( const VS_CHAR* target, const VS_CHAR* pattern, int startpos ) { VS_REGEXP_CLASS re; if ( ! re.comp( pattern ) ) return -1; if ( startpos < 0 ) return -1; int z = 0; while( startpos-- ) { if ( target[z] == 0 ) return -1; z++; } if ( re.m( target + z ) ) return z + re.sub_sp( 0 ); else return -1; } int str_rfind_regexp( const VS_CHAR* target, const VS_CHAR* pattern ) { VS_REGEXP_CLASS re; if ( ! re.comp( pattern ) ) return -1; int z = str_len( target ); while(4) { z--; if ( re.m( target + z ) ) return z + re.sub_sp( 0 ); if ( z == 0 ) break; } return -1; } /***************************************************************************** ** ** Hex string to pattern conversion ** ** Converts hex-string to binary pattern (data) ** example: `56 6C 61 64 69' -> ... ** returns pattern length ** *****************************************************************************/ int __hex_code( VS_CHAR ch ) { ch = VS_FN_TOUPPER( ch ); if( ch >= '0' && ch <= '9' ) return ch - '0'; if( ch >= 'A' && ch <= 'F' ) return ch - 'A' + 10; return -1; } int hex_string_to_pattern( const VS_CHAR *str, VS_CHAR* pattern ) { const VS_CHAR *pc = pattern; while( *str ) { while( *str == ' ' || *str == '\t' ) str++; int hex = __hex_code( *str++ ); if( hex == -1 ) return 0; int hex2 = __hex_code( *str++ ); if( hex2 == -1 ) return 0; hex <<= 4; hex += hex2; *pattern = hex; pattern ++; } return pattern - pc; } /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/COPYING0000644000175000017500000004307014145574056013367 0ustar cadecade GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vfu-5.09/vstring/vref.cpp0000644000175000017500000000300014367022256013763 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vdef.h" #include "vref.h" /**************************************************************************** ** ** aux functions ** ****************************************************************************/ void *vs_memcpy( char *dest, const char *src, size_t n ) { return memcpy( dest, src, n * sizeof( char ) ); } void *vs_memmove( char *dest, const char *src, size_t n ) { return memmove( dest, src, n * sizeof( char ) ); } void *vs_memcpy( wchar_t *dest, const wchar_t *src, size_t n ) { return memcpy( dest, src, n * sizeof( wchar_t ) ); } void *vs_memmove( wchar_t *dest, const wchar_t *src, size_t n ) { return memmove( dest, src, n * sizeof( wchar_t ) ); } vfu-5.09/vstring/vstring.cpp0000644000175000017500000000172214367022256014526 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vref.h" #define _VSTRING_WIDE_ #include "vdef.h" #include "vstring_internal.h" #undef _VSTRING_WIDE_ #include "vdef.h" #include "vstring_internal.cpp" vfu-5.09/vstring/Makefile0000644000175000017500000000435614371545162013775 0ustar cadecade# use this to disable flto optimizations: # make NO_FLTO=1 # and this to enable verbose mode: # make V=1 AR?=gcc-ar STRIP?=strip RANLIB?=ranlib PKG_CONFIG?=pkg-config PCRE08_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-8) PCRE08_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-8) PCRE32_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-32) PCRE32_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-32) all: libvstring.a test wtest SRCS:=\ test.cpp \ vref.cpp \ vstring.cpp \ vstrlib.cpp \ vstruti.cpp \ wstring.cpp \ wstrlib.cpp \ wtest.cpp OBJS:=$(SRCS:.cpp=.o) DEPS:=$(OBJS:.o=.d) ifndef NO_FLTO CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign CXXFLAGS+=-flto=auto else CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign endif CXXFLAGS+=$(CCDEF) # some architectures do not have -mno-stackrealign HAVESREA:=$(shell if $(CXX) -mno-stackrealign -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) # old comiplers do not have -Wdate-time HAVEWDTI:=$(shell if $(CXX) -Wdate-time -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) MYCXXFLAGS:=$(CPPFLAGS) $(CXXFLAGS) $(PCRE08_CC) $(PCRE32_CC) -Wall -Wextra -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIE ifeq ("$(HAVESREA)","no") MYCXXFLAGS:=$(filter-out -mno-stackrealign,$(MYCXXFLAGS)) endif ifeq ("$(HAVEWDTI)","no") MYCXXFLAGS:=$(filter-out -Wdate-time,$(MYCXXFLAGS)) endif MYLDFLAGS:=$(MYCXXFLAGS) $(LDFLAGS) -fPIE -pie MYLIBS:=$(LIBS) $(PCRE08_LD) $(PCRE32_LD) MYLDFLAGS+=$(LDDEF) ifeq ("$(V)","1") Q:= E:=@true else Q:=@ E:=@echo endif LIBOBJ:=$(filter-out test.o,$(OBJS)) LIBOBJ:=$(filter-out wtest.o,$(LIBOBJ)) %.o: %.cpp $(E) DE $@ $(Q)$(CXX) $(MYCXXFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $< $(E) CXX $@ $(Q)$(CXX) $(MYCXXFLAGS) -c -o $@ $< libvstring.a: $(LIBOBJ) $(Q)rm -f $@ $(E) AR $@ $(Q)$(AR) rv $@ $+ $(E) RANLIB $@ $(Q)$(RANLIB) $@ test: test.o libvstring.a $(E) LD $@ $(Q)$(CXX) -o $@ $(MYLDFLAGS) $< $(MYLIBS) -L. -lvstring wtest: wtest.o libvstring.a $(E) LD $@ $(Q)$(CXX) -o $@ $(MYLDFLAGS) $< $(MYLIBS) -L. -lvstring clean: $(E) CLEAN $(Q) rm -f *.a *.o *.d test wtest re: $(Q)$(MAKE) --no-print-directory clean $(Q)$(MAKE) --no-print-directory -j -include $(DEPS) .PHONY: all clean re vfu-5.09/vstring/LICENSE0000644000175000017500000004307014367022256013335 0ustar cadecade GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vfu-5.09/vstring/wtest.cpp0000644000175000017500000004266414444676576014232 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include #include #include "vstring.h" #include "wstring.h" #include "vstrlib.h" #include "wstrlib.h" typedef VString VPath; void test1() { WString str = L"Hello"; str += L" World"; // str is `Hello World' now str_reverse( str ); // str is `dlroW olleH' now ASSERT( str == L"dlroW olleH" ); str_low( str ); // lower case ASSERT( str == L"dlrow olleh" ); wprintf( L"************************ test 1 mid: %ls\n", str.data() ); // this should print `hello world' WArray va = str_split( L" +", str ); // array contains `dlroW' at pos 0 and `olleH' at 1 va.print(); va.reverse(); // array reversed: `dlroW' at pos 1 and `olleH' at 0 int z; for( z = 0; z < va.count(); z++ ) { str_reverse( va[z] ); // reverses each string element } str = str_join( va, L" " ); // joins into temporary string wprintf( L"************************ test 1 result is: %ls\n", str.data() ); // this should print `hello world' ASSERT( str == L"hello world" ); ASSERT( str_rfind( str, L'l' ) == 9 ); ASSERT( str_rfind( str, L'l', -3 ) == 3 ); ASSERT( str_rfind( str, L'o', 7 ) == 7 ); ASSERT( str_rfind( str, L'o', 6 ) == 4 ); str = L"hello mello tello start!"; ASSERT( str_rfind( str, L"llo" ) == 14 ); ASSERT( str_rfind( str, L"llo", -12 ) == 8 ); ASSERT( str_rfind( str, L"llo", 7 ) == 2 ); str.compact( 1 ); str.fix(); str_add_ch( str, '!' ); str_add_ch( str, '?' ); str_add_ch( str, '*' ); } void test2() { WArray va; va.push( L"hello" ); // pos 0 va.push( L"world" ); // pos 1 va.ins( 1, L"your" ); // pos 1 shifted va[1] = L"my"; // replaces `your' va[3] = L"!"; // set outside the size, array is extended WString str = va.pop(); // pops last element, str is now `!' str = str_join( va, L"-" ); // joins to given string str_tr( str, L"-", L" " ); // replaces dashes with spaces str_replace( str, L" my ", L" " ); // removes ` my ' wprintf( L"************************ test 2 result is: %ls\n", str.data() ); // this should print `hello world' ASSERT( str == L"hello world" ); WString res1; str_copy( res1, str, 3, 5 ); ASSERT( res1 == L"lo wo" ); WString res2; str_copy( res2, str, 8, 9 ); ASSERT( res2 == L"rld" ); } void test3() { WTrie tr; // hash-like WArray va; // inserting keys and values tr[ L"tralala" ] = L"data1"; tr[ L"opala" ] = L"data2"; tr[ L"keynext" ] = L"data3"; // inserting elements into the array va.push( L"this" ); va.push( L"just" ); va.push( L"test" ); va.push( L"simple" ); // adding string to the first element of the array va[1] += L" x2"; // the array is converted to trie (hash) and merged into `tr' tr += va; // same as: tr.merge( &va ); ASSERT( tr[ L"this" ] == L"just x2" ); // clear the array--remove all elements va.undef(); // take keys from `tr' as array and store them into va, returns count // i.e. i = tr.count(); int i; va = tr.keys(); wprintf( L"keys count = %d\n", va.count() ); ASSERT( va.count() == 5 ); // printing the array and trie data for( i = 0; i < va.count(); i++ ) { wprintf( L"%d -> %ls (%ls)\n", i, va[i].data(), tr[ va[i] ].data() ); } WArray v1; wprintf( L"--------------------\n" ); v1 = tr; // same as: v1.undef; v1.push( &tr ); v1.print(); // print array data WRegexp re( L"a([0-9]+)" ); // compiling new regexp wprintf( L"regexp error: %ls\n", re.error_str() ); if( re.m( L"tralala85.zz" ) ) // match against regexp { wprintf( L"sub 0 = %ls\n", re[0].data() ); // re[0] returns `a85' wprintf( L"sub 1 = %ls\n", re[1].data() ); // re[1] returns `85' ASSERT( re[0] == L"a85" ); ASSERT( re[1] == L"85" ); } WString vs; if( re.m( L"tralala85.", L"a(la)+" ) ) // match against regexp { wprintf( L"sub 0 = %ls\n", re[0].data() ); // `alala' wprintf( L"sub 1 = %ls\n", re[1].data() ); // `la' ASSERT( re[0] == L"alala" ); ASSERT( re[1] == L"la" ); } wprintf( L"--------------------\n" ); v1 = str_split( L",", L"*.tralala,opala and another one" ); // splits on spaces v1.print(); WString js1 = str_join( v1, L"---" ); wprintf( L"joined: %ls\n", (const wchar_t*)js1 ); // join the same data back ASSERT( js1 == L"*.tralala---opala and another one" ); WString m1 = v1[0]; WString m2 = v1[1]; wprintf( L"1[%ls] 2[%ls]\n", m1.data(), m2.data() ); wprintf( L"--------------------\n" ); v1 = str_split( L" +", L"tralala opala and another one", 3 ); // splits data on spaces up to 3 elements v1.print(); ASSERT( v1[2] == L"and another one" ); //exit(1); wprintf( L"--------------------\n" ); v1[1] = L"hack this one here"; // set (overwrite) element 1 str_sleft( v1[2], 11 ); // reset element 2 to the left 11 chars only v1[0] = 12345; // convert integer into string v1.print(); wprintf( L"--------------------\n" ); WArray aa[3]; // array of arrays aa[0] = str_split( L" ", L"this is just a simple test" ); aa[1] = str_split( L" ", L"never ending story" ); aa[2] = str_split( L" ", L"star-wars rulez" ); aa[0][1] = L"was"; // first array, second element, replaces `is' with `was' aa[2][0] = L"slackware"; // third array, first element, `star-wars' is now `slackware' // expands the array from 3 to 11 elements aa[1][10] = L"king of the hill"; for( i = 0; i < 3; i++ ) { wprintf( L"---\n"); aa[i].print(); } wprintf( L"---box test-----------------------------\n" ); i = 20; while( i-- ) { v1.push( L"this" ); v1.push( L"just" ); v1.push( L"test" ); v1.push( L"simple" ); } v1.print(); WArray vv = v1; // this makes vv data aliased to the data of v1 vv.print(); // actually print the v1's data which is shared right now vv.set( 0, L"---" ); // vv makes own copy of the array data vv.print(); // vv's data is no more aliased to v1's WRegexp re_see( L"^\\s*see\\s*=\\s*([^, \011]*)\\s*,(.*)$", L"i" ); if( re_see.m( L"see=*.tgz,tralala" ) ) { WString str; str = str + re_see[1] + L":" + re_see[2]; wprintf( L"WRegexp[1+2]=[%ls]\n", str.data() ); ASSERT( str == L"*.tgz:tralala" ); } wprintf( L"************************ test 3 ends here\n" ); } void test4() { // this is regression test, please ignore it... int i; int ii; WArray va; ii = 20; i = ii; while( i-- ) { va = str_split( L",", L"this is, just a simple. but fixed, nonsense test, voila :)" ); ASSERT( va.count() == 4 ); printf( "%d%% va count = %d\n", (100*i)/ii, va.count() ); } WString set; WString cat; WString setn; WString catn; WString sete; WString setp; i = 2000; while( i-- ) { set.set( L"this is, just a simple. but fixed, nonsense test, voila :)" ); cat.cat( L"this is, just a simple. but fixed, nonsense test, voila :)" ); setn.setn( L"this is, just a simple. but fixed, nonsense test, voila :)", 20 ); catn.catn( L"this is, just a simple. but fixed, nonsense test, voila :)", 20 ); sete = L"this is, just a simple. but fixed, nonsense test, voila :)"; setp += L"this is, just a simple. but fixed, nonsense test, voila :)"; } printf( "set = %zd\n", str_len( set ) ); printf( "cat = %zd\n", str_len( cat ) ); printf( "setn = %zd\n", str_len( setn ) ); printf( "catn = %zd\n", str_len( catn ) ); printf( "sete = %zd\n", str_len( sete ) ); printf( "setp = %zd\n", str_len( setp ) ); printf( "--------------------\n" ); i = 2000; while( i-- ) { set = L"this is, just a simple. but fixed, nonsense test, voila :)"; setn = set; str_del( set, 20, 10 ); str_ins( set, 30, L"***opa***" ); str_del( set, 40, 100 ); str_replace( setn, L"i", L"[I]" ); ASSERT( set == L"this is, just a simpxed, nonse***opa***n" ); ASSERT( str_len(set) == str_len(L"this is, just a simpxed, nonse***opa***n") ); ASSERT( setn == L"th[I]s [I]s, just a s[I]mple. but f[I]xed, nonsense test, vo[I]la :)" ); ASSERT( str_len(setn) == str_len(L"th[I]s [I]s, just a s[I]mple. but f[I]xed, nonsense test, vo[I]la :)") ); } printf( "set = %ls\n", set.data() ); printf( "setn = %ls\n", setn.data() ); printf( "---array sort-------\n" ); va.undef(); va = str_split( L"[, \t]+", L"this is, just a simple. but fixed, nonsense test, voila :)" ); va.sort(); va.print(); printf( "--------------------\n" ); va.sort( 1 ); va.print(); printf( "--------------------\n" ); } /* void test5() { WTrie tr; // hash-like WArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr[ "key4" ] = "data4"; tr[ "key5" ] = "data5"; tr.print(); exit(1); tr.reverse(); tr.print(); tr.reverse(); tr.print(); printf( "************************ test 5 ends here\n" ); } void test6() { WRegexp re; WArray va; re.comp( "^([^!]+)!(.+)=apquxz(.+)$" ); int i = re.m( "abc!pqr=apquxz.ixr.zzz.ac.uk" ); i--; while( i >= 0 ) { va.push( re[i] ); i--; } va.print(); va.undef(); va += "/this/is/samle/file.tail"; va += "/file.tail"; va += "/this/is/./samle/file.tail/"; va += "/this/..../is/../samle/.file.tail"; va += "/.file.tail"; va += "/"; const char* ps; va.reset(); while( ( ps = va.next() ) ) { printf( "------------------------------------\n" ); printf( "file is: %ls\n", ps ); printf( "path is: %ls\n", (const char*)str_file_path( ps ) ); printf( "name is: %ls\n", (const char*)str_file_name( ps ) ); printf( "ext is: %ls\n", (const char*)str_file_ext( ps ) ); printf( "n+ex is: %ls\n", (const char*)str_file_name_ext( ps ) ); printf( "reduced path is: %ls\n", (const char*)str_reduce_path( ps ) ); printf( "dot reduce sample is: %ls\n", (const char*)str_dot_reduce( ps, 10 ) ); } va.fsave( "/tmp/a.aaa" ); va.fload( "/tmp/a.aaa" ); va.print(); } void test7() { WTrie tr; // hash-like WTrie tr2; // hash-like WArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr.print(); printf( "---------------------------------1---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------2---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------3---\n" ); tr2 = str_split( " ", "this is simple one way test" ); tr2.print(); printf( "---------------------------------4---\n" ); tr2 += tr; tr2.print(); printf( "---------------------------------5---\n" ); va = tr2; va.print(); printf( "---------------------------------6---\n" ); } void test8() { WString v1; WString v2; v1 = "this is simple test "; v1 *= 1024; printf( "v1 len: %d\n", str_len( v1 ) ); v2.compact( 1 ); // makes v2 compact, i.e. it will get as much memory as it // needs. otherwise it will get fixed amount of blocks v2 = v1; // data is shared between v1 and v2. any change to v1 or v2 will // detach this data and both will get own copy v2[0] = ' '; // this will create own data for v2 str_tr( v2, "ti", "TI" ); // capitalize T and I v2 = ""; // this will free all data allocated by v2 printf( "copy 7,6: [%ls]", (const char*)str_copy( v2, v1, 8, 6 ) ); printf( "copy 10: [%ls]", (const char*)str_copy( v2, v1, -10 ) ); printf( "************************ test 5 ends here\n" ); } void test9() { WArray va; WTrie tr; printf( "---9---------------------------------------------------\n" ); va.push( "one" ); va.push( "two" ); va.push( "tri" ); va.push( "pet" ); va.print(); tr = va; tr.print(); printf( "\n ----\n" ); va.push( tr ); va.print(); WArray va2; va2.push( "1" ); va2.push( "2" ); va2.push( "3" ); va2.push( "4" ); va2.unshift( "0" ); va2.unshift( va ); va2.push( va ); va2.print(); } */ void print_vpath( VPath& vp ) { wprintf( L"pp = %ls\n", WString( vp.data() ).data() ); } void test10() { WArray va; WTrie tr; wint_t x = 5; wchar_t name[] = L"GEEKS"; wprintf( L"x = %d \n", x); wprintf( L"HELLO %ls \n", name); fwide( stdout, 0 ); printf( "--- WIDE CHAR TESTS -----------------------------------\n" ); fwide( stdout, 1 ); wchar_t t[256]; wcscpy( t, L"Това е проста проба щеш не щеш" ); wprintf( L"%ls\n", L"Това е проста проба щеш не щеш" ); printf( "--- test 10 ends --------------------------------------\n" ); WString ws( "щото това е конвертиране" ); wprintf( L"%ls\n", ws.data() ); ws = "и това е пак също ковертиране"; wprintf( L"%ls\n", ws.data() ); VString vv = ws; WString ww = vv; ww += L" x2"; wprintf( L"%ls\n", ww.data() ); VPath pp = ws; pp = str_dot_reduce( pp.data(), 11 ); vv = 123; print_vpath( vv ); str_dot_reduce( ww, 16 ); } void test11() { ASSERT( sfn_match( "vf*", "vfudir" ) == 0 ); ASSERT( sfn_match( "vf*r", "vfudir" ) == 0 ); ASSERT( sfn_match( "vf*t", "vfudir" ) != 0 ); ASSERT( sfn_match( "vf[you]*", "vfudir" ) == 0 ); ASSERT( sfn_match( "vf\\*d[g-k]?z", "vf*dirz" ) == 0 ); ASSERT( sfn_match( "v*[c-e]*", "vfudirtest.txt" ) == 0 ); ASSERT( sfn_match( "v*xt**", "vfudirtest.txt" ) == 0 ); ASSERT( sfn_match( "v*xt**?", "vfudirtest.txt" ) != 0 ); ASSERT( sfn_match( "vf*i*r", "vfudir" ) == 0 ); ASSERT( sfn_match( "vf*x*r", "vfudir" ) != 0 ); ASSERT( sfn_match( "vf*\\?*r", "vfutest?xdir" ) == 0 ); ASSERT( sfn_match( "??*", "vfutest" ) == 0 ); ASSERT( sfn_match( "??*", "vf" ) == 0 ); ASSERT( sfn_match( "??*", "v" ) != 0 ); ASSERT( sfn_match( "*vfu*?", "vfuz" ) == 0 ); ASSERT( sfn_match( "vf[^you]*", "vfudir" ) != 0 ); ASSERT( sfn_match( "vf\\u*", "vfudir", SFN_NOESCAPE ) != 0 ); ASSERT( sfn_match( "vf\\u*", "vf\\udir", SFN_NOESCAPE ) == 0 ); ASSERT( sfn_match( "vf\\U*", "Vf\\udir", SFN_NOESCAPE ) != 0 ); ASSERT( sfn_match( "vf\\U*", "Vf\\udir", SFN_NOESCAPE | SFN_CASEFOLD ) == 0 ); ASSERT( sfn_match( "vF\\*d[g-k]?z", "Vf*dIrz", SFN_CASEFOLD ) == 0 ); ASSERT( sfn_match( L"vf\\*d[g-k]?z", L"vf*dirz" ) == 0 ); ASSERT( sfn_match( L"vf*\\?*r", L"vfutest?xdir" ) == 0 ); ASSERT( sfn_match( L"vF\\*d[g-k]?z", L"Vf*dIrz", SFN_CASEFOLD ) == 0 ); ASSERT( sfn_match( "*ing*", "vstring.txt" ) == 0 ); ASSERT( sfn_match( "*.deb", "vfu.debug" ) != 0 ); ASSERT( sfn_match( "*.tar.gz", "vfu-5.02.tar.gz" ) == 0 ); ASSERT( sfn_match( "*.tar*z", "vfu-5.02.tar.xz" ) == 0 ); ASSERT( sfn_match( "*.tar*z", "vfu-5.02_tar.xz" ) != 0 ); ASSERT( sfn_match( "*.tar*z", "vfu-5.tar.tar.xz" ) == 0 ); ASSERT( sfn_match( "*.tar*m*z", "vfu-5.tar.tar.xz" ) != 0 ); ASSERT( sfn_match( "*tar*", "vfu tar tar.xz" ) == 0 ); ASSERT( sfn_match( "vf*u*xz", "vfu tar tar.xz" ) == 0 ); ASSERT( sfn_match( "vf*[u]*xz", "vfu tar tar.xz" ) == 0 ); ASSERT( sfn_match( "vf*[u*xz", "vfu tar tar.xz" ) != 0 ); } int main( void ) { setlocale( LC_ALL, "" ); #if 0 char t[256] = "123456----------------------------------------9999999999999"; char T[256] = "123456----------------------------------------9999999999999"; str_trim_left( t, 3 ); printf( "%ls\n", t ); for( long z; z < 300000000; z++ ) { //str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str //str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str //memmove( T, t, 222 ); //memcpy( T, t, 222 ); //str_copy( T, t, 0, 222 ); // check for overlapping borders, begin of str } #endif wchar_t t[92] = L"this is simple test"; wchar_t r[92] = L"1111111111111111111"; str_word( t, L" ", r ); ASSERT( wcscmp( t, L"is simple test" ) == 0 ); ASSERT( wcscmp( r, L"this" ) == 0 ); str_set( t, L" opa" ); str_cut_left( t, L" " ); ASSERT( wcscmp( t, L"opa" ) == 0 ); str_set( t, L"opa " ); str_cut_right( t, L" " ); ASSERT( wcscmp( t, L"opa" ) == 0 ); str_set( t, L"this is good" ); str_ins( t, 8, L"not " ); ASSERT( wcscmp( t, L"this is not good" ) == 0 ); str_del( t, 8, 4 ); ASSERT( wcscmp( t, L"this is good" ) == 0 ); str_set( t, L"more" ); str_mul( t, 6 ); ASSERT( wcscmp( t, L"moremoremoremoremoremore" ) == 0 ); WString s = L"more"; str_mul( s, 6 ); ASSERT( s == L"moremoremoremoremoremore" ); ASSERT( str_len( s ) == str_len((const wchar_t*)s) ); str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str str_set( t, L"despicable me" ); str_word( t, L" ", r ); ASSERT( wcscmp( r, L"despicable" ) == 0 ); str_word( t, L" ", r ); ASSERT( wcscmp( r, L"me" ) == 0 ); ASSERT( t[0] == 0 ); #if 1 test1(); test2(); test3(); test4(); #if 0 test5(); test6(); test7(); test8(); test9(); #endif test10(); test11(); #endif return 0; } vfu-5.09/vstring/wstring.h0000644000175000017500000000215614367022256014176 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _WSTRING_H_ #define _WSTRING_H_ #define _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstring_internal.h" #endif /* TOP */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vstrlib_internal.h0000644000175000017500000002725014367022256016064 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vdef.h" #include /*************************************************************************** ** ** GLOBALS ** ****************************************************************************/ #define VCHARSET_BLOCK_SIZE 32 /* max pattern length for file_find_*() and ... */ #define MAX_PATTERN 2048 /* max file_grep() text line input length... :| */ #define MAX_GREP_LINE 4096 /**************************************************************************** ** ** VString aditional functions ** ****************************************************************************/ #ifdef _VSTRING_WIDE_ const VS_CHAR* time2wstr( const time_t tim ); #else const VS_CHAR* time2str( const time_t tim ); #endif time_t str2time( const VS_CHAR* timstr ); /***************************************************************************** ** ** sfn_match function provides simplified pattern matching for the common ** shell expansion. supported rules are: ** * -- any nuber of characters ** ? -- any single character ** [...] -- single position of any of the enclosed chars ** (supports ranges in the form [A-Z], [0-7], etc. ** [!...] -- single range of any different chars than enclosed ** [^...] -- the same ** \x -- will escape and treat 'x' as non-special char (like *,?,etc.) ** ** supported flags, should be OR'd ( flag1 | flag2 ... ) ** SFN_NOESCAPE -- disables escaping with '\' ** SFN_CASEFOLD -- case-insesitive matching ** ** returns 0 for matched pattern or other for error *****************************************************************************/ #ifndef __SFN_FLAGS__ #define __SFN_FLAGS__ #define SFN_NOESCAPE (1 << 1) #define SFN_CASEFOLD (1 << 4) #endif int sfn_match( const VS_CHAR* pattern, const VS_CHAR* string, int flags = 0 ); /***************************************************************************** ** ** Next mem* search functions are used to find pattern into memory block ** p is pattern, ps is pattern size, d is data searched and ds is its size ** return found pttern position or -1 for not found ** *****************************************************************************/ int mem_kmp_search ( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ); int mem_quick_search( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ); int mem_sum_search ( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ); /* no-case versions */ int mem_quick_search_nc( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ); /***************************************************************************** ** ** Function which return position of pattern into a file ** this uses mem* functions above or defaults to mem_quick_search ** *****************************************************************************/ long file_pattern_search( const VS_CHAR *p, int ps, FILE* f, const VS_CHAR* opt = VS_CHAR_L(""), int (*mem_search)( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) = NULL ); long file_pattern_search( const VS_CHAR *p, int ps, const char* fn, const VS_CHAR* opt = VS_CHAR_L(""), int (*mem_search)( const VS_CHAR *p, int ps, const VS_CHAR *d, int ds ) = NULL ); /***************************************************************************** ** ** This function reads lines from a text file and runs regexp on it. ** file_grep_max_line defines the max line length read (1024) ** file_grep_lines_read reports how many lines are read in during the ** last file_grep() call ** re_string is regexp string, not arbitrary (binary) pattern ** spos defines what file start offset should be accepted ** *****************************************************************************/ long file_grep( const VS_CHAR *re_string, const char* file_name, int nocase, off_t spos = -1 ); long file_grep( const VS_CHAR *re_string, FILE* f, int nocase, off_t spos = -1 ); /***************************************************************************** ** ** Search interface functions ** ** options are: ** ** i -- ignore case ** r -- regular expression (grep) ** h -- hex pattern search ** *****************************************************************************/ int mem_string_search ( const VS_CHAR *p, const VS_CHAR* d, const VS_CHAR* opt ); long file_string_search( const VS_CHAR *p, const char* fn, const VS_CHAR* opt ); long file_string_search( const VS_CHAR *p, FILE *f, const VS_CHAR* opt ); /*************************************************************************** ** ** VCHARSET ** ****************************************************************************/ class VS_CHARSET_CLASS { VS_CHAR* _data; int _size; // size (in bytes) void resize( int new_size ); public: VS_CHARSET_CLASS(); ~VS_CHARSET_CLASS(); void push( VS_CHAR n, int val = 1 ); void undef( VS_CHAR n ); void undef(); int in( VS_CHAR n ); /* push int get ( int pn ); void set_range1( int start, int end ); void set_range0( int start, int end ); void set_str1( const char* str ); void set_str0( const char* str ); int in( const char *str ); // return 1 if all str's chars are in the set int in( int pn ) { if ( pn < 0 || pn >= size ) return 0; else return get( pn ); }; void reverse() { for(int z = 0; z < datasize; z++) data[z] = ~data[z]; }; void set( int pn, int val ) { if ( val ) set1( pn ); else set0( pn ); }; void set_all1() { if ( data ) memset( data, 0xff, datasize ); }; void set_all0() { if ( data ) memset( data, 0x00, datasize ); }; const int operator [] ( int pn ) { ASSERT( pn >= 0 && pn < size ); return get( pn ); }; int resize( int p_size ); VCharSet& operator = ( const VCharSet &b1 ); VCharSet& operator &= ( const VCharSet &b1 ); VCharSet& operator |= ( const VCharSet &b1 ); VCharSet operator ~ (); friend VCharSet operator & ( const VCharSet &b1, const VCharSet &b2 ); friend VCharSet operator | ( const VCharSet &b1, const VCharSet &b2 ); */ }; /*************************************************************************** ** ** VREGEXP ** ****************************************************************************/ /* ** options are: ** i -- case insensitive ** m -- multiline matches ** s -- single line (`.' matches and NEWLINE's) ** x -- extended (ifnores whitespace and comments) ** ** f -- plain find (substring) using quick search ** h -- hex search, input pattern is converted from hex string ** ** for more docs see perlre(1) and pcre library docs ** ** ** WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! ** extracting of the captured substring is only possible while subject input ** line which is used as matching target is intact! which means that if you ** change this line between match and substring extraction this will lead to ** segmentation fault! ** */ /* number of subpatterns which can be catched by VRegexp::m() */ #ifndef VREGEXP_MAX_SUBS #define VREGEXP_MAX_SUBS 32 #endif class VS_REGEXP_CLASS { /* search modes */ enum SearchMode { MODE_REGEXP = 0, MODE_FIND, MODE_HEX }; /* common data */ SearchMode opt_mode; int opt_nocase; // 1 if caseless search needed /* regexp data */ pcre2_code *re; // regexp object, allocated here, for MODE_REGEXP pcre2_match_data *md; // match data int rc; // result after successful pcre_exec() const VS_CHAR *lp; // last subject data to search in, external, just keep ptr /* no-regexp/hex search pattern */ VS_CHAR* pt; // pattern for MODE_FIND and MODE_HEX int pl; // pattern length int pos; // last match found pos /* common data */ //VString substr; VS_STRING_CLASS errstr; int get_options( const VS_CHAR* opt ); public: VS_REGEXP_CLASS(); VS_REGEXP_CLASS( const VS_CHAR* pattern, const VS_CHAR *opt = NULL ); // compiles new regexp ~VS_REGEXP_CLASS(); int comp( const VS_CHAR* pattern, const VS_CHAR *opt = NULL ); // compile re, return > 0 for success // options are: // i -- ignore case // m -- multiline match // s -- match dot against all chars (\n) // x -- extended, ignore whitespace // f -- plain string search (no regexp used) // h -- hex search, converts string ( `56 6C 61 64 69' ) to search pattern // r -- regexp match (default, no need to specify) // last options found are mandatory: "fhr" options sets regexp match int study(); // optimizing regexp for (big-size) multiple matches int ok(); // return 1 if regexp is compiled ok, 0 if not int m( const VS_CHAR* line ); // execute re against line, return 1 for match int m( const VS_CHAR* line, const VS_CHAR* pattern, const VS_CHAR *opt = NULL ); // same as exec, but compiles first VS_STRING_CLASS sub( int n ); // return n-th substring match int sub_sp( int n ); // return n-th substring start position int sub_ep( int n ); // return n-th substring end position VS_STRING_CLASS operator []( int n ) // same as sub() { return sub( n ); } const VS_CHAR* error_str() { return errstr.data(); }; }; /*************************************************************************** ** ** UTILITIES ** ****************************************************************************/ // split `source' with `regexp_str' regexp VS_ARRAY_CLASS str_split( const VS_CHAR* regexp_str, const VS_CHAR* source, int maxcount = -1 ); // split `source' with exact string `delimiter_str' VS_ARRAY_CLASS str_split_simple( const VS_CHAR* delimiter_str, const VS_CHAR* source, int maxcount = -1 ); // join array data to single string with `glue' string // returns the result string or store to optional `dest' VS_STRING_CLASS str_join( VS_ARRAY_CLASS array, const VS_CHAR* glue = VS_CHAR_L("") ); /***************************************************************************** ** ** find/rfind versions for regexp separators ** *****************************************************************************/ int str_find_regexp( const VS_CHAR* target, const VS_CHAR* pattern, int startpos = 0 ); // warning: str_rfind_regexp() is slow! it can execute pattern matching to `n' // times where n is the target string length... int str_rfind_regexp( const VS_CHAR* target, const VS_CHAR* pattern ); /***************************************************************************** ** ** Hex string to pattern conversion ** ** Converts hex-string to binary pattern (data) ** example: `56 6C 61 64 69' -> ... ** returns pattern length ** *****************************************************************************/ int hex_string_to_pattern( const VS_CHAR *str, VS_CHAR* pattern ); /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vstrlib.h0000644000175000017500000000215614367022256014166 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _VSTRLIB_H_ #define _VSTRLIB_H_ #undef _VSTRING_WIDE_ #include "vdef.h" #include "vref.h" #include "vstrlib_internal.h" #endif /* *TOP* */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vstring_internal.cpp0000644000175000017500000015125114444676576016445 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #include "vstring_internal.h" ssize_t str_len( const VS_CHAR *s ) { return VS_FN_STRLEN( s ); } /**************************************************************************** ** ** VSTRING BOX ** ****************************************************************************/ VS_STRING_BOX* VS_STRING_BOX::clone() { VS_STRING_BOX* box = new VS_STRING_BOX(); box->resize_buf( size ); box->sl = sl; box->compact = compact; vs_memcpy( box->s, s, size ); //TODO: FIXME: just sl? return box; } void VS_STRING_BOX::resize_buf( int new_size ) { /* FIXME: this breaks a lot of things: ==, strcmp, const VS_CHAR*, ... if ( new_size == 0 ) { sl = 0; if ( s ) free( s ); s = NULL; size = 0; return; } */ new_size++; /* for the trailing 0 */ if ( ! compact ) { new_size = new_size / block_size + ( new_size % block_size != 0 ); new_size *= block_size; } if( s == NULL ) { /* first time alloc */ s = (VS_CHAR*)malloc( new_size * sizeof( VS_CHAR ) ); ASSERT( s ); s[ 0 ] = 0; size = new_size; sl = 0; } else if ( size != new_size ) { /* expand/shrink */ s = (VS_CHAR*)realloc( s, new_size * sizeof( VS_CHAR ) ); size = new_size; s[ size - 1 ] = 0; if ( sl > size - 1 ) sl = size - 1; } } void VS_STRING_BOX::set_block_size( int new_block_size ) { block_size = new_block_size < 1 ? VSTRING_DEFAULT_BLOCK_SIZE : new_block_size; } VS_STRING_BOX::~VS_STRING_BOX() { undef(); if ( s ) free( s ); /* FILE* fo = fopen( "/tmp/vstrboxstats.txt", "a" ); fprintf( fo, "cbs=%d sbs=%d sbc=%d sbg=%f\n", cbs, sbs, sbc, sbg ); fclose( fo ); */ } /**************************************************************************** ** ** VSTRING ** ****************************************************************************/ VS_STRING_CLASS::VS_STRING_CLASS( VS_STRING_CLASS_R rs ) { box = new VS_STRING_BOX(); set( rs.data() ); } void VS_STRING_CLASS::detach() { if ( box->refs() == 1 ) return; VS_STRING_BOX *new_box = box->clone(); box->unref(); box = new_box; } void VS_STRING_CLASS::i( const int n ) { VS_CHAR tmp[64]; VS_FN_SPRINTF( tmp, LENOF_VS_CHAR(tmp), VS_CHAR_L("%d"), n ); set( tmp ); } void VS_STRING_CLASS::l( const long n ) { VS_CHAR tmp[64]; VS_FN_SPRINTF( tmp, LENOF_VS_CHAR(tmp), VS_CHAR_L("%ld"), n ); set( tmp ); } void VS_STRING_CLASS::ll( const long long n ) { VS_CHAR tmp[64]; VS_FN_SPRINTF( tmp, LENOF_VS_CHAR(tmp), VS_CHAR_L("%lld"), n ); set( tmp ); } void VS_STRING_CLASS::f( const double d ) { VS_CHAR tmp[64]; VS_FN_SPRINTF( tmp, LENOF_VS_CHAR(tmp), VS_CHAR_L("%.10f"), d ); int z = VS_FN_STRLEN( tmp ); while( tmp[z-1] == VS_CHAR_L('0') ) z--; if ( tmp[z-1] == VS_CHAR_L('.') ) z--; tmp[z] = 0; set( tmp ); } void VS_STRING_CLASS::fi( const double d ) // sets double as int (w/o frac) { VS_CHAR tmp[64]; VS_FN_SPRINTF( tmp, LENOF_VS_CHAR(tmp), VS_CHAR_L("%.0f"), d ); set( tmp ); } void VS_STRING_CLASS::set( const VS_CHAR* ps ) { if (ps == NULL || ps[0] == 0) { resize( 0 ); ASSERT( box->s ); box->sl = 0; box->s[ 0 ] = 0; } else { int sl = str_len( ps ); resize( sl ); vs_memcpy( box->s, ps, sl ); box->sl = sl; box->s[ sl ] = 0; } } void VS_STRING_CLASS::cat( const VS_CHAR* ps ) { if (ps == NULL) return; if (ps[0] == 0) return; int psl = str_len( ps ); resize( box->sl + psl ); vs_memcpy( box->s + box->sl, ps, psl ); box->s[ box->sl + psl ] = 0; box->sl += psl; } void VS_STRING_CLASS::setn( const VS_CHAR* ps, int len ) { if ( !ps || len < 1 ) { resize( 0 ); box->sl = 0; box->s[ 0 ] = 0; return; } int z = str_len( ps ); if ( len < z ) z = len; resize( z ); box->sl = z; vs_memcpy( box->s, ps, z ); box->s[z] = 0; } void VS_STRING_CLASS::catn( const VS_CHAR* ps, int len ) { if ( !ps || len < 1 ) return; int z = str_len( ps ); if ( len < z ) z = len; resize( box->sl + z ); vs_memcpy( box->s + box->sl, ps, z ); box->sl += z; box->s[ box->sl ] = 0; } const VS_STRING_CLASS& VS_STRING_CLASS::operator = ( const VS_STRING_CLASS_R& rs ) { set( rs.data() ); return *this; } const VS_STRING_CLASS& VS_STRING_CLASS::operator = ( const VS_CHAR_R* prs ) { set( prs ); return *this; } /**************************************************************************** ** ** VS_STRING_CLASS Functions (for class VS_STRING_CLASS) ** ****************************************************************************/ VS_STRING_CLASS &str_mul( VS_STRING_CLASS &target, int n ) // multiplies the VS_STRING_CLASS n times, i.e. "1"*5 = "11111" { target.resize( target.box->sl * n ); str_mul( target.box->s, n ); target.box->sl *= n; return target; } VS_STRING_CLASS &str_del( VS_STRING_CLASS &target, int pos, int len ) // deletes `len' VS_CHARs starting from `pos' { if ( pos > target.box->sl || pos < 0 ) return target; target.detach(); str_del( target.box->s, pos, len ); if ( pos + len < target.box->sl ) target.box->sl -= len; else target.box->sl = pos; return target; } VS_STRING_CLASS &str_ins( VS_STRING_CLASS &target, int pos, const VS_CHAR* s ) // inserts `s' in position `pos' { if ( pos > target.box->sl || pos < 0 ) return target; int ssl = str_len(s); target.resize( target.box->sl + ssl ); str_ins( target.box->s, pos, s ); target.box->sl += ssl; return target; } VS_STRING_CLASS &str_ins_ch( VS_STRING_CLASS &target, int pos, VS_CHAR ch ) // inserts `ch' in position `pos' { if ( pos > target.box->sl || pos < 0 ) return target; target.resize( target.box->sl + 1 ); str_ins_ch( target.box->s, pos, ch ); target.box->sl++; return target; } VS_STRING_CLASS &str_replace( VS_STRING_CLASS &target, const VS_CHAR* out, const VS_CHAR* in ) // replace `out' w. `in' { int outl = str_len( out ); int inl = str_len( in ); int z = str_find( target, out ); while(z != -1) { str_del( target, z, outl ); str_ins( target, z, in ); z = str_find( target, out, z + inl ); } ASSERT(target.check()); return target; } VS_STRING_CLASS &str_copy( VS_STRING_CLASS &target, const VS_CHAR* source, int pos, int len ) // returns `len' VS_CHARs from `pos' { if( __str_copy_calc_offsets( source, pos, len ) ) { target.undef(); return target; } target.resize( len ); vs_memmove( target.box->s, source + pos, len ); target.box->s[ len ] = 0; target.box->sl = len; return target; } VS_STRING_CLASS &str_left( VS_STRING_CLASS &target, const VS_CHAR* source, int len ) // returns `len' VS_CHARs from the left { return str_copy( target, source, 0, len ); } VS_STRING_CLASS &str_right( VS_STRING_CLASS &target, const VS_CHAR* source, int len ) // returns `len' VS_CHARs from the right { return str_copy( target, source, str_len( source ) - len, len ); } VS_STRING_CLASS &str_sleft( VS_STRING_CLASS &target, int len ) // SelfLeft -- just as 'Left' but works on `this' { if ( len < target.box->sl ) { target.detach(); target.box->s[len] = 0; target.fix(); } return target; } VS_STRING_CLASS &str_sright( VS_STRING_CLASS &target, int len ) // SelfRight -- just as 'Right' but works on `this' { target.detach(); str_sright( target.box->s, len ); target.fix(); return target; } VS_STRING_CLASS &str_trim_left( VS_STRING_CLASS &target, int len ) // trims `len' VS_CHARs from the beginning (left) { target.detach(); str_trim_left( target.box->s, len ); target.fix(); return target; } VS_STRING_CLASS &str_trim_right( VS_STRING_CLASS &target, int len ) // trim `len' VS_CHARs from the end (right) { target.detach(); str_trim_right( target.box->s, len ); target.fix(); return target; } VS_STRING_CLASS &str_cut_left( VS_STRING_CLASS &target, const VS_CHAR* charlist ) // remove all VS_CHARs `charlist' from the beginning (i.e. from the left) { target.detach(); str_cut_left( target.box->s, charlist ); target.fix(); return target; } VS_STRING_CLASS &str_cut_right( VS_STRING_CLASS &target, const VS_CHAR* charlist ) // remove all VS_CHARs `charlist' from the end (i.e. from the right) { target.detach(); str_cut_right( target.box->s, charlist ); target.fix(); return target; } VS_STRING_CLASS &str_cut( VS_STRING_CLASS &target, const VS_CHAR* charlist ) // does `CutR(charlist);CutL(charlist);' { target.detach(); str_cut_left( target.box->s, charlist ); str_cut_right( target.box->s, charlist ); target.fix(); return target; } VS_STRING_CLASS &str_cut_spc( VS_STRING_CLASS &target ) // does `Cut(" ");' { return str_cut( target, VS_CHAR_L(" ") ); } VS_STRING_CLASS &str_pad( VS_STRING_CLASS &target, int len, VS_CHAR ch ) { target.resize( (len > 0) ? len : -len ); str_pad( target.box->s, len, ch ); target.fixlen(); return target; } VS_STRING_CLASS &str_comma( VS_STRING_CLASS &target, VS_CHAR delim ) { int new_size = str_len( target ) / 3 + str_len( target ); target.resize( new_size ); str_comma( target.box->s, delim ); target.fix(); return target; } void str_set_ch( VS_STRING_CLASS &target, int pos, const VS_CHAR ch ) // sets `ch' VS_CHAR at position `pos' { if ( pos < 0 ) pos = target.box->sl + pos; if ( pos < 0 || pos >= target.box->sl ) return; if (target.box->s[pos] != ch) target.detach(); target.box->s[pos] = ch; } VS_CHAR str_get_ch( VS_STRING_CLASS &target, int pos ) // return VS_CHAR at position `pos' { if ( pos < 0 ) pos = target.box->sl + pos; if ( pos < 0 || pos >= target.box->sl ) return 0; return target.box->s[pos]; } void str_add_ch( VS_STRING_CLASS &target, const VS_CHAR ch ) // adds `ch' at the end { int sl = target.box->sl; if( sl + 1 >= target.box->size ) target.resize( sl + 1 ); target.box->s[sl] = ch; target.box->s[sl+1] = 0; target.box->sl++; } void str_add_ch_range( VS_STRING_CLASS &target, const VS_CHAR fr, const VS_CHAR to ) // adds all from `fr' to 'to' at the end { if( fr > to ) return; target.resize( target.box->sl + ( to - fr ) + 1 ); for( int i = fr; i <= to; i++ ) target.box->s[target.box->sl++] = i; target.box->s[target.box->sl] = 0; } VS_CHAR* str_word( VS_STRING_CLASS &target, const VS_CHAR* delimiters, VS_CHAR* result ) { target.detach(); str_word( target.box->s, delimiters, result ); target.fix(); return result[0] ? result : NULL; } VS_CHAR* str_rword( VS_STRING_CLASS &target, const VS_CHAR* delimiters, VS_CHAR* result ) { target.detach(); str_rword( target.box->s, delimiters, result ); target.fix(); return result; } int sprintf( int init_size, VS_STRING_CLASS &target, const VS_CHAR *format, ... ) { VS_CHAR *tmp = new VS_CHAR[init_size]; va_list vlist; va_start( vlist, format ); int res = VS_FN_VSPRINTF( tmp, init_size, format, vlist ); va_end( vlist ); target = tmp; delete [] tmp; return res; } int sprintf( VS_STRING_CLASS &target, const VS_CHAR *format, ... ) { #define VSPRINTF_BUF_SIZE 1024 VS_CHAR tmp[VSPRINTF_BUF_SIZE]; va_list vlist; va_start( vlist, format ); int res = VS_FN_VSPRINTF( tmp, VSPRINTF_BUF_SIZE, format, vlist ); va_end( vlist ); target = tmp; return res; } VS_STRING_CLASS& str_tr ( VS_STRING_CLASS& target, const VS_CHAR *from, const VS_CHAR *to ) { target.detach(); str_tr( target.box->s, from, to ); return target; } VS_STRING_CLASS& str_up ( VS_STRING_CLASS& target ) { target.detach(); str_up( target.box->s ); return target; } VS_STRING_CLASS& str_low( VS_STRING_CLASS& target ) { target.detach(); str_low( target.box->s ); return target; } VS_STRING_CLASS& str_flip_case( VS_STRING_CLASS& target ) { target.detach(); str_flip_case( target.box->s ); return target; } VS_STRING_CLASS& str_reverse( VS_STRING_CLASS& target ) { target.detach(); str_reverse( target.box->s ); return target; } VS_STRING_CLASS &str_squeeze( VS_STRING_CLASS &target, const VS_CHAR* sq_VS_CHARs ) // squeeze repeating VS_CHARs to one only { target.detach(); str_squeeze( target.box->s, sq_VS_CHARs ); target.fix(); return target; } /* utilities */ void VS_STRING_CLASS::print() // print string data to stdout (console) { #ifdef _VSTRING_WIDE_ wprintf( L"%ls\n", box->s ); #else printf( "%s\n", box->s ); #endif } /* conversions/reversed char type functions */ void VS_STRING_CLASS::set( const VS_CHAR_R* prs ) { if (prs == NULL || prs[0] == 0) resize( 0 ); else { #ifdef _VSTRING_WIDE_ set_failsafe( prs ); #else int rz = VS_FN_CONVERT( NULL, prs, 0 ); // calc required "result size" if( rz == -1 ) return resize( 0 ); resize( rz ); VS_FN_CONVERT( box->s, prs, rz ); box->s[rz] = 0; box->sl = rz; #endif } } #ifdef _VSTRING_WIDE_ int VS_STRING_CLASS::set_failsafe( const char* mbs ) { int err = 0; undef(); mbtowc( NULL, NULL, 0 ); /* reset mbtowc shift state */ wchar_t wch; const char* ps = mbs; while( ps ) { int r = mbtowc( &wch, ps, 4 ); if( r == -1 ) { err++; wch = 0xFFFD; ps++; } else if( r > 0 ) { ps += r; } else { return err; } str_add_ch( *this, wch ); } return err; } #endif /**************************************************************************** ** ** VS_STRING_CLASS Functions (for class VS_STRING_CLASS) ** ****************************************************************************/ /**************************************************************************** ** ** VS_STRING_CLASS Functions (for VS_CHAR*) ** ****************************************************************************/ VS_CHAR* str_set( VS_CHAR* target, const VS_CHAR* ps ) { target[0] = 0; if (ps) VS_FN_STRCPY( target, ps ); VS_FN_STRCPY( target, ps ); return target; } VS_CHAR* str_mul( VS_CHAR* target, int n ) // multiplies the string n times, i.e. "1"*5 = "11111" { if ( n < 0 ) return target; if ( n == 0 ) { target[0] = 0; return target; } int sl = str_len( target ); int z = (n - 1) * sl; while( z > 0 ) { vs_memcpy( target + z, target, sl ); z -= sl; } target[sl*n] = 0; return target; } int str_find( const VS_CHAR* target, const VS_CHAR c, int startpos ) // returns first zero-based position of VS_CHAR, or -1 if not found { if (startpos < 0) return -1; int sl = str_len( target ); if (startpos >= sl) return -1; const VS_CHAR* pc = VS_FN_STRCHR( target + startpos, c ); if( ! pc ) return -1; return pc - target; } int str_rfind( const VS_CHAR* target, const VS_CHAR c, int startpos ) // returns last zero-based position of VS_CHAR, or -1 if not found { if( startpos == 0 ) { const VS_CHAR* pc = VS_FN_STRRCHR( target, c ); if( ! pc ) return -1; return pc - target; } else { int sl = str_len( target ); int pos = startpos > 0 ? startpos : sl + startpos; if( pos < 0 ) return -1; if( pos >= sl ) pos = sl - 1; while( pos > -1 ) { if( target[pos] == c ) return pos; pos--; } return pos; } } int str_find( const VS_CHAR* target, const VS_CHAR* s, int startpos ) // returns first zero-based position of VS_STRING_CLASS, or -1 if not found { if (startpos < 0) return -1; int sl = str_len( target ); if (startpos >= sl) return -1; const VS_CHAR* pc = VS_FN_STRSTR( target + startpos, s ); if( ! pc ) return -1; return pc - target; } int str_rfind( const VS_CHAR* target, const VS_CHAR* s, int startpos ) // returns last zero-based position of VS_STRING_CLASS, or -1 if not found { int sl = str_len( target ); int sls = str_len( s ); int z = sl - sls; if( startpos < 0 ) { z += startpos; if( z < 0 ) return -1; } else if( startpos > 0 ) { if( startpos > z ) return -1; z = startpos; } while ( z > 0 ) { if ( VS_FN_STRNCMP( target + z, s, sls ) == 0 ) return z; z--; } return -1; } VS_CHAR* str_del( VS_CHAR* target, int pos, int len ) // deletes `len' VS_CHARs starting from `pos' { int sl = str_len( target ); if ( pos > sl || pos < 0 ) return target; int z = sl - pos - len; if ( pos + len < sl ) vs_memmove( target + pos, target + pos + len, z + 1 ); else target[pos] = 0; return target; } VS_CHAR* str_ins( VS_CHAR* target, int pos, const VS_CHAR* s ) // inserts `s' in position `pos' { int sl = str_len( target ); if ( pos > sl || pos < 0 ) return target; int sls = str_len( s ); if ( sls < 1 ) return target; vs_memmove( target + pos + sls, target + pos, sl - pos + 1 ); vs_memmove( target + pos, s, sls ); return target; } VS_CHAR* str_ins_ch( VS_CHAR* target, int pos, VS_CHAR ch ) // inserts `ch' in position `pos' { VS_CHAR tmp[2]; tmp[0] = ch; tmp[1] = 0; str_ins( target, pos, tmp ); return target; } VS_CHAR* str_replace( VS_CHAR* target, const VS_CHAR* out, const VS_CHAR* in ) // replace `out' w. `in' { int outl = str_len( out ); int inl = str_len( in ); int z = str_find( target, out ); while(z != -1) { str_del( target, z, outl ); str_ins( target, z, in ); z = str_find( target, out, z + inl ); } return target; } inline int __str_copy_calc_offsets( const VS_CHAR* source, int& pos, int& len ) { ASSERT( len >= -1 ); int sl = str_len( source ); if ( pos < 0 ) { pos = sl + pos; if ( pos < 0 ) pos = 0; } if ( pos < 0 || pos >= sl ) return 1; if ( len == -1 ) len = sl - pos; // untill the end of the string (default arg) if ( len < 1 ) return 1; if ( pos + len >= sl ) len = sl - pos; return 0; } VS_CHAR* str_copy( VS_CHAR* target, const VS_CHAR* source, int pos, int len ) // returns `len' VS_CHARs from `pos' { if( __str_copy_calc_offsets( source, pos, len ) ) { target[ 0 ] = 0; return target; } vs_memmove( target, source + pos, len ); target[ len ] = 0; return target; } VS_CHAR* str_left( VS_CHAR* target, const VS_CHAR* source, int len ) // returns `len' VS_CHARs from the left { return str_copy( target, source, 0, len ); } VS_CHAR* str_right( VS_CHAR* target, const VS_CHAR* source, int len ) // returns `len' VS_CHARs from the right { return str_copy( target, source, str_len( source ) - len, len ); } VS_CHAR* str_sleft( VS_CHAR* target, int len ) // SelfLeft -- just as 'Left' but works on `this' { if ( len < str_len(target) && len >= 0 ) target[len] = 0; return target; } VS_CHAR* str_sright( VS_CHAR* target, int len ) // SelfRight -- just as 'Right' but works on `this' { int sl = str_len( target ); if( len < sl ) { vs_memmove( target, target + ( sl - len ), len + 1 ); target[len] = 0; } return target; } VS_CHAR* str_trim_left( VS_CHAR* target, int len ) // trims `len' VS_CHARs from the beginning (left) { int sl = str_len( target ); int sr = sl - len; // result string length (without trailing 0) if( sr >= sl ) return target; if( sr > 0 ) { vs_memmove( target, target + len, sr + 1 ); target[sr] = 0; } else target[0] = 0; return target; } VS_CHAR* str_trim_right( VS_CHAR* target, int len ) // trim `len' VS_CHARs from the end (right) { int sl = str_len( target ); int sr = sl - len; // result string length (without trailing 0) if( sr >= sl ) return target; if( sr > 0 ) target[sr] = 0; else target[0] = 0; return target; } void str_set_ch( VS_CHAR* target, int pos, const VS_CHAR ch ) // sets `ch' VS_CHAR at position `pos' { int sl = str_len( target ); if ( pos < 0 ) pos = sl + pos; if ( pos < 0 || pos >= sl ) return; target[pos] = ch; } VS_CHAR str_get_ch( VS_CHAR* target, int pos ) // return VS_CHAR at position `pos' { int sl = str_len( target ); if ( pos < 0 ) pos = sl + pos; if ( pos < 0 || pos >= sl ) return 0; return target[pos]; } void str_add_ch( VS_CHAR* target, const VS_CHAR ch ) // adds `ch' at the end { int sl = str_len( target ); target[sl] = ch; target[sl+1] = 0; } void str_add_ch_range( VS_CHAR* target, const VS_CHAR fr, const VS_CHAR to ) // adds all from `fr' to 'to' at the end { if( fr > to ) return; int sl = str_len( target ); for( int i = fr; i < to; i++ ) target[sl++] = i; target[sl] = 0; } // return first `word', i.e. from pos 0 to first found delimiter VS_CHAR // after that deletes this `word' from the target VS_CHAR* str_word( VS_CHAR* target, const VS_CHAR* delimiters, VS_CHAR* result ) { int z = 0; int sl = str_len( target ); while ((VS_FN_STRCHR(delimiters, target[z]) == NULL) && (target[z] != 0)) z++; vs_memmove(result, target, z); result[z] = 0; if ( z > 0 ) { if( z < sl ) vs_memmove( target, target + z + 1, sl - z ); // including trailing zero else target[0] = 0; } return result[0] ? result : NULL; } // ...same but `last' word VS_CHAR* str_rword( VS_CHAR* target, const VS_CHAR* delimiters, VS_CHAR* result ) { result[0] = 0; int sl = str_len( target ); int z = sl - 1; while ( VS_FN_STRCHR( delimiters, target[z] ) == NULL && z > 0 ) z--; if (z < 0) return NULL; vs_memmove( result, target + z + 1, sl - z ); target[z] = 0; return result; } VS_CHAR* str_cut_left( VS_CHAR* target, const VS_CHAR* charlist ) // remove all VS_CHARs `charlist' from the beginning (i.e. from the left) { int sl = str_len(target); if (sl == 0) return target; int z = 0; while ((VS_FN_STRCHR(charlist, target[z]) != NULL) && (target[z] != 0)) z++; if (z == 0) return target; vs_memmove( target, target + z, sl - z + 1 ); return target; } VS_CHAR* str_cut_right( VS_CHAR* target, const VS_CHAR* charlist ) // remove all VS_CHARs `charlist' from the end (i.e. from the right) { if (str_len(target) == 0) return target; int z = str_len(target) - 1; while ((VS_FN_STRCHR(charlist, target[z]) != NULL) && (z > 0)) z--; target[z+1] = 0; return target; } VS_CHAR* str_cut( VS_CHAR* target, const VS_CHAR* charlist ) // does `CutR(charlist);CutL(charlist);' { str_cut_left( target, charlist ); str_cut_right( target, charlist ); return target; } VS_CHAR* str_cut_spc( VS_CHAR* target ) // does `Cut(" ");' { str_cut_left( target, VS_CHAR_L(" ") ); str_cut_right( target, VS_CHAR_L(" ") ); return target; } // expand string to width 'len' filling with VS_CHAR 'ch' // if len > 0 target will be padded right, else left VS_CHAR* str_pad( VS_CHAR* target, int len, VS_CHAR ch ) { int sl = str_len( target ); int _len; _len = (len >= 0) ? len : - len; if ( _len <= sl ) { target[_len] = 0; return target; } // FIXME: do it without new mem VS_CHAR *tmp = new VS_CHAR[_len + 1]; // result buffer -- need len + 1 tmp[0] = ch; tmp[1] = 0; str_mul( tmp, _len - sl ); if ( len < 0 ) { VS_FN_STRCAT( target, tmp ); } else { VS_FN_STRCAT( tmp, target ); VS_FN_STRCPY( target, tmp ); } delete [] tmp; return target; } // insert `commas' for 1000's delimiter or use another delimiter // VS_STRING_CLASS supposed to be a integer or real w/o `e' format VS_CHAR* str_comma( VS_CHAR* target, VS_CHAR delim ) { int dot = str_rfind( target, VS_CHAR_L('.') ); if (dot == -1) dot = str_len( target ); dot -= 3; while( dot > 0 ) { str_ins_ch( target, dot , delim ); dot -= 3; } return target; } // translate VS_CHARs from `from' to `to' // length of `from' MUST be equal to length of `to' VS_CHAR* str_tr( VS_CHAR* target, const VS_CHAR *from, const VS_CHAR *to ) { ASSERT( str_len( from ) == str_len( to ) ); if ( str_len( from ) != str_len( to ) ) return target; int z = 0; int sl = str_len( target ); for( z = 0; z < sl; z++ ) { const VS_CHAR *pc = VS_FN_STRCHR( from, target[z] ); if (pc) target[z] = to[ pc - from ]; } return target; } VS_CHAR* str_up( VS_CHAR* target ) { int sl = str_len( target ); for( int z = 0; z < sl; z++ ) target[z] = VS_FN_TOUPPER( target[z] ); return target; } VS_CHAR* str_low( VS_CHAR* target ) { int sl = str_len( target ); for( int z = 0; z < sl; z++ ) target[z] = VS_FN_TOLOWER( target[z] ); return target; } VS_CHAR* str_flip_case( VS_CHAR* target ) // CUTE nali? :) // vladi { int sl = str_len( target ); for( int z = 0; z < sl; z++ ) { if ( target[z] >= 'a' && target[z] <= 'z' ) target[z] -= 32; else if ( target[z] >= 'A' && target[z] <= 'Z' ) target[z] += 32; } return target; } VS_CHAR* str_reverse( VS_CHAR* target ) // reverse the VS_STRING_CLASS: `abcde' becomes `edcba' :) { int z = 0; int x = str_len(target)-1; while( z < x ) { VS_CHAR ch = target[ z ]; target[ z++ ] = target[ x ]; target[ x-- ] = ch; } return target; } VS_CHAR* str_squeeze( VS_CHAR* target, const VS_CHAR* sq_VS_CHARs ) // squeeze repeating VS_CHARs to one only { if ( ! target ) return NULL; if ( ! sq_VS_CHARs ) return NULL; int rc = -1; int pos = 0; while( target[pos] ) { if ( rc == -1 && VS_FN_STRCHR( sq_VS_CHARs, target[pos] ) ) { rc = target[pos]; pos++; } else if ( rc != -1 && target[pos] == rc ) { str_del( target, pos, 1 ); } else if ( rc != -1 && target[pos] != rc ) { rc = -1; } else pos++; } return target; } /**************************************************************************** ** ** VS_STRING_CLASS Functions (for const VS_CHAR*) ** ****************************************************************************/ VS_STRING_CLASS str_up ( const VS_CHAR* src ) { VS_STRING_CLASS ret = src; return str_up( ret ); } VS_STRING_CLASS str_low( const VS_CHAR* src ) { VS_STRING_CLASS ret = src; return str_low( ret ); } VS_STRING_CLASS str_flip_case( const VS_CHAR* src ) { VS_STRING_CLASS ret = src; return str_flip_case( ret ); } /**************************************************************************** ** ** VS_STRING_CLASS Functions -- common (VS_STRING_CLASS class will pass transparently) ** ****************************************************************************/ int str_count( const VS_CHAR* target, const VS_CHAR* charlist, int startpos ) // returns match count of all VS_CHARs from `charlist' { if (!target) return 0; int sl = str_len( target ); if ( startpos >= sl || startpos < 0 ) return 0; int z; int cnt = 0; for ( z = startpos; z < sl; z++ ) cnt += ( VS_FN_STRCHR( charlist, target[z]) != NULL ); return cnt; } int str_str_count( const VS_CHAR* target, const VS_CHAR* s, int startpos ) // returns match count of `s' VS_STRING_CLASS into target { if (!target) return 0; int cnt = 0; int sl = str_len( s ); if ( startpos >= sl || startpos < 0 ) return 0; const VS_CHAR* pc = target + startpos; while( (pc = VS_FN_STRSTR( pc, s )) ) { pc += sl; cnt++; } return cnt; } int str_is_int( const VS_CHAR* target ) // check if VS_STRING_CLASS is correct int value { if (!target) return 0; VS_CHAR *tmp = VS_FN_STRDUP( target ); str_cut_spc( tmp ); int dc = str_count( tmp, VS_CHAR_L("0123456789") ); int sl = str_len( tmp ); free( tmp ); return ( dc == sl ); } int str_is_double( const VS_CHAR* target ) // check if VS_STRING_CLASS is correct double (w/o `e' format :( ) { if (!target) return 0; VS_CHAR *tmp = VS_FN_STRDUP( target ); str_cut_spc( tmp ); int dc = str_count( tmp, VS_CHAR_L("0123456789") ); int cc = str_count( tmp, VS_CHAR_L(".") ); int sl = str_len( tmp ); free( tmp ); return ( (dc + cc == sl) && ( cc == 1 ) ); } /*************************************************************************** ** ** VARRAYBOX ** ****************************************************************************/ VS_ARRAY_BOX::VS_ARRAY_BOX() { _data = NULL; _size = 0; _count = 0; block_size = VARRAY_DEFAULT_BLOCK_SIZE; } VS_ARRAY_BOX::~VS_ARRAY_BOX() { undef(); } void VS_ARRAY_BOX::undef() { resize( 0 ); } VS_ARRAY_BOX* VS_ARRAY_BOX::clone() { VS_ARRAY_BOX *new_box = new VS_ARRAY_BOX(); new_box->resize( _size ); new_box->_count = _count; int i; for( i = 0; i < _count; i++ ) { new_box->_data[i] = new VS_STRING_CLASS; *new_box->_data[i] = *_data[i]; } return new_box; } void VS_ARRAY_BOX::resize( int new_size ) { if ( new_size < 0 ) new_size = 0; while ( new_size < _count ) { ASSERT( _data[ _count - 1 ] ); delete _data[ _count - 1 ]; _data[ _count - 1 ] = NULL; _count--; } if ( new_size == 0 ) { if ( _data ) delete [] _data; _data = NULL; _size = 0; _count = 0; return; } new_size = new_size / block_size + (new_size % block_size != 0); new_size *= block_size; if ( new_size == _size ) return; VS_STRING_CLASS** new_data = new VS_STRING_CLASS*[ new_size ]; ASSERT( new_data ); memset( new_data, 0, new_size * sizeof(VS_STRING_CLASS*) ); if ( _data ) { memcpy( new_data, _data, (_size < new_size ? _size : new_size) * sizeof(VS_STRING_CLASS*) ); delete [] _data; } _size = new_size; _data = new_data; } void VS_ARRAY_BOX::set_block_size( int new_block_size ) { block_size = new_block_size < 1 ? VARRAY_DEFAULT_BLOCK_SIZE : new_block_size; } /*************************************************************************** ** ** VARRAY ** ****************************************************************************/ void VS_ARRAY_CLASS::new_pos( int n ) { if( n < 0 ) return; detach(); if ( n >= box->_count ) { if ( n + 1 > box->_size ) box->resize( n + 1 ); for( int i = box->_count; i < n + 1; i++ ) { box->_data[i] = new VS_STRING_CLASS; if( compact ) box->_data[i]->compact( compact ); } box->_count = n + 1; } else { if ( box->_count == box->_size ) box->resize( box->_size + 1 ); memmove( &box->_data[0] + n + 1, &box->_data[0] + n, ( box->_count - n ) * sizeof(VS_STRING_CLASS*) ); box->_count++; box->_data[n] = new VS_STRING_CLASS; if( compact ) box->_data[n]->compact( compact ); } } void VS_ARRAY_CLASS::del_pos( int n ) { if ( n < 0 || n >= box->_count ) return; detach(); delete box->_data[n]; memmove( &box->_data[0] + n, &box->_data[0] + n + 1, ( box->_count - n ) * sizeof(VS_STRING_CLASS*) ); box->_count--; if ( box->_size - box->_count > box->block_size ) box->resize( box->_count ); } VS_ARRAY_CLASS::VS_ARRAY_CLASS() { box = new VS_ARRAY_BOX(); compact = 1; } VS_ARRAY_CLASS::VS_ARRAY_CLASS( const VS_ARRAY_CLASS& arr ) { box = arr.box; box->ref(); compact = 1; } VS_ARRAY_CLASS::VS_ARRAY_CLASS( const VS_TRIE_CLASS& tr ) { box = new VS_ARRAY_BOX(); compact = 1; *this = tr; } VS_ARRAY_CLASS::~VS_ARRAY_CLASS() { box->unref(); } void VS_ARRAY_CLASS::detach() { if ( box->refs() == 1 ) return; VS_ARRAY_BOX *new_box = box->clone(); box->unref(); box = new_box; } void VS_ARRAY_CLASS::ins( int n, const VS_CHAR* s ) { new_pos( n ); box->_data[n]->set( s ); } void VS_ARRAY_CLASS::del( int n ) { del_pos( n ); } void VS_ARRAY_CLASS::set( int n, const VS_CHAR* s ) { if( n >= box->_count ) new_pos( n ); box->_data[n]->set( s ); } const VS_CHAR* VS_ARRAY_CLASS::get( int n ) { if ( n < 0 || n >= box->_count ) return NULL; else return box->_data[n]->data(); } int VS_ARRAY_CLASS::push( const VS_CHAR* s ) { ins( box->_count, s ); return box->_count; } int VS_ARRAY_CLASS::push( VS_TRIE_CLASS *tr ) { tr->keys_and_values( this, this ); return box->_count; } int VS_ARRAY_CLASS::push( VS_ARRAY_CLASS *arr ) { ASSERT( arr != this ); int cnt = arr->count(); for( int z = 0; z < cnt; z++ ) push( arr->get( z ) ); return box->_count; } const VS_CHAR* VS_ARRAY_CLASS::pop() { if ( box->_count == 0 ) return NULL; _ret_str = get( box->_count - 1 ); del( box->_count - 1 ); return _ret_str.data(); } int VS_ARRAY_CLASS::unshift( const VS_CHAR* s ) { ins( 0, s ); return box->_count; } int VS_ARRAY_CLASS::unshift( VS_TRIE_CLASS *tr ) { VS_ARRAY_CLASS tmp_arr; tr->keys_and_values( &tmp_arr, &tmp_arr ); unshift( &tmp_arr ); return box->_count; } int VS_ARRAY_CLASS::unshift( VS_ARRAY_CLASS *arr ) { ASSERT( arr != this ); // TODO: this is not efficient, data storage must be moved by input count and filled in place! int cnt = arr->count(); for( int z = cnt - 1; z >= 0; z-- ) unshift( arr->get( z ) ); return box->_count; } const VS_CHAR* VS_ARRAY_CLASS::shift() { if ( box->_count == 0 ) return NULL; _ret_str = get( 0 ); del( 0 ); return _ret_str.data(); } void VS_ARRAY_CLASS::ins( int n, const VS_STRING_CLASS& vs ) { new_pos( n ); *box->_data[n] = vs; } void VS_ARRAY_CLASS::set( int n, const VS_STRING_CLASS& vs ) { if( n >= box->_count ) new_pos( n ); *box->_data[n] = vs; } int VS_ARRAY_CLASS::push( const VS_STRING_CLASS& vs ) { ins( box->_count, vs ); return box->_count; } int VS_ARRAY_CLASS::unshift( const VS_STRING_CLASS& vs ) { ins( 0, vs ); return box->_count; } int VS_ARRAY_CLASS::fload( const char* fname ) { undef(); FILE* f = fopen( fname, "rt" ); if (!f) return 1; int r = fload( f ); fclose(f); return r; } int VS_ARRAY_CLASS::fsave( const char* fname ) { FILE* f = fopen( fname, "wt" ); if (!f) return 1; int r = fsave( f ); fclose(f); return r; } int VS_ARRAY_CLASS::fload( FILE* f ) { undef(); char buf[1024*1024]; VString vstr; while( fgets( buf, sizeof(buf)-1, f ) ) { vstr += buf; if ( str_get_ch( vstr, -1 ) != '\n' && !feof(f) ) continue; while ( str_get_ch( vstr, -1 ) == '\r' || str_get_ch( vstr, -1 ) == '\n' ) str_trim_right( vstr, 1 ); #ifdef _VSTRING_WIDE_ push( WString( vstr ) ); #else push( vstr ); #endif vstr.undef(); } return 0; } int VS_ARRAY_CLASS::fsave( FILE* f ) { for( int z = 0; z < box->_count; z++ ) { size_t len; const char* ps; #ifdef _VSTRING_WIDE_ VString vstr = get(z); ps = vstr.data(); len = str_len( vstr ); #else ps = get(z); len = str_len( ps ); #endif if ( fwrite( ps, 1, len, f ) != len ) return 2; if ( fwrite( "\n", 1, 1, f ) != 1 ) return 2; } return 0; } void VS_ARRAY_CLASS::sort( int rev, int (*q_strcmp)(const VS_CHAR *, const VS_CHAR *) ) { if ( count() > 1 ) q_sort( 0, count() - 1, q_strcmp ? q_strcmp : VS_FN_STRCMP ); if ( rev ) // FIXME: not optimal... reverse(); } void VS_ARRAY_CLASS::q_sort( int lo, int hi, int (*q_strcmp)(const VS_CHAR *, const VS_CHAR *) ) { int m, l, r; const VS_CHAR* v; m = ( hi + lo ) / 2; v = box->_data[m]->data(); l = lo; r = hi; do { while( (l <= hi) && (q_strcmp(box->_data[l]->data(),v) < 0) ) l++; while( (r >= lo) && (q_strcmp(v,box->_data[r]->data()) < 0) ) r--; if ( l <= r ) { VS_STRING_CLASS *t; t = box->_data[l]; box->_data[l] = box->_data[r]; box->_data[r] = t; l++; r--; } } while( l <= r ); if ( lo < r ) q_sort( lo, r, q_strcmp ); if ( l < hi ) q_sort( l, hi, q_strcmp ); } void VS_ARRAY_CLASS::reverse() { int m = box->_count / 2; for( int z = 0; z < m; z++ ) { VS_STRING_CLASS *t; t = box->_data[z]; box->_data[z] = box->_data[box->_count-1-z]; box->_data[box->_count-1-z] = t; } } void VS_ARRAY_CLASS::shuffle() /* Fisher-Yates shuffle */ { int i = box->_count - 1; while( i >= 0 ) { int j = rand() % ( i + 1 ); VS_STRING_CLASS *t; t = box->_data[i]; box->_data[i] = box->_data[j]; box->_data[j] = t; i--; } } void VS_ARRAY_CLASS::print() { int z; for( z = 0; z < count(); z++ ) VS_FN_PRINTF( VS_CHAR_L( "%d=" VS_SFMT "\n" ), z, get(z) ); } int VS_ARRAY_CLASS::max_len() { if ( count() == 0 ) return 0; int l = 0; int z; for( z = 0; z < count(); z++ ) { int sl = str_len(get(z)); if ( sl > l ) l = sl; } return l; } int VS_ARRAY_CLASS::min_len() { if ( count() == 0 ) return 0; int l = str_len(get(0)); int z; for( z = 0; z < count(); z++ ) { int sl = str_len(get(z)); if ( sl < l ) l = sl; } return l; } /*************************************************************************** ** ** VTRIENODE ** ****************************************************************************/ VS_TRIE_NODE::VS_TRIE_NODE() { next = NULL; down = NULL; c = 0; data = NULL; } VS_TRIE_NODE::~VS_TRIE_NODE() { //printf( " ~DELETE: %p [%c] ---> next: %p down %p data %p\n", this, c, next, down, data ); if ( next ) delete next; if ( down ) delete down; if ( data ) delete data; } VS_TRIE_NODE *VS_TRIE_NODE::clone() { VS_TRIE_NODE *tmp = new VS_TRIE_NODE(); tmp->c = c; if ( next ) tmp->next = next->clone(); if ( down ) tmp->down = down->clone(); if ( data ) tmp->data = new VS_STRING_CLASS( *data ); return tmp; } void VS_TRIE_NODE::print() { VS_FN_PRINTF( VS_CHAR_L("---------------------------------\n") ); VS_FN_PRINTF( VS_CHAR_L("this = %p\n"), (void*)this ); VS_FN_PRINTF( VS_CHAR_L("key = %c\n"), c ); VS_FN_PRINTF( VS_CHAR_L("next = %p\n"), (void*)next ); VS_FN_PRINTF( VS_CHAR_L("down = %p\n"), (void*)down ); VS_FN_PRINTF( VS_CHAR_L("data = " VS_SFMT "\n"), data ? data->data() : VS_CHAR_L("(null)") ); if (next) next->print(); if (down) down->print(); } /*************************************************************************** ** ** VTRIEBOX ** ****************************************************************************/ VS_TRIE_BOX* VS_TRIE_BOX::clone() { VS_TRIE_BOX *new_box = new VS_TRIE_BOX(); delete new_box->root; new_box->root = root->clone(); return new_box; } VS_TRIE_NODE* VS_TRIE_BOX::find_node( VS_TRIE_NODE* node, const VS_CHAR* key, int create ) { if( ! key || ! key[0] ) return NULL; if( key[0] == node->c ) { // VS_CHAR in the current key if ( key[1] == 0 ) { // last VS_CHAR and is in the key--found! return node; } else { // not last VS_CHAR... if ( ! node->down && create ) { // nothing below but should create node->down = new VS_TRIE_NODE(); node->down->c = key[1]; } if ( node->down ) return find_node( node->down, key + 1, create ); // search below else return NULL; // not found } } else { // VS_CHAR not in the current key--try next if ( ! node->next && create ) { // no next but should create node->next = new VS_TRIE_NODE(); node->next->c = key[0]; } if ( node->next ) return find_node( node->next, key, create ); // search next else return NULL; // not found } } int VS_TRIE_BOX::count_data_nodes( VS_TRIE_NODE* node ) { if( ! node ) return 0; return ( node->data ? 1 : 0 ) + count_data_nodes( node->next ) + count_data_nodes( node->down ); } /* void VS_TRIE_BOX::del_node( VS_TRIE_NODE* node, const VS_CHAR *key, int branch ) { VS_TRIE_NODE* del_node = find_node( node, key ); if( ! del_node ) return; if( del_node->data ) { delete del_node->data; del_node->data = NULL; } if( branch && del_node->down ) { delete del_node->down; del_node->down = NULL; } } */ void VS_TRIE_BOX::del_node( VS_TRIE_NODE* node, const VS_CHAR *key, int branch ) { //printf( "%p --> [%c] data: %p next: %p down: %p <-- key: %s\n", node, node->c, node->data, node->next, node->down, key ); if( ! key || ! key[0] ) return; if( key[0] == node->c ) { // VS_CHAR in the current key if ( key[1] == 0 ) { // last VS_CHAR and is in the key--found! //printf( "FOUND: %p --> [%c] data: %p next: %p down: %p <-- key: %s\n", node, node->c, node->data, node->next, node->down, key ); if( node->data ) { delete node->data; node->data = NULL; } if( branch && node->down ) { //printf( " DELETE: DOWN: %p [%c] -> %p\n", node, node->c, node->down ); delete node->down; node->down = NULL; } } else { // not last VS_CHAR... if ( node->down ) { del_node( node->down, key + 1, branch ); // search below if( ! node->down->data && ! node->down->down ) { //printf( " DELETE: AFTER DOWN: %p [%c] -> %p\n", node, node->c, node->down ); VS_TRIE_NODE* tmp = node->down; node->down = node->down->next; tmp->next = NULL; delete tmp; } } } } else { // VS_CHAR not in the current key--try next if ( node->next ) { del_node( node->next, key, branch ); // search next if( ! node->next->data && ! node->next->down ) { //printf( " DELETE: NEXT: %p [%c] -> %p\n", node, node->c, node->next ); VS_TRIE_NODE* tmp = node->next; node->next = node->next->next; tmp->next = NULL; delete tmp; } } } } int VS_TRIE_BOX::vacuum_node( VS_TRIE_NODE* node ) { int vc = 0; // vacuum count if( node->down ) { vc += vacuum_node( node->down ); if( ! node->down->data && ! node->down->down ) { VS_TRIE_NODE* tmp = node->down; node->down = node->down->next; delete tmp; vc++; } } if( node->next ) { vc += vacuum_node( node->next ); if( ! node->next->data && ! node->next->down ) { VS_TRIE_NODE* tmp = node->next; node->next = node->next->next; delete tmp; vc++; } } return vc; } int VS_TRIE_BOX::vacuum() { return vacuum_node( root ); } /*************************************************************************** ** ** VTRIE ** ****************************************************************************/ VS_TRIE_CLASS::VS_TRIE_CLASS() { box = new VS_TRIE_BOX(); } VS_TRIE_CLASS::VS_TRIE_CLASS( const VS_ARRAY_CLASS& arr ) { box = new VS_TRIE_BOX(); merge( (VS_ARRAY_CLASS*)&arr ); } VS_TRIE_CLASS::VS_TRIE_CLASS( const VS_TRIE_CLASS& tr ) { box = tr.box; box->ref(); } VS_TRIE_CLASS::~VS_TRIE_CLASS() { box->unref(); } int VS_TRIE_CLASS::count( const VS_CHAR* key ) { return box->count_data_nodes( key ? box->find_node( box->root, key ) : box->root ); } void VS_TRIE_CLASS::detach() { if ( box->refs() == 1 ) return; VS_TRIE_BOX *new_box = box->clone(); box->unref(); box = new_box; } void VS_TRIE_CLASS::trace_node( VS_TRIE_NODE *node, VS_ARRAY_CLASS* keys, VS_ARRAY_CLASS *vals ) { int kl = str_len( temp_key ); if ( node->c ) str_add_ch( temp_key, node->c ); if ( node->data ) { if ( keys ) keys->push( temp_key ); if ( vals ) vals->push( node->data->data() ); } if ( node->down ) trace_node( node->down, keys, vals ); str_sleft( temp_key, kl ); if ( node->next ) trace_node( node->next, keys, vals ); } void VS_TRIE_CLASS::print_trace_node( VS_TRIE_NODE *node, int level ) { for( int i = 0; i < level*8; i++ ) printf( " " ); printf( "%p --> [%c] data: %p next: %p down: %p\n", (void*)node, node->c, (void*)(node->data), (void*)(node->next), (void*)(node->down) ); if ( node->down ) print_trace_node( node->down, level + 1 ); if ( node->next ) print_trace_node( node->next, level ); } void VS_TRIE_CLASS::set( const VS_CHAR* key, const VS_CHAR* value ) { if ( !value || !key || !key[0] ) return; detach(); VS_TRIE_NODE *node = box->find_node( box->root, key, 1 ); ASSERT( node ); if ( ! node->data ) node->data = new VS_STRING_CLASS(); node->data->set( value ); } const VS_CHAR* VS_TRIE_CLASS::get( const VS_CHAR* key ) { if ( !key || !key[0] ) return NULL; VS_TRIE_NODE *node = box->find_node( box->root, key ); if ( node && node->data ) return node->data->data(); else return NULL; } void VS_TRIE_CLASS::del( const VS_CHAR* key, int branch ) { if ( !key || !key[0] ) return; detach(); box->del_node( box->root, key, branch ); } int VS_TRIE_CLASS::exists( const VS_CHAR* key ) // return != 0 if key exist (with data) { VS_TRIE_NODE *node = box->find_node( box->root, key ); if ( node && node->data ) return 1; else return 0; } void VS_TRIE_CLASS::keys_and_values( VS_ARRAY_CLASS *keys, VS_ARRAY_CLASS *values ) { trace_node( box->root, keys, values ); } VS_ARRAY_CLASS VS_TRIE_CLASS::keys() { VS_ARRAY_CLASS arr; keys_and_values( &arr, NULL ); return arr; } VS_ARRAY_CLASS VS_TRIE_CLASS::values() { VS_ARRAY_CLASS arr; keys_and_values( NULL, &arr ); return arr; } void VS_TRIE_CLASS::reverse() { VS_ARRAY_CLASS ka = keys(); VS_ARRAY_CLASS va = values(); ASSERT( ka.count() == va.count() ); undef(); int z = ka.count(); while( z-- ) { set( va.get( z ), ka.get( z ) ); } } void VS_TRIE_CLASS::merge( VS_TRIE_CLASS *tr ) { ASSERT( tr != this ); VS_ARRAY_CLASS ka = tr->keys(); VS_ARRAY_CLASS va = tr->values(); ASSERT( ka.count() == va.count() ); int z = ka.count(); while( z-- ) { set( ka.get( z ), va.get( z ) ); } } void VS_TRIE_CLASS::merge( VS_ARRAY_CLASS *arr ) { int z = 0; while( z < arr->count() ) { set( arr->get( z ), arr->get( z + 1 ) ); z += 2; } } int VS_TRIE_CLASS::fload( const char* fname ) { FILE* f = fopen( fname, "rt" ); if (!f) return 1; int r = fload( f ); fclose(f); return r; } int VS_TRIE_CLASS::fsave( const char* fname ) { FILE* f = fopen( fname, "wt" ); if (!f) return 1; int r = fsave( f ); fclose(f); return r; } int VS_TRIE_CLASS::fload( FILE* f ) { VS_ARRAY_CLASS arr; int r = arr.fload( f ); if ( r == 0 ) merge( &arr ); return r; } int VS_TRIE_CLASS::fsave( FILE* f ) { VS_ARRAY_CLASS arr; arr.push( this ); return arr.fsave( f ); } void VS_TRIE_CLASS::print() { VS_ARRAY_CLASS ka = keys(); VS_ARRAY_CLASS va = values(); ASSERT( ka.count() == va.count() ); while( ka.count() && va.count() ) { VS_FN_PRINTF( VS_CHAR_L( "" VS_SFMT "=" VS_SFMT "\n"), ka.pop(), va.pop() ); } } void VS_TRIE_CLASS::print_trace() { print_trace_node( box->root, 0 ); } /**************************************************************************** ** ** VS_STRING_CLASS Utilities -- functions and classes ** ****************************************************************************/ VS_STRING_CLASS str_dot_reduce( const VS_CHAR* s, int width ) { VS_STRING_CLASS dest; dest = s; int sl = str_len( dest ); if ( sl <= width ) return dest; int pos = (width-3) / 2; str_del( dest, pos, sl - width + 3 ); str_ins( dest, pos, VS_CHAR_L("...") ); return dest; } /**************************************************************************** ** ** VS_STRING_CLASS file names utilities -- functions and classes ** NOTE: does not use any external function calls! ** ****************************************************************************/ VS_CHAR* str_fix_path( VS_CHAR* s, int slashtype ) { size_t sl = str_len( s ); if ( s[sl-1] != slashtype ) { s[sl] = slashtype; s[sl+1] = 0; } return s; } const VS_STRING_CLASS& str_fix_path( VS_STRING_CLASS &s, int slashtype ) { size_t sl = str_len( s ); if ( s[sl-1] != slashtype ) str_add_ch( s, slashtype ); return s; } VS_STRING_CLASS str_file_ext( const VS_CHAR *ps ) { VS_STRING_CLASS ext; int len = str_len(ps); int z = len - 1; while ( ps[z] != VS_CHAR_L('.') && ps[z] != VS_CHAR_L('/') && z > 0 ) z--; if ( ps[z] == VS_CHAR_L('.') ) if ( !(z == 0 || (z > 0 && ps[z-1] == VS_CHAR_L('/'))) ) // `.name' has no extension! ext = ps + z + 1; return ext; } VS_STRING_CLASS str_file_name( const VS_CHAR *ps ) { VS_STRING_CLASS name; int len = str_len( ps ); int z = len - 1; while ( z >= 0 && ps[z] != VS_CHAR_L('/') ) z--; name = ps + z + 1; z = str_len( name ) - 1; while ( z > 0 && name[z] != VS_CHAR_L('.') && name[z] != VS_CHAR_L('/') ) z--; if ( z > 0 && name[z] == VS_CHAR_L('.')) // `.name' has no extension! str_sleft( name, z ); return name; } VS_STRING_CLASS str_file_name_ext( const VS_CHAR *ps ) { VS_STRING_CLASS name; int len = str_len( ps ); int z = len - 1; while ( z >= 0 && ps[z] != VS_CHAR_L('/') ) z--; name = ps + z + 1; return name; } VS_STRING_CLASS str_file_path( const VS_CHAR *ps ) { VS_STRING_CLASS name; int len = str_len( ps ); int z = len; while ( z >= 0 && ps[z] != VS_CHAR_L('/') ) z--; name = ps; str_sleft( name, z+1 ); return name; } VS_STRING_CLASS str_reduce_path( const VS_CHAR* path ) // removes ".."s { VS_STRING_CLASS dest; dest = path; str_replace( dest, VS_CHAR_L("/./"), VS_CHAR_L("/") ); int i = -1; while( (i = str_find( dest, VS_CHAR_L("/../") ) ) != -1 ) { int j = i - 1; while( j >= 0 && dest[j] != VS_CHAR_L('/') ) j--; ASSERT( j >= -1 ); if ( j < 0 ) { if ( dest[0] == VS_CHAR_L('/') ) str_del( dest, 0, 3 ); } else str_del( dest, j+1, i+3-j ); } return dest; } /**************************************************************************** ** ** VS_STRING_CLASS Conversions ** ****************************************************************************/ long hex2long( const VS_CHAR* s ) // hex to long { long P = 1; long C = 0; VS_CHAR tmp[255]; VS_FN_STRCPY( tmp, s ); str_up( tmp ); str_cut_spc( tmp ); int sl = str_len(tmp); for( int z=sl-1; z >= 0; z-- ) { int i = -1; if( (i = str_find( VS_CHAR_L("0123456789ABCDEF"), tmp[z] )) != -1) C = C + P*i; else C = 0; P = P*16; } return C; } /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/vref.h0000644000175000017500000000412314367022256013437 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #ifndef _VREF_H_ #define _VREF_H_ #include #include #include #include #include #include #include #include #ifndef ASSERT #define ASSERT assert #endif /*************************************************************************** ** ** VREF ** ****************************************************************************/ class VRef { int _ref; public: VRef() { _ref = 1; } // creator get first reference virtual ~VRef() { ASSERT( _ref == 0 ); } void ref() { _ref++; } void unref() { ASSERT( _ref > 0 ); _ref--; if ( _ref < 1 ) delete this; } int refs() { return _ref; } }; /**************************************************************************** ** ** aux functions ** ****************************************************************************/ void *vs_memcpy( char *dest, const char *src, size_t n ); void *vs_memmove( char *dest, const char *src, size_t n ); void *vs_memcpy( wchar_t *dest, const wchar_t *src, size_t n ); void *vs_memmove( wchar_t *dest, const wchar_t *src, size_t n ); #endif /* TOP */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/README.md0000644000175000017500000001311714367022256013606 0ustar cadecade # NAME VSTRING is C++ string manipulation and handling library with Unicode support. # SYNOPSIS #include "vstring.h" #include "vstrlib.h" VString str = "Hello"; str += " World"; // str is 'Hello World' now str_reverse( str ); // str is 'dlroW olleH' now str_low( str ); // lower case VArray va = str_split( " +", str ); // array has 'dlroW', 'olleH' // " +" is Regexp str_reverse( va[0] ); str_reverse( va[1] ); str = str_join( va, " " ); // str is back to "Hello World" VTrie tr = va; // tr[ "Hello" ] contains "World" # DESCRIPTION VSTRING provides dynamic strings and char* compatibility and also Perl-like arrays, hashes and regexp objects. It also offers wide char (wchar_t) support for ISO10646/Unicode strings. The dynamic string object can be freely exchanged with standard char* (or wchar_t*) type, so there is no need to change function calls nor the implementation when you change from char* to String (and vice versa). The main difference from other similar libs is that the dynamic string class has no visible methods (except operators) so you will use it as a plain char* but it will expand/shrink as needed. All classes (VString, VArray, Vtrie and their counterparts WString, WArray, WTrie) implementation provide shallow copy and copy-on-write functionality so assignment operators like: str1 = str2 arr1 = arr2 trie1 = tri2 are cheap and fast! # REFERENCE vstring.h and vstrlib.h files can be used as reference. This file contains brief introduction and some notes but for further API documentation check the .h files. # BASE char*/wchar_t* AND VString/WString FUNCTIONS NOTES All functions for char*/wchar_t* handling may overflow! If you need safe strings, use the same functions but with VString instead of char* (and WString instead of wchar_t*). Functions common for char* and VString: str_set( str, "hello" ); str_mul( str, 4 ); str_replace( str, "o", " " ); str_left( dest_str, str, 4 ); str_up( dest_str ); In the examples above, str, dest_str and source_str may be either char* or VString. # VString CLASS NOTES VString str = "hello"; str += " world"; if( str == "hello world") { ... } int len = str_len( str ); str[3] = 'z'; // safe! even outside string boundaries # VArray CLASS NOTES VArray va; // append array elements va.push( "element 1" ); va.push( str ); // i.e. VString va.push( other_varray ); va.push( trie ); // see VTrie below // take out the last element VString str = va.pop() // push elements at the beginning va.unshift( "element 1" ); va.unshift( str ); // i.e. VString va.unshift( other_varray ); va.unshift( trie ); // see VTrie below // take out the first element VString str = va.shift(); va.reverse(); // reverse elements order va.undef(); // remove all elements # VTrie CLASS NOTES VTrie tr; tr[ "hello" ] = "world"; tr[ "number" ] = "12345"; VArray va = tr; // array is: hello world number 12345 // however only key+value order is preserved! tr.reverse(); // reverse keys <-> values tr.undef(); // remove all keys # VRegexp CLASS NOTES VRegexp re( "a([0-9]+)" ); // compiling new regexp if( re.m( "tralala85." ) ) // match against compiled regexp res1 = re[1]; // re[1] returns '85' if( re.m( "tralala85.", "(la)+" ) ) // match against new regexp pattern { str_all_matched = re[0]; // 'lala' str_first_capture = re[1]; // 'la' } re.comp( "^[a-z]+[0-9]*" ); // reuse/recompile new regexp in the same obj re.study(); // takes extra time to speed multiple matchings with m() # W-CLASSES AND wchar_t* FUNCTIONS All WString, WArray, WTrie classes and functions behave the same way as V-ones. The only difference is that they hold wider-data (wchar_t) and keys. Conversions between the two are implicit: VString str; WString wide; str = "проста проба едно"; wide = str; wide = L"две прости проби"; str = wide; int f1 = str_find( str, "проб" ); // returns 7 int f2 = str_find( wide, "проб" ); // returns 11 VString always holds byte string, it has no knowledge if the string is UTF8 or not. It does not have functions to manage characers in UTF8-encoded string. To do so it is needed that VString be converted to WString, which always works on Unicode Level 1 characters. Conversion from VString to WString is safe, i.e. if VString holds incorrectly encoded UTF8, all incorrect chars will be replaced by 0xFFFD (unknown char) symbol and the rest will be converted properly. Conversion from WString to VString is always correct. If it is needed to check the result of the conversion from VString to WString, there is explicit function "set_failsafe()" which returns the count of the incorrect chars in the string: VString str; WString wide; str = "ще се видим на славейков"; int err = wide.set_failsafe( str ); if( err ) { // there were errors, reset string or whatever... // wide.undef(); } # FEEDBACK If you find bug or have comment on library API, code or documentation text, please, contact me. # AUTHOR Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.bg/projects/vstring/ https://github.com/cade-vs/vstring # LICENSE Distributed under the GPL license, see COPYING file for the full text. vfu-5.09/vstring/AUTHORS0000644000175000017500000000034414367022256013375 0ustar cadecade Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/ http://cade.noxrun.com/projects/vstring https://github.com/cade-vs/vstring vfu-5.09/vstring/vdef.h0000644000175000017500000001053114444676576013441 0ustar cadecade/**************************************************************************** # # VSTRING Library # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # http://cade.noxrun.com/ # # Distributed under the GPL license, you should receive copy of GPLv2! # # SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # # VSTRING library provides wide set of string manipulation features # including dynamic string object that can be freely exchanged with # standard char* (or wchar_t*) type, so there is no need to change # function calls nor the implementation when you change from # char* to VString (and from wchar_t* to WString). # ***************************************************************************/ #undef PCRE2_CODE_UNIT_WIDTH #undef VS_CHAR #undef VS_CHAR_R #undef VS_CHAR_L #undef VS_SFMT #undef VS_STRING_CLASS #undef VS_STRING_CLASS_R #undef VS_ARRAY_CLASS #undef VS_TRIE_CLASS #undef VS_REGEXP_CLASS #undef VS_CHARSET_CLASS #undef VS_STRING_BOX #undef VS_ARRAY_BOX #undef VS_TRIE_BOX #undef VS_TRIE_NODE #undef VS_FN_PRINTF #undef VS_FN_SPRINTF #undef VS_FN_VSPRINTF #undef VS_FN_STRLEN #undef VS_FN_STRCPY #undef VS_FN_STRCAT #undef VS_FN_STRCMP #undef VS_FN_STRNCMP #undef VS_FN_STRCHR #undef VS_FN_STRRCHR #undef VS_FN_STRSTR #undef VS_FN_STRDUP #undef VS_FN_STRTOL #undef VS_FN_STRTOLL #undef VS_FN_STRTOD #undef VS_FN_TOUPPER #undef VS_FN_TOLOWER #undef VS_FN_CONVERT #undef LENOF_VS_CHAR #ifdef _VSTRING_WIDE_ #include #include #define PCRE2_CODE_UNIT_WIDTH 32 #define VS_CHAR wchar_t #define VS_CHAR_R char #define VS_CHAR_L(s) L##s #define VS_SFMT "%ls" #define VS_STRING_CLASS WString #define VS_STRING_CLASS_R VString #define VS_ARRAY_CLASS WArray #define VS_TRIE_CLASS WTrie #define VS_REGEXP_CLASS WRegexp #define VS_CHARSET_CLASS WCharSet #define VS_STRING_BOX WStringBox #define VS_ARRAY_BOX WArrayBox #define VS_TRIE_BOX WTrieBox #define VS_TRIE_NODE WTrieNode #define VS_FN_PRINTF wprintf #define VS_FN_SPRINTF swprintf #define VS_FN_VSPRINTF vswprintf #define VS_FN_STRLEN wcslen #define VS_FN_STRCPY wcscpy #define VS_FN_STRCAT wcscat #define VS_FN_STRCMP wcscmp #define VS_FN_STRNCMP wcsncmp #define VS_FN_STRCHR wcschr #define VS_FN_STRRCHR wcsrchr #define VS_FN_STRSTR wcsstr #define VS_FN_STRDUP wcsdup #define VS_FN_STRTOL(s) wcstol(s,NULL,10) #define VS_FN_STRTOLL(s) wcstoll(s,NULL,10) #define VS_FN_STRTOD(s) wcstod(s,NULL) #define VS_FN_TOUPPER towupper #define VS_FN_TOLOWER towlower #define VS_FN_CONVERT mbstowcs #else #define PCRE2_CODE_UNIT_WIDTH 8 #define VS_CHAR char #define VS_CHAR_R wchar_t #define VS_CHAR_L #define VS_SFMT "%s" #define VS_STRING_CLASS VString #define VS_STRING_CLASS_R WString #define VS_ARRAY_CLASS VArray #define VS_TRIE_CLASS VTrie #define VS_REGEXP_CLASS VRegexp #define VS_CHARSET_CLASS VCharSet #define VS_STRING_BOX VStringBox #define VS_ARRAY_BOX VArrayBox #define VS_TRIE_BOX VTrieBox #define VS_TRIE_NODE VTrieNode #define VS_FN_PRINTF printf #define VS_FN_SPRINTF snprintf #define VS_FN_VSPRINTF vsnprintf #define VS_FN_STRLEN strlen #define VS_FN_STRCPY strcpy #define VS_FN_STRCAT strcat #define VS_FN_STRCMP strcmp #define VS_FN_STRNCMP strncmp #define VS_FN_STRCHR strchr #define VS_FN_STRRCHR strrchr #define VS_FN_STRSTR strstr #define VS_FN_STRDUP strdup #define VS_FN_STRTOL(s) strtol(s,NULL,10) #define VS_FN_STRTOLL(s) strtoll(s,NULL,10) #define VS_FN_STRTOD(s) strtod(s,NULL) #define VS_FN_TOUPPER toupper #define VS_FN_TOLOWER tolower #define VS_FN_CONVERT wcstombs #endif #define LENOF_VS_CHAR(n) ((sizeof(n)<1)?(0):(sizeof(n)/sizeof(VS_CHAR))) /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-5.09/vstring/README0000644000175000017500000000531614367022256013211 0ustar cadecade VSTRING Library Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/ Distributed under the GPL license, you should receive copy of GPLv2! SEE 'README', 'LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! VSTRING library provides wide set of string manipulation features including dynamic string object that can be freely exchanged with standard char* (or wchar_t*) type, so there is no need to change function calls nor the implementation when you change from char* to VString (and from wchar_t* to WString). The main difference from other similar libs is that the dynamic VString/WString class has no visible methods (except operators) so you will use it as a plain char*/wchar_t* but it will expand/shrink as needed. If you find bug or you have note about vstring lib, please feel free to contact me. VSTRING part (vstring.h and vstring.cpp) implements plain string-only manipulations: char* -- functions to manipulate in-memory string buffers VString -- dynamic char* string, which resizes automatically WSTRING part (wstring.h and wstring.cpp) implements wide char (wchar_t*) string manipulations: wchar_t* -- functions to manipulate in-memory string buffers WString -- wide char (wchar_t*) unicode strings VSTRLIB/WSTRLIB part (vstrlib.h and vstrlib.cpp, wstrlib.h and wstrlib.cpp) provide string data structures which mimic Perl's. There are several classes: VArray -- array of VString elements WArray -- array of WString elements VTrie -- associative array (hash) of VString elements WTrie -- associative array (hash) of WString elements VRegexp -- regular expression helper class WRegexp -- regular expression helper class (wide VS_CHAR) All classes use shallow copy and copy-on-write functionality, so things like str1 = str2, varray1 = varray2 etc. are cheap and fast :) usage: include char* support: #include #include include wchar_t* support: #include #include include both char* and wchar_t* support: #include #include #include #include usage: compile & link: g++ -I/path/to/vstring ... ld -L/path/to/vstring -lvstring If you find bug or you have note about vstring lib, please feel free to contact me at: Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.bg/projects/vstring/ https://github.com/cade-vs/vstring vfu-5.09/install0000755000175000017500000000115614145574023012225 0ustar cadecade#!/bin/bash if [ ! -e vfu/vfu ]; then echo "vfu/vfu cannot be found, try to build it first..." exit; fi # this is not completely correct, it should check for all rx_* tools if [ ! -e rx/rx_auto ]; then echo "rx/rx_* cannot be found, check distribution/docs..." exit; fi echo "Press ENTER to install vfu in /usr/local/bin and rx_* in /usr/libexec/vfu" echo "Or press Ctrl+C to cancel" read cp vfu/vfu /usr/local/bin mkdir /usr/libexec/vfu cp rx/rx_* /usr/libexec/vfu cp vfu.1 /usr/local/man/man1 cp vfu.conf /usr/libexec/vfu cd /usr/local/bin chmod 755 vfu cd /usr/libexec/vfu chmod 755 rx_* echo "done." vfu-5.09/rx/0000755000175000017500000000000014372250714011257 5ustar cadecadevfu-5.09/rx/rx_deb0000755000175000017500000000351014145574023012447 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $ac=`ar t "$archive"`; die "unsupported DEB package\n" unless $ac =~ /(data.tar.[a-z0-9]+)/; my $data = $1; my $cache = "$archive.$data.rx.cache"; $cache =~ s/^(.+)\/([^\/]+)$/$2/; if( $cmd eq "l" || $cmd eq "v" || $cmd eq "x" ) { if( ! -e "/tmp/$cache" ) { # cache not found--fill it system( qq[ umask 0077 ; ar p "$archive" $data > "/tmp/$cache\" ] ); } else { utime time(), time(), "/tmp/$cache"; # update last modification time of the cache } if( $cmd eq 'x' ) { for( @ARGV ) { if( /^\@(.+)/ ) { fix_list_names( $1 ); } else { s/^(?!\.\/)/.\//; } } } system( "/usr/libexec/vfu/rx_tar", $cmd, "/tmp/$cache", @ARGV ); } else { die $0 . ": wrong command.\n"; } sub fix_list_names { my $fn = shift; # file name my @l = file_load_ar( $fn ); s/^(?!\.\/)/.\// for @l; file_save( $fn, @l ); return 1; } sub file_load_ar { my $fn = shift; # file name open( my $i, "<", $fn ) or return undef; my @all = <$i>; close $i; return @all; } sub file_save { my $fn = shift; # file name open( my $o, ">", $fn ) or return 0; print $o @_; close $o; return 1; } vfu-5.09/rx/rx_auto0000755000175000017500000000267114372250714012674 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # http://cade.webbg.com # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; my $CTTL = 16; # cache time to live in seconds my $file = $ARGV[1]; for( glob "/tmp/*.rx.cache" ) { # clean cache--silently skip errors next unless time() - file_mtime( $_ ) > $CTTL; unlink $_; } my $rx = choose( $file ) or die "$0: this file type is not known to me, sorry\n"; exec_if_exists( "/usr/libexec/vfu/$rx", @ARGV ); exec_if_exists( "$ENV{HOME}/bin/$rx", @ARGV ); die "$0: cannot find and/or execute [$rx]\n"; sub choose { local $_ = shift; return "rx_tar" if /\.(tar(\.(z|gz|xz|bz2|zst))?|t[bgx]z)$/i; return "rx_zip" if /\.(zip|jar|pk3|egg|maff)$/i; return "rx_deb" if /\.deb$/i; return "rx_ftp" if /\.ftp$/i; return "rx_rar" if /\.rar$/i; return "rx_rpm" if /\.rpm$/i; return undef; } sub file_mtime { return (stat($_[0]))[9]; } sub exec_if_exists { my $rxbin = shift; return undef unless -x $rxbin; exec( $rxbin, @_ ); } vfu-5.09/rx/rx_rar0000755000175000017500000000412214145574023012501 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $cache = "/tmp/$archive.rx.cache"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift @ARGV; if ( $dir ) { $dir .= "/" unless $dir =~ /\/$/; } if( ! -e $cache ) { # cache not found--fill it system( qq[ umask 0077 ; unrar v "$archive" > "$cache" ] ); } else { utime time(), time(), $cache; # update last modification time of the cache } my $in = 0; open( i, $cache ); while() { $in = ! $in and next if/^[\- ]{16,}$/; next unless $in; chop; s/^\s+//; my @D; push @D, $_; $_ = ; chop; s/^\s+//; push @D, split /\s+/; my $N = $D[0]; # name if ( $cmd eq "l" ) { next unless $N =~ s/^$dir([^\/]+\/?)$//; $N = $1; } my $S = $D[1]; my $T = $D[4] . $D[5]; $T =~ s/(\d\d)-(\d\d)-(\d\d)(\d\d):(\d\d)/$3$2$1$4$5/; $T = ( $3 < 70 ? '20' : '19' ) . $T; my $M = $D[6]; $N .= '/' if $M =~ /D/; print "NAME:$N\nSIZE:$S\nTIME:$T\nMODE:$M\n\n"; } close( i ); } elsif ( $cmd eq "x" ) { my $list; if ( $ARGV[0] =~ /^\@(.+)$/ ) { $list = $1; } else { $list = "/tmp/$$.rx.list"; sysopen $fo, $list, O_CREAT | O_EXCL, 0600; print $fo "$_\n" for @ARGV; close $fo; } system( "unrar x $archive \@$list 1> /dev/null 2> /dev/null" ); unlink $list; } else { die $0 . ": wrong command.\n"; } vfu-5.09/rx/rx_zip0000755000175000017500000000410314145574023012516 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # http://cade.webbg.com # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; use POSIX; # umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $cache = "/tmp/$archive.rx.cache"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift @ARGV; if ( $dir ) { $dir .= "/" unless $dir =~ /\/$/; } if( ! -e $cache ) { # cache not found--fill it system( "unzip -l '$archive' > '$cache'" ); chmod oct(600), $cache; # a bit late but still... :) } else { utime time(), time(), $cache; # update last modification time of the cache } my $in = 0; open( i, $cache ); while() { $in = ! $in and next if/^[\- ]{16,}$/; next unless $in; chop; s/\s+->\s+\S+$//; # no symlinks support? my @D = split /\s+/, $_, 5; my $N = $D[4]; # name if ( $cmd eq "l" ) { next unless $N =~ s/^$dir([^\/]+\/?)$//; $N = $1; } my $S = $D[1]; my $T = $D[2] . $D[3]; $T =~ s/(\d\d)-(\d\d)-(\d\d)(\d\d):(\d\d)/$3$2$1$4$5/; $T = ( $3 < 70 ? '20' : '19' ) . $T; print "NAME:$N\nSIZE:$S\nTIME:$T\n\n"; } close( i ); } elsif ( $cmd eq "x" ) { my $list; if ( $ARGV[0] =~ /^\@(.+)$/ ) { $list = $1; } else { $list = "/tmp/$$.rx.list"; sysopen my $fo, $list, O_CREAT | O_EXCL, 0600; print $fo "'$_'\n" for @ARGV; close $fo; } system( "unzip $archive `cat $list` 1> /dev/null 2> /dev/null" ); unlink $list; } else { die $0 . ": wrong command.\n"; } vfu-5.09/rx/rx_tar0000755000175000017500000001057314372250714012512 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # http://cade.webbg.com # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; use POSIX; umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $cache = "/tmp/$archive.rx.cache"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift @ARGV; if( ! -e $cache ) { # cache not found--fill it system( qq[ umask 0077; tar tvf "$archive" > "$cache" ] ) if $archive =~ /\.tar$/i; system( qq[ umask 0077; gzip -dc "$archive" | tar tvf - > "$cache" ] ) if $archive =~ /\.tar\.g?z(\.rx\.cache)?$/i; system( qq[ umask 0077; gzip -dc "$archive" | tar tvf - > "$cache" ] ) if $archive =~ /\.tgz$/i; system( qq[ umask 0077; xz -dc "$archive" | tar tvf - > "$cache" ] ) if $archive =~ /(\.txz|\.tar\.xz(\.rx\.cache)?)$/i; system( qq[ umask 0077; bzip2 -dc "$archive" | tar tvf - > "$cache" ] ) if $archive =~ /\.tar\.bz2?$/i; system( qq[ umask 0077; zstd -dc "$archive" | tar tvf - > "$cache" ] ) if $archive =~ /\.tar\.zst$/i; } else { utime time(), time(), $cache; # update last modification time of the cache } my $content = read_archive( $cache ); # use Data::Dumper; # print Dumper( $content ); if ( $cmd eq "l" ) { $dir .= "/" unless $dir =~ /\/$/; } else { $dir = '*'; } exit unless exists $content->{ $dir }; for my $e ( keys %{ $content->{ $dir } } ) { my %E = %{ $content->{ $dir }{ $e } }; print "NAME:$E{ NAME }\nSIZE:$E{ SIZE }\nMODE:$E{ MODE }\nTIME:$E{ TIME }\n\n"; } } elsif ( $cmd eq "x" ) { my $list; if ( $ARGV[0] =~ /^\@(.+)$/ ) { $list = $1; } else { $list = "/tmp/rx.$$.rxlist"; sysopen my $fo, $list, O_CREAT | O_EXCL | O_RDWR, 0600; print $fo "$_\n" for @ARGV; print STDERR "[$_]\n" for @ARGV; close $fo; } system( qq[ tar xvf "$archive" -T "$list" ] ) if $archive =~ /\.tar$/i; system( qq[ gzip -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.tar\.g?z(\.rx\.cache)?$/i; system( qq[ gzip -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.tgz$/i; system( qq[ xz -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.tar\.xz(\.rx\.cache)?$/i; system( qq[ xz -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.txz$/i; system( qq[ bzip2 -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.tar\.bz2?$/i; system( qq[ zstd -dc "$archive" | tar xvf - -T "$list" ] ) if $archive =~ /\.tar\.zst$/i; unlink $list; } else { die $0 . ": wrong command.\n"; } sub read_archive { my $cache_fn = shift; my %C; open( my $i, $cache_fn ); while(<$i>) { chop; s/\s+->\s+\S+?$//; # no symlinks support? my @D = split /\s+/, $_, 6; my $N = $D[5]; # name my $M = $D[0]; # mode #use Data::Dumper; #print STDERR Dumper( $_, \@D, $N ); # strip leading /s $N =~ s/^\.\///; $N =~ s/^\//\//; $N =~ s/\/$//; my $F = $N; # full name, before path split my $P; # parent if( $N =~ /^(.+?\/)([^\/]+)$/ ) { $P = $1; $N = $2; } my $T = "$D[3]$D[4]"; # time $T =~ s/[\-\s\:]//g; $T = substr( $T, 0, 12 ); my %E; $E{ NAME } = $N; $E{ SIZE } = $D[2]; $E{ MODE } = $M; $E{ TIME } = $T; $C{ $P }{ $N } = \%E; $C{ '*' }{ $F } = { %E, NAME => $F }; } close( $i ); # preprocessing missing dirs for my $p ( keys %C ) { next if $p eq '*'; $p =~ s/\/$//; my @p = split /\//, $p; my $path; while( @p ) { my $next = shift @p; if( ! exists $C{ $path }{ $next } ) { my %E; $E{ NAME } = "$next/"; $E{ SIZE } = 0; $E{ MODE } = "dr-xr-xr-x"; $E{ TIME } = "197101010000"; $C{ $path }{ $next } = \%E; } $path .= "$next/"; } } $C{ '/' } = $C{ '' }; return \%C; } vfu-5.09/rx/rxx0000755000175000017500000001355514371547215012043 0ustar cadecade#!/usr/bin/perl ############################################################################# # # RXX -- abstract archiver interface # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # http://cade.webbg.com # based on rx_* dispatcher and handlers for VFU File Manager # # usage: # rxx l archive directory # list archive directory # rxx v archive # list entire archive # rxx x archive files... # extract one file # rxx x archive @listfile # extract list of files # ############################################################################# use strict; umask 0077; my $CACHE_TTL = 16; # cache time to live in seconds ############################################################################# # # DISPATCHER # ############################################################################# my $file = $ARGV[1]; for( glob "/tmp/*.rxx.tmp" ) { # clean cache and tmp files, silently skipping errors next unless time() - file_mtime( $_ ) > $CACHE_TTL; unlink $_; } my $rx = rx_choose( $file ); my %RX_MAP = ( 'rx_tar' => \&rx_tar, ); my $rx_func = $RX_MAP{ $rx }; die "$0: this file type is not known to me, sorry\n" unless $rx_func; # FIXME: getopt? $rx_func->( @ARGV ); sub rx_choose { local $_ = shift; return "rx_tar" if /\.(tar(\.(z|gz|bz2))?|t[bgx]z)$/i; return "rx_zip" if /\.(zip|jar|pk3|egg|maff)$/i; return "rx_deb" if /\.deb$/i; return "rx_ftp" if /\.ftp$/i; return "rx_rar" if /\.rar$/i; return "rx_rpm" if /\.rpm$/i; } ############################################################################# # # TAR # ############################################################################# sub rx_tar { my $cmd = lc shift; my $archive = shift; my $cache = "/tmp/$archive.cache.rxx.tmp"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; # strip middle path if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift; if( ! -e $cache ) { # cache not found--fill it system( "tar xvf \"$archive\" > \"$cache\"" ) if $archive =~ /\.tar$/i; system( "gzip -dc \"$archive\" | tar tvf - > \"$cache\"" ) if $archive =~ /\.tar\.g?z(\.rx\.cache)?$/i; system( "gzip -dc \"$archive\" | tar tvf - > \"$cache\"" ) if $archive =~ /\.tgz$/i; system( "xz -dc \"$archive\" | tar tvf - > \"$cache\"" ) if $archive =~ /\.txz$/i; system( "bzip2 -dc \"$archive\" | tar tvf - > \"$cache\"" ) if $archive =~ /\.tar\.bz2?$/i; chmod oct(600), $cache; # a bit late but still... :) } else { utime time(), time(), $cache; # update last modification time of the cache } my $content = rx_tar_read_archive( $cache ); if ( $cmd eq "l" ) { $dir .= "/" unless $dir =~ /\/$/; $dir =~ s/^\/(.+)$/$1/; # strip leading /, FIXME: make func } else { $dir = '*'; } exit unless exists $content->{ $dir }; for my $e ( keys %{ $content->{ $dir } } ) { my %E = %{ $content->{ $dir }{ $e } }; print "NAME:$E{ NAME }\nSIZE:$E{ SIZE }\nMODE:$E{ MODE }\nTIME:$E{ TIME }\n\n"; } } elsif ( $cmd eq "x" ) { my $list_fn; if ( $_[0] =~ /^\@(.+)$/ ) # FIXME: handle @_ { $list_fn = $1; } else { $list_fn = "/tmp/$$.list.rxx.tmp"; sysopen my $fo, $list_fn, O_CREAT | O_EXCL, 0600; for my $entry ( @_ ) # FIXME: handle @_ { next if $entry eq '/'; next if $entry eq '.'; $entry =~ s/^\/(.+)$/$1/; # strip leading /, FIXME: make func print $fo "$entry\n"; } close $fo; } system( " tar xvf $archive -T $list_fn" ) if $archive =~ /\.tar$/i; system( "gzip -dc $archive | tar xvf - -T $list_fn" ) if $archive =~ /\.tar\.g?z(\.rx\.cache)?$/i; system( "gzip -dc $archive | tar xvf - -T $list_fn" ) if $archive =~ /\.tgz$/i; system( "xz -dc $archive | tar xvf - -T $list_fn" ) if $archive =~ /\.txz$/i; system( "bzip2 -dc $archive | tar xvf - -T $list_fn" ) if $archive =~ /\.tar\.bz2?$/i; unlink $list_fn; #print "gzip -dc $archive | tar xvf - -T $list"; } else { die $0 . ": wrong command.\n"; } } sub rx_tar_read_archive { my $cache_fn = shift; my %C; open( my $i, $cache_fn ); while(<$i>) { chop; s/\s+->\s+\S+$//; # no symlinks support? my @D = split /\s+/; my $N = $D[5]; # name my $M = $D[0]; # mode # strip leading /s $N =~ s/^\.\///; $N =~ s/^\//\//; $N =~ s/\/$//; my $F = $N; # full name, before path split my $P; # parent if( $N =~ /^(.+?\/)([^\/]+)$/ ) { $P = $1; $N = $2; } my $T = "$D[3]$D[4]"; # time $T =~ s/[\-\s\:]//g; $T = substr( $T, 0, 12 ); my %E; $E{ NAME } = $N; $E{ SIZE } = $D[2]; $E{ MODE } = $M; $E{ TIME } = $T; $C{ $P }{ $N } = \%E; $C{ '*' }{ $F } = { %E, NAME => $F }; } close( $i ); # preprocessing missing dirs for my $p ( keys %C ) { next if $p eq '*'; $p =~ s/\/$//; my @p = split /\//, $p; my $path; while( @p ) { my $next = shift @p; if( ! exists $C{ $path }{ $next } ) { my %E; $E{ NAME } = "$next/"; $E{ SIZE } = 0; $E{ MODE } = "dr-xr-xr-x"; $E{ TIME } = "197101010000"; $C{ $path }{ $next } = \%E; } $path .= "$next/"; } } $C{ '/' } = $C{ '' }; return \%C; } ############################################################################# # # UTILS # ############################################################################# sub file_mtime { return (stat($_[0]))[9]; } ############################################################################# # # EOF # ############################################################################# vfu-5.09/rx/jane.ftp0000644000175000017500000000002014145574023012677 0ustar cadecadelocalhost - - - vfu-5.09/rx/rx_rpm0000755000175000017500000000407114145574023012516 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_rpm handler for VFU File Manager # (c) 2005 Ralph Muller # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# use strict; umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $cache = "/tmp/$archive.rx.cache"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift @ARGV; if ( $dir ) { $dir .= "/" unless $dir =~ /\/$/; } if( ! -e $cache ) { # cache not found--fill it system( qq[ umask 0077 ; rpm2cpio "$archive" | cpio -tv --quiet > "$cache" ] ); } else { utime time(), time(), $cache; # update last modification time of the cache } my $in = 0; open( my $i, $cache ); while(<$i>) { # $in = ! $in and next if/^[\- ]{16,}$/; # FixMe # next unless $in; chomp; s/\s+->\s+\S+$//; # no symlinks support? my @D = split /\s+/; my $N = $D[8]; # name # if ( $cmd eq "l" ) # { # next unless $N =~ s/^$dir([^\/]+\/?)$//; # FixMe # $N = $1; # } my $S = $D[4]; # my $T = $D[5] . " " . $D[6] . " " . $D[7]; # FixMe # $T =~ s/(\d\d)-(\d\d)-(\d\d)(\d\d):(\d\d)/$3$2$1$4$5/; # $T = ( $3 < 70 ? '20' : '19' ) . $T; # print "NAME:$N\nSIZE:$S\nTIME:$T\n\n"; print "NAME:$N\nSIZE:$S\n\n"; } close( $i ); } elsif ( $cmd eq "x" ) { my $list; if ( $ARGV[0] =~ /^\@(.+)$/ ) { $list = $1; } else { $list = "/tmp/$$.rx.list"; sysopen my $fo, $list, O_CREAT | O_EXCL, 0600; print $fo "$_\n" for @ARGV; close $fo; } system( "rpm2cpio $archive | cpio -iumd --quiet `cat $list`" ); unlink $list; } else { die $0 . ": wrong command.\n"; } vfu-5.09/rx/aenea.ftp0000644000175000017500000000001414145574023013036 0ustar cadecade192.168.1.1 vfu-5.09/rx/rx_ftp0000755000175000017500000000432414145574023012512 0ustar cadecade#!/usr/bin/perl ############################################################################# # # rx_* dispatcher and handlers for VFU File Manager # Copyright (c) 2002-2020 Vladi Belperchinov-Shabanski "Cade" # http://cade.webbg.com # # usage: # rx_* l archive directory # list archive directory # rx_* v archive # list entire archive # rx_* x archive files... # extract one file # rx_* x archive @listfile # extract list of files # ############################################################################# # # BUGS: # - cannot show recursively entire site # - cannot copy/extract directories # # TODO: # - cache? # ############################################################################# use strict; use Net::FTP; umask 0077; my $cmd = lc shift @ARGV; my $archive = shift @ARGV; my $cache = "/tmp/$archive.rx.cache"; $cache =~ s/^(\/tmp\/)(.+)\/([^\/]+)$/$1$3/; my $i; open $i, $archive; my $host = <$i>; my $user = <$i>; my $pass = <$i>; close $i; chop($host); chop($user); chop($pass); my $ftp = Net::FTP->new( $host ) or die "$0: cannot connect to $host\n"; $user = 'anonymous' if $user eq '-'; $pass = "$ENV{USER}\@$ENV{HOSTNAME}" if $pass eq '-'; $ftp->login( $user, $pass ) or die "$0: cannot login as $user into $host\n"; if ( $cmd eq "l" || $cmd eq "v" ) { my $dir = shift @ARGV; $ftp->cwd( $dir ); my @list = $ftp->dir(); for( @list ) { my @D = split /\s+/, $_; my $N = pop @D; my $M = $D[0]; my $S = $D[4]; $N .= '/' if $M =~ /^d/i; print "NAME:$N\nSIZE:$S\nMODE:$M\n\n"; } } elsif ( $cmd eq "x" ) { my @list; if ( $ARGV[0] =~ /^\@(.+)$/ ) { my $i; open $i, $1; @list = <$i>; close $i; chop( @list ); } else { @list = @ARGV; } for my $e ( @list ) { $e =~ s/^\///; my $p; my $l; if( $e =~ /^(.+\/)([^\/]+)$/ ) { $l = $2; $p = $1; }; mkpath( $p ); # print "$e:$p:$l\n"; $ftp->get( $e, "$p$l" ); } } else { die $0 . ": wrong command.\n"; } sub mkpath { my $p = shift; my @p = split /\//, $p; my $cp; while( @p ) { $cp .= shift( @p ) . '/'; mkdir $cp; print ">>$cp\n"; } } vfu-5.09/rx/AUTHORS0000644000175000017500000000021414145574023012324 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-5.09/rx/README0000644000175000017500000000222714145574023012142 0ustar cadecade RX_* TOOLS README This is preliminary documentation to the `RX_*' utilities. The main purpose of rx_* is to provide standard archive-like interface to various containers (archives, ftp sites, etc). It is mainly used with VFU file manager which can be found at http://cade.datamax.bg/vfu. `FTP' archives contain just info how to connect to the required site. example1: text file named: soul.ftp ---cut--- soul.teti.org cade secretpassword ---cut--- command line: rx_auto v soul.ftp /pub will show all files in the `/pub' directory at the FTP site soul.teti.org. you can use dash `-' as username and password to mimic anonymous access: example2: text file named: cade.jane.org ---cut--- jane.teti.org - - ---cut--- command line: rx_ftp x cade.jane.ftp pub/welcome.txt will extract/download welcome.txt file from he pub directory at the jane.teti.org FTP site. username will be `anonymouse' and password will be `current_username@current_hostname' (from the environment) For more details and if you have problems contact me: Vladi Belperchinov-Shabanski Good luck! vfu-5.09/README.MAC_OSX0000644000175000017500000000100014372204606012624 0ustar cadecade Hello, this is very quick note on how to compile VFU on Mac OS X: 1. install Xcode 2. get vfu-X.XX.tar.gz 3. tar xzvf vfu-X.XX.tar.gz 4. cd vfu-X.XX 5. export export CCDEF="-I../vslib/pcre" 6. export LDDEF="-L../vslib/pcre -lpcre" 7. make -C vslib 8. make -C vfu now you should have VFU ready inside vfu dir: /home/cade/vfu-X.XX/vfu/vfu if you have questions or problems with VFU build procedure, please, contact me at these email addresses: cade@bis.bg cade@noxrun.com vfu-5.09/XWINDOW.NOTES0000644000175000017500000000253314372204606012601 0ustar cadecade This file contains some hints for using VFU under XWindow, i.e. in a xterm, rxvt or other compatible(?) terminal. The only solution I come up to use VFU with colors, keys etc. under xterm is to set terminal type to `linux', i.e. console: export TERM=linux or export TERM=rxvt (if you use rxvt like me:)) It works fine here, so try it. I suggest you create script like this: ---cut--- #!/bin/bash # # vfux -- script for starting vfu under xterm # export TERM=linux vfu ---cut--- If you still have problems (wrong keys,missing sequences,etc.) you can try a modified terminfo definition (based on linux-c) which I use. Check the `terminfo' directory in the vfu package. If you want to use keypad for navigation (arrows, +, -, ...) and your keypad is mapped to numbers (KP_1 ... KP_9, etc.) you have to add these lines to your Xmodmap ( usually located at /var/X11R6/lib/xinit/.Xmodmap ) ---cut--- keycode 63 = asterisk keycode 112 = slash keycode 108 = Return keycode 79 = Home keycode 80 = Up keycode 81 = Page_Up keycode 82 = minus keycode 83 = Left keycode 85 = Right keycode 86 = plus keycode 87 = End keycode 88 = Down keycode 89 = Page_Down keycode 90 = Insert keycode 91 = Delete ---cut--- If you have problems contact me at vfu-5.09/vfu-get-latest-git-snapshot.pl0000755000175000017500000000235514151762146016462 0ustar cadecade#!/usr/bin/perl ############################################################################## # # Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" # ############################################################################## use strict; my $dest = shift; die "need accessible target directory as 1st arg\n" unless $dest and chdir( $dest ); clone_or_update( "https://github.com/cade-vs/vfu-dist.git" ); chdir( 'vfu-dist' ); clone_or_update( "https://github.com/cade-vs/vfu.git" ); clone_or_update( "https://github.com/cade-vs/vslib.git" ); clone_or_update( "https://github.com/cade-vs/vstring.git" ); clone_or_update( "https://github.com/cade-vs/yascreen.git" ); status( "done." ); sub clone_or_update { my $repo = shift; my $dir = $1 if $repo =~ /([^\/]+)\.git$/; die "cannot figure dir name from repo [$repo]" unless $dir; if( -e $dir ) { status( "updating $repo" ); chdir( $dir ); system qq[ git pull origin master ]; chdir( '..' ); } else { status( "cloning $repo" ); system qq[ git clone $repo ]; } return 1; } sub status { my $s = shift; print( "\n" . ('-' x 16) . ' ' . $s . "\n" ); } ###EOF######################################################################## vfu-5.09/vslib/0000755000175000017500000000000014444676600011753 5ustar cadecadevfu-5.09/vslib/unicon.cpp0000644000175000017500000002554314367022260013752 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "unicon.h" /**************************************************************************** ** ** UNIX Part ** ****************************************************************************/ #ifdef _UNICON_USE_CURSES_ /**************************************************************************** ** This part is loosely based on `linconio': ** --------------------------------------------------------------------- ** File: conio.h Date: 03/09/1997 Version: 1.02 ** CONIO.H an implementation of the conio.h for Linux based on ncurses. ** This is copyright (c) 1996,97 by Fractor / Mental EXPlosion. ** If you want to copy it you must do this following the terms of the ** GNU Library Public License ** Please read the file "README" before using this library. ****************************************************************************/ int __fg; int __bg; int __ta; WINDOW *conio_scr; #define CON_PAIR(f,b) (((b)*8)+(f)+1) /* Some internals... */ int colortab(int a) /* convert UNIX/Curses Color code to DOS-standard */ { switch(a) { case cBLACK : return COLOR_BLACK; case cBLUE : return COLOR_BLUE; case cGREEN : return COLOR_GREEN; case cCYAN : return COLOR_CYAN; case cRED : return COLOR_RED; case cMAGENTA : return COLOR_MAGENTA; case cYELLOW : return COLOR_YELLOW; case cWHITE : return COLOR_WHITE; } return 0; } int con_init() { initscr(); start_color(); qiflush(); cbreak(); if ( !getenv("UNICON_NO_RAW") ) { /* To allow curses app to handle properly ctrl+z, ctrl+c, etc. I should not call raw() here, anyway it is known that this will cause misinterpretation of some keys (arrows) after resuming (SUSP -- ctrl+z)... So, unless UNICON_NO_RAW exported and set to any value raw() is called as usual. */ raw(); } noecho(); nonl(); // `return' translation off if (!has_colors()) fprintf(stderr,"Attention: A color terminal may be required to run this application !\n"); conio_scr=newwin(0,0,0,0); keypad(conio_scr,TRUE); // allow function keys (keypad) meta(conio_scr,TRUE); // switch to 8 bit terminal return values // intrflush(conio_scr,FALSE); // idlok(conio_scr,TRUE); // hardware line ins/del (required?) idcok(conio_scr,TRUE); // hardware char ins/del (required?) // nodelay(conio_scr,FALSE); // blocking getch() scrollok(conio_scr,TRUE); // scroll screen if required (cursor at bottom) /* Color initialization */ for ( __bg=0; __bg<8; __bg++ ) for ( __fg=0; __fg<8; __fg++ ) init_pair( CON_PAIR(__fg,__bg), colortab(__fg), colortab(__bg)); con_ta(7); return 0; } void con_done() { delwin(conio_scr); endwin(); } void con_suspend() { con_done(); } void con_restore() { con_init(); } void con_reset_screen_size() { //ungetch( KEY_RESIZE ); //nodelay(conio_scr,TRUE); // non-blocking getch() } void con_ta( int attr ) { __ta = attr; wattrset(conio_scr,0); /* (???) My curses-version needs this ... */ __fg = COLORFG(attr); __bg = COLORBG(attr); wattrset(conio_scr,COLOR_PAIR(CON_PAIR( __fg%8, __bg%8 )) | ( __bg > 7 )*(A_BLINK) | ( __fg > 7 )*(A_BOLD) ); wbkgdset( conio_scr, COLOR_PAIR(CON_PAIR( __fg%8, __bg%8 )) ); } void con_ce( int attr ) { if (attr != -1) { int ta = __ta; con_ta( attr ); wclrtoeol(conio_scr); wrefresh(conio_scr); con_ta( ta ); } else { wclrtoeol(conio_scr); wrefresh(conio_scr); } } void con_cs( int attr ) { if (attr != -1) { int ta = __ta; con_ta( attr ); wclear(conio_scr); wmove(conio_scr,0,0); wrefresh(conio_scr); con_ta( ta ); } else { wclear(conio_scr); wmove(conio_scr,0,0); wrefresh(conio_scr); } } void con_puts( const char *s ) { waddstr(conio_scr,s); wrefresh(conio_scr); } int con_max_x() { return getmaxx(conio_scr); } int con_max_y() { return getmaxy(conio_scr); } int con_x() { return getcurx(conio_scr)+1; } int con_y() { return getcury(conio_scr)+1; } void con_fg( int color ) { __fg=color; con_ta( CONCOLOR( __fg, __bg ) ); } void con_bg( int color ) { __bg=color; con_ta( CONCOLOR( __fg, __bg ) ); } void con_xy( int x, int y ) { wmove(conio_scr,y-1,x-1); wrefresh(conio_scr); } void con_chide() { con_xy( 1, 1 ); leaveok(conio_scr,TRUE); curs_set( 0 ); } void con_cshow() { leaveok(conio_scr,FALSE); curs_set( 1 ); } int con_kbhit() { int i; nodelay(conio_scr,TRUE); i=wgetch(conio_scr); nodelay(conio_scr,FALSE); if (i==-1) return 0; else ungetch(i); return(i); } int con_getch() { int i = wgetch(conio_scr); if( i == -1 ) i = 0; if( i == 27 ) if( con_kbhit() ) i = UKEY_ALT_PREFIX + wgetch(conio_scr); return( i > 0xFF ? UKEY_WIDE(i) : i ); } void con_beep() { printf( "\007" ); fflush( stdout ); } wchar_t con_getwch() { wchar_t wch = 0; char s[6]; int z = 0; s[z] = 0; int ch = con_getch(); if( UKEY_IS_WIDE_CTRL( ch ) ) return ch; // control key int x = -1; if( (ch & 0x80) == 0x00 ) x = 0; else if( (ch & 0xE0) == 0xC0 ) x = 1; else if( (ch & 0xF0) == 0xE0 ) x = 2; else if( (ch & 0xF8) == 0xF0 ) x = 3; s[z++] = ch; s[z] = 0; while( x > 0 ) { ch = con_getch(); if( (ch & 0xC0) != 0x80 ) return -1; s[z++] = ch; s[z] = 0; x--; } if( mbtowc( &wch, s, MB_CUR_MAX ) ) { } return wch; } #elif defined(_UNICON_USE_YASCREEN_) #include #include int __fg; int __bg; int __ta; int __x=0; int __y=0; uint32_t __attr=0; yascreen *ya_s=NULL; /* Some internals... */ int colortab(int a) /* convert UNIX/Curses Color code to DOS-standard */ { switch(a) { case cBLACK : return YAS_BLACK; case cBLUE : return YAS_BLUE; case cGREEN : return YAS_GREEN; case cCYAN : return YAS_CYAN; case cRED : return YAS_RED; case cMAGENTA : return YAS_MAGENTA; case cYELLOW : return YAS_YELLOW; case cWHITE : return YAS_WHITE; } return 0; } void con_yas_sigwinch( int sig __attribute__((unused)) ) { signal( SIGWINCH, con_yas_sigwinch ); // (re)setup signal handler con_reset_screen_size(); } int con_init() { signal( SIGWINCH, con_yas_sigwinch ); // (re)setup signal handler if (ya_s) { yascreen_term_set(ya_s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO); if (-1==yascreen_resize(ya_s,0,0)) yascreen_resize(ya_s,80,25); yascreen_altbuf(ya_s,1); yascreen_cursor(ya_s,0); con_ta(7); yascreen_redraw(ya_s); return 0; } ya_s=yascreen_init(0,0); if (!ya_s) ya_s=yascreen_init(80,25); if (!ya_s) return 1; yascreen_term_set(ya_s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO); yascreen_altbuf(ya_s,1); yascreen_cursor(ya_s,0); con_ta(7); return 0; } void con_done() { yascreen_clear(ya_s); yascreen_altbuf(ya_s,0); yascreen_cursor(ya_s,1); yascreen_term_restore(ya_s); yascreen_free(ya_s); ya_s=NULL; } void con_suspend() { con_done(); } void con_restore() { con_init(); yascreen_update(ya_s); } void con_reset_screen_size() { yascreen_pushch(ya_s,YAS_SCREEN_SIZE); } void con_ta( int attr ) { __ta = attr; __fg = COLORFG(attr); __bg = COLORBG(attr); __attr = YAS_FGCOLOR(colortab(__fg%8))|YAS_BGCOLOR(colortab(__bg%8))|((__bg>7)*YAS_ITALIC)|((__fg>7)*YAS_BOLD); } void con_ce( int attr ) { if (attr != -1) { int ta = __ta; con_ta( attr ); yascreen_printxyu(ya_s,__x,__y,__attr,"%*s",yascreen_sx(ya_s),""); con_ta( ta ); } else { yascreen_printxyu(ya_s,__x,__y,__attr,"%*s",yascreen_sx(ya_s),""); } } void con_cs( int attr ) { if (attr != -1) { int ta = __ta; con_ta( attr ); yascreen_clear_mem(ya_s,__attr); __x=__y=0; yascreen_update(ya_s); con_ta( ta ); } else { yascreen_clear_mem(ya_s,__attr); __x=__y=0; yascreen_update(ya_s); } } void con_puts( const char *s ) { yascreen_putsxyu(ya_s,__x,__y,__attr,s); __x=yascreen_x(ya_s); __y=yascreen_y(ya_s); } int con_max_x() { return yascreen_sx(ya_s); } int con_max_y() { return yascreen_sy(ya_s); } int con_x() { return(__x+1); } int con_y() { return(__y+1); } void con_fg( int color ) { __fg=color; con_ta( CONCOLOR( __fg, __bg ) ); } void con_bg( int color ) { __bg=color; con_ta( CONCOLOR( __fg, __bg ) ); } void con_xy( int x, int y ) { __x=x-1; __y=y-1; yascreen_cursor_xy(ya_s,__x,__y); } void con_chide() { con_xy( 1, 1 ); yascreen_cursor(ya_s,0); } void con_cshow() { yascreen_cursor(ya_s,1); } int con_kbhit() { int i = yascreen_peekch(ya_s); if( i == YAS_K_NONE ) i = 0; return(i); } int con_getch() { int i = yascreen_getch( ya_s ); if( i == YAS_K_NONE ) i = 0; return( i ); } void con_beep() { printf( "\007" ); fflush( stdout ); } wchar_t con_getwch() { wchar_t w = yascreen_getwch( ya_s ); if( w == YAS_K_NONE ) w = 0; return( w ); } #else #error One of ncurses/yascreen libraries is required under UNIX #error use compile time -D_UNICON_USE_YASCREEN_ or #error use compile time -D_UNICON_USE_CURSES_ to select wanted library #endif /**************************************************************************** ** ** COMMON Part ** ****************************************************************************/ void con_out( int x, int y, const char *s ) { con_out( x, y, s, __ta ); } void con_out( int x, int y, const char *s, int attr ) { int ta = __ta; con_ta( attr ); con_xy( x, y ); con_puts( s ); con_ta( ta ); } void con_puts( const char *s, int attr ) { int ta = __ta; con_ta( attr ); con_puts( s ); con_ta( ta ); } /**************************************************************************** ** EOF ****************************************************************************/ vfu-5.09/vslib/unicon.h0000644000175000017500000003242314367022260013412 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _UNICON_H_ #define _UNICON_H_ #include #include "target.h" #if defined(_UNICON_USE_CURSES_) #include #elif defined(_UNICON_USE_YASCREEN_) #include #else #error One of ncurses/yascreen libraries is required under UNIX #error use compile time -D_UNICON_USE_YASCREEN_ or #error use compile time -D_UNICON_USE_CURSES_ to select wanted library #endif /**************************************************************************** ** ** COLOR defines ** ****************************************************************************/ #define CONCOLOR(f,b) (b*16+f) #define COLORFG(t) (t % 16) #define COLORBG(t) (t / 16) /***** std-colors/normal ******/ #define cNORMAL 7 #define cBOLD 8 #define cREVERSED CONCOLOR(cBLACK,cWHITE) /***** low-colors/normal ******/ #define cBLACK 0 #define cBLUE 1 #define cGREEN 2 #define cCYAN 3 #define cRED 4 #define cMAGENTA 5 #define cBROWN 6 #define cYELLOW 6 #define cLGRAY 7 /***** hi-colors/bright *******/ #define chBLACK 7 #define cWHITE 7 #define cDGRAY 8 #define chBLUE 9 #define chGREEN 10 #define chCYAN 11 #define chRED 12 #define chMAGENTA 13 #define chYELLOW 14 #define chWHITE 15 /**************************************************************************** ** ** KEY defines ** ****************************************************************************/ /******* common ************************************************************/ #define UKEY_WIDE_CTRL_PREFIX 0xF0000 #define UKEY_WIDE_CTRL_PREFIX_END 0xFFFFD #define UKEY_WIDE(k) (UKEY_WIDE_CTRL_PREFIX+(k)) #define UKEY_IS_WIDE_CTRL(k) (((k)>=UKEY_WIDE_CTRL_PREFIX)&&((k)<=UKEY_WIDE_CTRL_PREFIX_END)) #define UKEY_CTRL_A 1 #define UKEY_CTRL_B 2 #define UKEY_CTRL_C 3 #define UKEY_CTRL_D 4 #define UKEY_CTRL_E 5 #define UKEY_CTRL_F 6 #define UKEY_CTRL_G 7 #define UKEY_CTRL_H 8 #define UKEY_CTRL_I 9 #define UKEY_CTRL_J 10 #define UKEY_CTRL_K 11 #define UKEY_CTRL_L 12 #define UKEY_CTRL_M 13 #define UKEY_CTRL_N 14 #define UKEY_CTRL_O 15 #define UKEY_CTRL_P 16 #define UKEY_CTRL_Q 17 #define UKEY_CTRL_R 18 #define UKEY_CTRL_S 19 #define UKEY_CTRL_T 20 #define UKEY_CTRL_U 21 #define UKEY_CTRL_V 22 #define UKEY_CTRL_W 23 #define UKEY_CTRL_X 24 #define UKEY_CTRL_Y 25 #define UKEY_CTRL_Z 26 #define UKEY_TAB 9 #define UKEY_ENTER 13 #define UKEY_ESC 27 /******* DJGPP/DOS dropped on 2023.JAN.03 *******************************/ #ifdef _TARGET_GO32_ #error DJGPP/GO32/DOS Suppot was dropped on 2023.JAN.03 #error use older version or contact me at /******* UNIX/NCURSES ******************************************************/ #elif defined(_UNICON_USE_CURSES_) #define UKEY_INS UKEY_WIDE(KEY_IC) #define UKEY_BACKSPACE UKEY_WIDE(KEY_BACKSPACE) #define UKEY_LEFT UKEY_WIDE(KEY_LEFT) #define UKEY_RIGHT UKEY_WIDE(KEY_RIGHT) #define UKEY_UP UKEY_WIDE(KEY_UP) #define UKEY_DOWN UKEY_WIDE(KEY_DOWN) #define UKEY_HOME UKEY_WIDE(KEY_HOME) #define UKEY_END UKEY_WIDE(KEY_END) #define UKEY_DEL UKEY_WIDE(KEY_DC) #define UKEY_PGUP UKEY_WIDE(KEY_PPAGE) #define UKEY_PGDN UKEY_WIDE(KEY_NPAGE) #define UKEY_ALT_PREFIX 1000 #define UKEY_ALT_A UKEY_WIDE(UKEY_ALT_PREFIX + 'a') #define UKEY_ALT_B UKEY_WIDE(UKEY_ALT_PREFIX + 'b') #define UKEY_ALT_C UKEY_WIDE(UKEY_ALT_PREFIX + 'c') #define UKEY_ALT_D UKEY_WIDE(UKEY_ALT_PREFIX + 'd') #define UKEY_ALT_E UKEY_WIDE(UKEY_ALT_PREFIX + 'e') #define UKEY_ALT_F UKEY_WIDE(UKEY_ALT_PREFIX + 'f') #define UKEY_ALT_G UKEY_WIDE(UKEY_ALT_PREFIX + 'g') #define UKEY_ALT_H UKEY_WIDE(UKEY_ALT_PREFIX + 'h') #define UKEY_ALT_I UKEY_WIDE(UKEY_ALT_PREFIX + 'i') #define UKEY_ALT_J UKEY_WIDE(UKEY_ALT_PREFIX + 'j') #define UKEY_ALT_K UKEY_WIDE(UKEY_ALT_PREFIX + 'k') #define UKEY_ALT_L UKEY_WIDE(UKEY_ALT_PREFIX + 'l') #define UKEY_ALT_M UKEY_WIDE(UKEY_ALT_PREFIX + 'm') #define UKEY_ALT_N UKEY_WIDE(UKEY_ALT_PREFIX + 'n') #define UKEY_ALT_O UKEY_WIDE(UKEY_ALT_PREFIX + 'o') #define UKEY_ALT_P UKEY_WIDE(UKEY_ALT_PREFIX + 'p') #define UKEY_ALT_Q UKEY_WIDE(UKEY_ALT_PREFIX + 'q') #define UKEY_ALT_R UKEY_WIDE(UKEY_ALT_PREFIX + 'r') #define UKEY_ALT_S UKEY_WIDE(UKEY_ALT_PREFIX + 's') #define UKEY_ALT_T UKEY_WIDE(UKEY_ALT_PREFIX + 't') #define UKEY_ALT_U UKEY_WIDE(UKEY_ALT_PREFIX + 'u') #define UKEY_ALT_V UKEY_WIDE(UKEY_ALT_PREFIX + 'v') #define UKEY_ALT_W UKEY_WIDE(UKEY_ALT_PREFIX + 'w') #define UKEY_ALT_X UKEY_WIDE(UKEY_ALT_PREFIX + 'x') #define UKEY_ALT_Y UKEY_WIDE(UKEY_ALT_PREFIX + 'y') #define UKEY_ALT_Z UKEY_WIDE(UKEY_ALT_PREFIX + 'z') #define UKEY_ALT_1 UKEY_WIDE(UKEY_ALT_PREFIX + '1') #define UKEY_ALT_2 UKEY_WIDE(UKEY_ALT_PREFIX + '2') #define UKEY_ALT_3 UKEY_WIDE(UKEY_ALT_PREFIX + '3') #define UKEY_ALT_4 UKEY_WIDE(UKEY_ALT_PREFIX + '4') #define UKEY_ALT_5 UKEY_WIDE(UKEY_ALT_PREFIX + '5') #define UKEY_ALT_6 UKEY_WIDE(UKEY_ALT_PREFIX + '6') #define UKEY_ALT_7 UKEY_WIDE(UKEY_ALT_PREFIX + '7') #define UKEY_ALT_8 UKEY_WIDE(UKEY_ALT_PREFIX + '8') #define UKEY_ALT_9 UKEY_WIDE(UKEY_ALT_PREFIX + '9') #define UKEY_ALT_0 UKEY_WIDE(UKEY_ALT_PREFIX + '0') #define UKEY_ALT_MINUS UKEY_WIDE(UKEY_ALT_PREFIX + '-') #define UKEY_ALT_EQ UKEY_WIDE(UKEY_ALT_PREFIX + '=') #define UKEY_ALT_BACKSPACE UKEY_WIDE(UKEY_ALT_PREFIX + KEY_BACKSPACE ) #define UKEY_F1 UKEY_WIDE(KEY_F(0) + 1) #define UKEY_F2 UKEY_WIDE(KEY_F(0) + 2) #define UKEY_F3 UKEY_WIDE(KEY_F(0) + 3) #define UKEY_F4 UKEY_WIDE(KEY_F(0) + 4) #define UKEY_F5 UKEY_WIDE(KEY_F(0) + 5) #define UKEY_F6 UKEY_WIDE(KEY_F(0) + 6) #define UKEY_F7 UKEY_WIDE(KEY_F(0) + 7) #define UKEY_F8 UKEY_WIDE(KEY_F(0) + 8) #define UKEY_F9 UKEY_WIDE(KEY_F(0) + 9) #define UKEY_F10 UKEY_WIDE(KEY_F(0) + 10) #define UKEY_SH_F1 UKEY_WIDE(KEY_F(0) + 11) #define UKEY_SH_F2 UKEY_WIDE(KEY_F(0) + 12) #define UKEY_SH_F3 UKEY_WIDE(KEY_F(0) + 13) #define UKEY_SH_F4 UKEY_WIDE(KEY_F(0) + 14) #define UKEY_SH_F5 UKEY_WIDE(KEY_F(0) + 15) #define UKEY_SH_F6 UKEY_WIDE(KEY_F(0) + 16) #define UKEY_SH_F7 UKEY_WIDE(KEY_F(0) + 17) #define UKEY_SH_F8 UKEY_WIDE(KEY_F(0) + 18) #define UKEY_SH_F9 UKEY_WIDE(KEY_F(0) + 19) #define UKEY_SH_F10 UKEY_WIDE(KEY_F(0) + 20) #define UKEY_SH_F11 UKEY_WIDE(KEY_F(0) + 21) #define UKEY_SH_F12 UKEY_WIDE(KEY_F(0) + 22) #define UKEY_CTRL_F1 UKEY_WIDE(KEY_F(0) + 23) #define UKEY_CTRL_F2 UKEY_WIDE(KEY_F(0) + 24) #define UKEY_CTRL_F3 UKEY_WIDE(KEY_F(0) + 25) #define UKEY_CTRL_F4 UKEY_WIDE(KEY_F(0) + 26) #define UKEY_CTRL_F5 UKEY_WIDE(KEY_F(0) + 27) #define UKEY_CTRL_F6 UKEY_WIDE(KEY_F(0) + 28) #define UKEY_CTRL_F7 UKEY_WIDE(KEY_F(0) + 29) #define UKEY_CTRL_F8 UKEY_WIDE(KEY_F(0) + 30) #define UKEY_CTRL_F9 UKEY_WIDE(KEY_F(0) + 31) #define UKEY_CTRL_F10 UKEY_WIDE(KEY_F(0) + 32) #define UKEY_CTRL_F11 UKEY_WIDE(KEY_F(0) + 33) #define UKEY_CTRL_F12 UKEY_WIDE(KEY_F(0) + 34) #define UKEY_ALT_F1 0 #define UKEY_ALT_F2 0 #define UKEY_ALT_F3 0 #define UKEY_ALT_F4 0 #define UKEY_ALT_F5 0 #define UKEY_ALT_F6 0 #define UKEY_ALT_F7 0 #define UKEY_ALT_F8 0 #define UKEY_ALT_F9 0 #define UKEY_ALT_F10 0 #define UKEY_RESIZE KEY_RESIZE #elif defined(_UNICON_USE_YASCREEN_) #define UKEY_INS YAS_K_INS #define UKEY_BACKSPACE YAS_K_BSP #define UKEY_LEFT YAS_K_LEFT #define UKEY_RIGHT YAS_K_RIGHT #define UKEY_UP YAS_K_UP #define UKEY_DOWN YAS_K_DOWN #define UKEY_HOME YAS_K_HOME #define UKEY_END YAS_K_END #define UKEY_DEL YAS_K_DEL #define UKEY_PGUP YAS_K_PGUP #define UKEY_PGDN YAS_K_PGDN #define UKEY_ALT_A YAS_K_ALT('a') #define UKEY_ALT_B YAS_K_ALT('b') #define UKEY_ALT_C YAS_K_ALT('c') #define UKEY_ALT_D YAS_K_ALT('d') #define UKEY_ALT_E YAS_K_ALT('e') #define UKEY_ALT_F YAS_K_ALT('f') #define UKEY_ALT_G YAS_K_ALT('g') #define UKEY_ALT_H YAS_K_ALT('h') #define UKEY_ALT_I YAS_K_ALT('i') #define UKEY_ALT_J YAS_K_ALT('j') #define UKEY_ALT_K YAS_K_ALT('k') #define UKEY_ALT_L YAS_K_ALT('l') #define UKEY_ALT_M YAS_K_ALT('m') #define UKEY_ALT_N YAS_K_ALT('n') #define UKEY_ALT_R YAS_K_ALT('r') #define UKEY_ALT_S YAS_K_ALT('s') #define UKEY_ALT_X YAS_K_ALT('x') #define UKEY_ALT_Z YAS_K_ALT('z') #define UKEY_ALT_0 YAS_K_ALT('0') #define UKEY_ALT_1 YAS_K_ALT('1') #define UKEY_ALT_2 YAS_K_ALT('2') #define UKEY_ALT_3 YAS_K_ALT('3') #define UKEY_ALT_4 YAS_K_ALT('4') #define UKEY_ALT_5 YAS_K_ALT('5') #define UKEY_ALT_6 YAS_K_ALT('6') #define UKEY_ALT_7 YAS_K_ALT('7') #define UKEY_ALT_8 YAS_K_ALT('8') #define UKEY_ALT_9 YAS_K_ALT('9') #define UKEY_ALT_EQ YAS_K_ALT('=') #define UKEY_ALT_MINUS YAS_K_ALT('-') #define UKEY_ALT_BACKSPACE YAS_K_ALT( UKEY_BACKSPACE ) #define UKEY_F1 YAS_K_F1 #define UKEY_F2 YAS_K_F2 #define UKEY_F3 YAS_K_F3 #define UKEY_F4 YAS_K_F4 #define UKEY_F5 YAS_K_F5 #define UKEY_F6 YAS_K_F6 #define UKEY_F7 YAS_K_F7 #define UKEY_F8 YAS_K_F8 #define UKEY_F9 YAS_K_F9 #define UKEY_F10 YAS_K_F10 #define UKEY_SH_F1 YAS_K_S_F1 #define UKEY_SH_F2 YAS_K_S_F2 #define UKEY_SH_F3 YAS_K_S_F3 #define UKEY_SH_F4 YAS_K_S_F4 #define UKEY_SH_F5 YAS_K_S_F5 #define UKEY_SH_F6 YAS_K_S_F6 #define UKEY_SH_F7 YAS_K_S_F7 #define UKEY_SH_F8 YAS_K_S_F8 #define UKEY_SH_F9 YAS_K_S_F9 #define UKEY_SH_F10 YAS_K_S_F10 #define UKEY_CTRL_F1 YAS_K_C_F1 #define UKEY_CTRL_F2 YAS_K_C_F2 #define UKEY_CTRL_F3 YAS_K_C_F3 #define UKEY_CTRL_F4 YAS_K_C_F4 #define UKEY_CTRL_F5 YAS_K_C_F5 #define UKEY_CTRL_F6 YAS_K_C_F6 #define UKEY_CTRL_F7 YAS_K_C_F7 #define UKEY_CTRL_F8 YAS_K_C_F8 #define UKEY_CTRL_F9 YAS_K_C_F9 #define UKEY_CTRL_F10 YAS_K_C_F10 #define UKEY_ALT_F1 YAS_K_A_F1 #define UKEY_ALT_F2 YAS_K_A_F2 #define UKEY_ALT_F3 YAS_K_A_F3 #define UKEY_ALT_F4 YAS_K_A_F4 #define UKEY_ALT_F5 YAS_K_A_F5 #define UKEY_ALT_F6 YAS_K_A_F6 #define UKEY_ALT_F7 YAS_K_A_F7 #define UKEY_ALT_F8 YAS_K_A_F8 #define UKEY_ALT_F9 YAS_K_A_F9 #define UKEY_ALT_F10 YAS_K_A_F10 #define UKEY_RESIZE YAS_SCREEN_SIZE #else #error One of ncurses/yascreen libraries is required under UNIX #error use compile time -D_UNICON_USE_YASCREEN_ or #error use compile time -D_UNICON_USE_CURSES_ to select wanted library #endif /******* common (part 2) ***************************************************/ #define KEY_INSERT KEY_IC #define KEY_INS KEY_IC #define KEY_DELETE KEY_DC #define KEY_DEL KEY_DC /**************************************************************************** ** ** Functions ** ****************************************************************************/ int con_init(); // should be called before any other con_xxx() void con_done(); // should be called at the end of the console io actions void con_suspend(); // suspends console (before system() for example) void con_restore(); // restores console after suspend void con_reset_screen_size(); // reset screen info on screen resize, called in SIGWINCH void con_ce( int attr = -1 ); // clear to end-of-line void con_cs( int attr = -1 ); // clear screen // following functions print string `s' at position `x,y' (col,row) with // color (attribute) `attr' void con_out( int x, int y, const char *s ); void con_out( int x, int y, const char *s, int attr ); void con_puts( const char *s ); void con_puts( const char *s, int attr ); void con_chide(); // cursor hide void con_cshow(); // cursor show int con_max_x(); // max screen x (column) int con_max_y(); // max screen y (row) int con_x(); // current screen x (column) int con_y(); // current screen y (row) void con_fg( int color ); // set foreground color void con_bg( int color ); // set background color void con_ta( int attr ); // set new attribute ( CONCOLOR(fg,bg) ) void con_xy( int x, int y ); // move cursor to position x,y int con_kbhit(); // return != 0 if key is waiting in "keyboard" buffer void con_beep(); // make a "beep" sound int con_getch(); // get single char from the "keyboard" wchar_t con_getwch(); // return wide char or control key (control is in the range 0xF0000-0xFFFFD), 0 for none key #endif /* _UNICON_H_ */ /**************************************************************************** ** EOF ****************************************************************************/ vfu-5.09/vslib/vslib.vpj0000644000175000017500000000362714145574047013623 0ustar cadecade[CONFIGURATIONS] activeconfig=,Release config=,Release [GLOBAL] workingdir=/home/cade/pro/vslib macro=\n version=6.0 [ASSOCIATION] [STATE] SCREEN: 800 600 -4 -4 800 549 0 0 N 0 0 0 0 651 493 CWD: /home/cade/pro/twins BUFFER: BN="/home/cade/pro/twins/twins.pl" BI: MA=1 74 1 TABS=1 3 WWS=1 IWT=0 ST=0 IN=1 BW=0 US=32000 RO=0 SE=1 SN=0 BIN=0 MN=Perl HM=0 MF=544 VIEW: LN=.0 CL=1 LE=0 CX=0 CY=1 WI=5 BI=15 HT=0 HN=0 HF=0 HC=4 WINDOW: 0 0 439 332 0 0 M WF=0 WT=2 "alias-vga,10,0,1" BUFFER: BN="/home/cade/pro/twins/twins.pl" VIEW: LN=.875 CL=12 LE=0 CX=11 CY=3 WI=66 BI=15 HT=0 HN=0 HF=0 HC=4 FILEHIST: 9 /home/cade/public_html/hl/index.html /home/cade/public_html/mw/mw.pl /home/cade/public_html/hl/data/.mime.types /home/cade/pro/seek.pl/ls-1R.pl /home/cade/pro/seek.pl/loc.pl /home/cade/bin/xvpic.pl /home/cade/pro/palm-fe3/pro/p3_conf.pm /home/cade/public_html/hl/hl.pl /home/cade/pro/twins/twins.pl [COMPILER.Release] FILTEREXPANSION=1 1 1 1 1 includedirs=%(INCLUDE) tagfiles= reffile= compile=copts: concur|capture|menu: Compile:&Compilecmd: make %n.o CCDEF="-DTEST" make=copts: concur|capture|saveall|menu: Build:&Buildcmd: make CCDEF="-DTEST" rebuild=copts: concur|capture|menu: Rebuild:&Rebuildcmd: debug=copts: menu: Debug:&Debugcmd: xxgdb [exename-here] execute=copts: concur|capture|saveall|menu: Execute:E&xecutecmd: make CCDEF="-DTEST" && vslib user1=copts: hide|menu: User 1:User 1cmd: user2=copts: hide|menu: User 2:User 2cmd: [FILES] ansiterm.cpp ansiterm.h clusters.cpp clusters.h conmenu.cpp conmenu.h dlog.cpp dlog.h eval.cpp eval.h fnmatch2.cpp fnmatch2.h form_in.cpp form_in.h getopt2.cpp getopt2.h mm.conf scroll.cpp scroll.h target.h test.cpp unicon.cpp unicon.h vscrc.cpp vslib.cpp vslib.h vstring.cpp vstring.h vstrlib.cpp vstrlib.h vsuti.cpp vsuti.h pcre/chartables.c pcre/config.h pcre/get.c pcre/internal.h pcre/mm.conf pcre/pcre.c pcre/pcre.h pcre/pcreposix.c pcre/pcreposix.h pcre/study.c vfu-5.09/vslib/form_in.cpp0000644000175000017500000000710614367022260014103 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include "form_in.h" #include "scroll.h" int EditStrBF = CONCOLOR( chWHITE, cBLUE ); int EditStrFH = CONCOLOR( cBLACK, cWHITE ); int TextInput( int x, int y, const char *prompt __attribute__((unused)), int maxlen, int fieldlen, WString &str_io, void (*handlekey)( int key, WString &str_io, int &pos ) ) { int res = 0; int insert = 1; WString work = str_io; ScrollPos scroll; scroll.set_min_max( 0, str_len( work ) ); scroll.set_pagesize( fieldlen - 2 ); scroll.set_pagestep( 0.3 ); scroll.go( str_len( work ) ); int show = 1; int firsthit = 1; int opage = -1; con_cshow(); while(1) { if (opage != scroll.page()) show = 1; if (show) { WString work_out; str_copy( work_out, work, scroll.page(), scroll.pagesize() ); str_pad( work_out, - scroll.pagesize() ); work_out = L"[" + work_out + L"]"; // used to be spaces if ( scroll.page() > 0 ) str_set_ch( work_out, 0, L'<' ); if ( scroll.page() + scroll.pagesize() < str_len( work ) ) str_set_ch( work_out, -1, L'>' ); VString str_out = work_out; con_out( x, y, str_out, firsthit ? EditStrFH : EditStrBF ); show = 0; opage = scroll.page(); } con_xy( x + scroll.pos() - scroll.page() + 1 , y ); wchar_t wch; wch = con_getwch(); if( ( ! UKEY_IS_WIDE_CTRL( wch ) ) && wch >= 32 && wch != UKEY_BACKSPACE && str_len( work ) < maxlen - 1 ) { if (firsthit) { work.undef(); scroll.go(0); firsthit = 0; } if (!insert) str_del( work, scroll.pos(), 1 ); str_ins_ch( work, scroll.pos(), wch ); scroll.set_min_max( 0, str_len( work ) ); scroll.go( scroll.pos() ); scroll.down(); show = 1; }; if (firsthit) { show = 1; firsthit = 0; } if( wch == 27 ) { res = 0; break; } else if( wch == 13 ) { str_io = work; res = 1; break; } else if( wch == UKEY_CTRL_U ) { scroll.go(0); work.undef(); show = 1; } else if( ( wch == UKEY_BACKSPACE || wch == 8 ) && (scroll.pos() > 0) ) { scroll.up(); str_del( work, scroll.pos(), 1 ); show = 1; } else if ( wch == UKEY_INS ) insert = !insert; else if ( wch == UKEY_LEFT ) scroll.up(); else if ( wch == UKEY_RIGHT ) scroll.down(); else /* if ( wch == UKEY_PGUP ) scroll.ppage(); else if ( wch == UKEY_PGDN ) scroll.npage(); else */ if ( wch == UKEY_HOME || wch == UKEY_CTRL_A ) scroll.go(0); else if ( wch == UKEY_END || wch == UKEY_CTRL_E ) scroll.go(str_len(work)); else if ( ( wch == UKEY_DEL || wch == UKEY_CTRL_D ) && scroll.pos() < str_len(work) ) { str_del( work, scroll.pos(), 1 ); show = 1; } else if ( handlekey ) { int npos = scroll.pos(); handlekey( wch, work, npos ); scroll.set_min_max( 0, str_len( work ) ); scroll.go( scroll.pos() ); if (scroll.pos() != npos) scroll.go( npos ); show = 1; } scroll.set_min_max( 0, str_len( work ) ); scroll.go( scroll.pos() ); } con_chide(); return res; } // eof form_in.cpp vfu-5.09/vslib/form_in.h0000644000175000017500000000136614365574240013562 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _FORM_IN_H_ #define _FORM_IN_H_ #include "unicon.h" #include "vstring.h" #include "wstring.h" extern int EditStrBF; // bakground/foreground extern int EditStrFH; // first hit color int TextInput( int x, int y, const char *prompt, int maxlen, int fieldlen, WString &str_io, void (*handlekey)( int key, WString &str_io, int &pos ) = NULL ); #endif //_FORM_IN_H_ vfu-5.09/vslib/target.h0000644000175000017500000000510314367022260013400 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE 'README','LICENSE' OR 'COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _TARGET_H_ #define _TARGET_H_ #define _TARGET_UNKNOWN_ /****************************************** define target OS ***************/ #ifdef _TARGET_UNKNOWN_ #if defined(DJGPP) || defined(_TARGET_DJGPP_) #define _TARGET_GO32_ #define _TARGET_DJGPP_ #define _TARGET_DOS_ #define _TARGET_DESCRIPTION_ "DOS/DJGPP" #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if defined(__linux__) || defined(__Linux__) || defined(_TARGET_LINUX_) #define _TARGET_LINUX_ #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "UNIX/LINUX" #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if defined(__NetBSD__) || defined(_TARGET_NETBSD_) #define _TARGET_NETBSD_ #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "UNIX/NETBSD" #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if defined(WIN32) || defined(_TARGET_WIN32_) #define _TARGET_WIN32_ #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "DOS/WIN32" // sorry :) #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if defined(MACOSX) || defined(__APPLE__) #define _TARGET_MACOSX_ #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "UNIX/MACOSX" #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if defined(__GNU__) || defined(_TARGET_GNU_) #define _TARGET_GNU_ #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "GNU" #undef _TARGET_UNKNOWN_ #endif #endif #ifdef _TARGET_UNKNOWN_ #if ((defined(__unix__) || defined(unix)) && !defined(USG)) || defined(_TARGET_UNIX_) #define _TARGET_UNIX_ #define _TARGET_DESCRIPTION_ "UNIX" #undef _TARGET_UNKNOWN_ #endif #endif #ifndef _TARGET_DESCRIPTION_ #define _TARGET_DESCRIPTION_ "UNKNOWN/UNKNOWN" #endif /****************************************** go error unless known :/ *******/ #ifdef _TARGET_UNKNOWN_ #error "Unknown target please define one manually!" #error "Supported are _TARGET_LINUX_, _TARGET_DJGPP_, _TARGET_NETBSD_, _TARGET_GNU_, _TARGET_UNIX_" #error "Read README or COMPILE file(s) for details" #endif /******************************************************************* eof ***/ #endif /* TOP */ vfu-5.09/vslib/conmenu.h0000644000175000017500000000267514365574240013601 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _CONMENU_H_ #define _CONMENU_H_ #include #include #include struct ToggleEntry { int key; wchar_t name[64]; int *data; const wchar_t **states; }; struct ConMenuInfo { ConMenuInfo() { defaults(); } void defaults() { cn = 112; ch = 47; ti = 95; bo = ec = ac = 0; } int cn; // normal color int ch; // highlight color int ti; // title color int bo; // should view borders? int st; // scroll type -- 1 dynamic and 0 normal/static wchar_t ec; // exit char (used by con_menu_box) wchar_t ac; // alternative confirm (used by menu box) /* char hide_magic[32]; */ }; extern ConMenuInfo con_default_menu_info; int con_toggle_box( int x, int y, const wchar_t *title, ToggleEntry* toggles, ConMenuInfo *menu_info ); int con_menu_box( int x, int y, const wchar_t *title, WArray *wa, int hotkeys, ConMenuInfo *menu_info ); /* show full screen varray list (w/o last two lines of the screen) */ int con_full_box( int x, int y, const wchar_t *title, WArray *wa, ConMenuInfo *menu_info ); #endif //_CONMENU_H_ vfu-5.09/vslib/clusters.h0000644000175000017500000002005114365574240013765 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ /* * * This is general purpose array classes. * `CLUSTERS' name replaces "boring" `Arrays' name :) * It really doesn't make much difference however... * */ #ifndef _CLUSTERS_H_ #define _CLUSTERS_H_ #include #include #include #include #include #ifndef ASSERT #define ASSERT assert #endif #ifndef int32 #define int32 int #endif // cluster errors #define CE_OK 0 // ok -- no error #define CE_MEM 1 // low memory #define CE_OVR 128 // overflow #define CE_ERR 255 // error /**************************************************************************** ** ** FREE-LIST cluster ** ****************************************************************************/ class FLCluster { int32 es; // element size int32 ds; // data size int32 size; // elements allocated int32 used; // elements used == count int32 ff; // first free element int32 growby; // `grow by' elements char *data; // actual data public: int base; int null; FLCluster(); ~FLCluster(); int create( int32 pcnt, int32 pgrow, int32 pes ); // pes=element size, initial count & `grow by' void done(); int32 add( void* pe ); // insert element, return handle or -1 int del( int32 pn ); // delete element (memset(`+') & mark it `free'), ret E_OK or error int get( int32 pn, void* pe ); // get element, return E_OK or error char* get( int32 pn ); // get element pointer or NULL if error int is_used( int32 pn ); // 1 if pn element is used void dump(); // only for debugging protected: int Realloc( int32 pn ); // expand/shrink data list, return CE_OK, or error }; /**************************************************************************** ** ** BASE Cluster prototype ** ****************************************************************************/ class BaseCluster { protected: int es; int size; int cnt; int bsize; char *data; int status; void (*destructor_func)( void* ); public: BaseCluster() { data = NULL; es = 0; size = 0; cnt = 0; bsize = 0; destructor_func = NULL; }; virtual ~BaseCluster() { if (data) done(); }; int create( int p_size, int p_bsize, int p_es ); void done(); void del( int pn ); void delall(); void free( int pn ); void freeall(); int count() { return cnt; }; void shrink() { Realloc( cnt ); }; void set_destructor( void (*dfunc)(void*) ) { destructor_func = dfunc; }; protected: virtual void destroy_element( void* pe ) { if ( destructor_func != NULL ) destructor_func( pe ); }; int Realloc( int pn ); }; /**************************************************************************** ** ** DATA cluster ** ****************************************************************************/ class DCluster : public BaseCluster { public: int add( void* pe ); int ins( int pn, void* pe ); void put( int pn, void* pe ); void get( int pn, void* pe ); const void* operator [] ( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return data + (pn*es); }; }; /**************************************************************************** ** ** TEMPLATE DATA cluster ** ****************************************************************************/ template< class T > class TDCluster : public DCluster { public: const T* operator [] ( int pn ) const { ASSERT( pn >= 0 && pn < cnt ); return (T*)(data + (pn*es)); }; T operator [] ( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return (*((T*)(data + (pn*es)))); }; }; /**************************************************************************** ** ** POINTER cluster ** ****************************************************************************/ class PCluster : public BaseCluster { public: int create( int p_size, int p_bsize ) { return BaseCluster::create( p_size, p_bsize, sizeof(void*) ); }; int add( void* pe ); int ins( int pn, void* pe ); void put( int pn, void* pe ); void* get( int pn ); void* operator [] ( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return ((void**)data)[pn]; }; protected: virtual void destroy_element( void* pe ) { if ( destructor_func != NULL ) destructor_func( ((void**)pe)[0] ); }; }; /**************************************************************************** ** ** TEMPLATE POINTER cluster ** ****************************************************************************/ template< class T > class TPCluster : public PCluster { public: int create( int p_size, int p_bsize ) { return BaseCluster::create( p_size, p_bsize, sizeof(void*) ); }; T* get( int pn ) {}; T* operator [] ( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return ((T**)data)[pn]; }; protected: virtual void destroy_element( void* pe ) { delete ((T**)pe)[0]; }; }; /**************************************************************************** ** ** TEMPLATE cluster ** ****************************************************************************/ template< class T > class TCluster { int cnt; int size; int bsize; T *data; public: TCluster() { data = NULL; cnt = 0; size = 0; bsize = 0; }; ~TCluster() { if (data) done(); }; void create( int p_size, int p_bsize ) { bsize = p_bsize; Realloc( p_size ); }; void done() { if (data) delete [] data; data = NULL; cnt = 0; size = 0; bsize = 0; }; long count() const { return cnt; } void Realloc( int pn ) { if( pn == 0 ) { if( data ) delete [] data; size = 0; cnt = 0; data = NULL; return; } T *newdata = new T[pn]; int less = pn < cnt ? pn : cnt; int z; for( z = 0; z < less; z++ ) newdata[z] = data[z]; if( data ) delete [] data; cnt = less; size = pn; data = newdata; }; void add( T &e ) { if ( cnt == size ) Realloc( size + bsize ); data[cnt] = e; cnt++; }; T& operator [] ( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return data[pn]; }; const T& operator [] ( int pn ) const { ASSERT( pn >= 0 && pn < cnt ); return data[pn]; }; }; /**************************************************************************** ** ** BIT-SET set ** ****************************************************************************/ class BSet { public: int size; // size (in bits) int datasize; // size (in bytes) char *data; BSet(); BSet( const BSet& b ); BSet( const char* str ); ~BSet(); void set1( int pn ); void set0( int pn ); int get ( int pn ); void set_range1( int start, int end ); void set_range0( int start, int end ); void set_str1( const char* str ); void set_str0( const char* str ); int in( const char *str ); // return 1 if all str's chars are in the set int in( int pn ) { if ( pn < 0 || pn >= size ) return 0; else return get( pn ); }; void reverse() { for(int z = 0; z < datasize; z++) data[z] = ~data[z]; }; void set( int pn, int val ) { if ( val ) set1( pn ); else set0( pn ); }; void set_all1() { if ( data ) memset( data, 0xff, datasize ); }; void set_all0() { if ( data ) memset( data, 0x00, datasize ); }; int operator [] ( int pn ) { ASSERT( pn >= 0 && pn < size ); return get( pn ); }; int resize( int p_size ); BSet& operator = ( const BSet &b1 ); BSet& operator &= ( const BSet &b1 ); BSet& operator |= ( const BSet &b1 ); BSet operator ~ (); friend BSet operator & ( const BSet &b1, const BSet &b2 ); friend BSet operator | ( const BSet &b1, const BSet &b2 ); }; #endif //_CLUSTERS_H_ vfu-5.09/vslib/clusters.cpp0000644000175000017500000002307414365574240014330 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include "clusters.h" #define RETURN(n) { return status = n; } /**************************************************************************** ** ** FREE-LIST cluster ** ****************************************************************************/ #define FL_EF(n) (((int32*)(data+(n)*es))[0]) // element `free' field #define FL_ED(n) ((char*)(data+(n)*es+sizeof(int32))) // element `data' field #define E_BUSY (-2) #define E_NULL (-1) #define E_FREE ( 0) FLCluster::FLCluster() { base = 0; null = -1; data = NULL; size = 0; used = 0; ff = E_NULL; } void FLCluster::done() { ASSERT( data ); if (data) ::free( data ); } FLCluster::~FLCluster() { done(); } int FLCluster::create( int32 pcnt, int32 pgrow, int32 pes ) { ASSERT( pes > 0 ); ASSERT( pcnt > -1 ); ASSERT( pgrow > -1 ); ASSERT( !(pcnt == 0 && pgrow == 0 ) ); es = pes + sizeof(int32); ds = pes; size = 0; used = 0; growby = pgrow; ff = E_NULL; data = NULL; return Realloc( pcnt ); } int32 FLCluster::add( void* pe ) // insert element { ASSERT( pe ); // if (ff == E_NULL) return -1; if (ff == E_NULL) { int32 res = Realloc( size + growby ); if (res != CE_OK) return null; } int32 ret = ff; ff = FL_EF(ret); ASSERT( FL_EF(ret) != E_BUSY ); FL_EF(ret) = E_BUSY; memcpy( FL_ED(ret), pe, ds ); return ret + base; } int FLCluster::get( int32 pn, void* pe ) // get element { pn -= base; ASSERT( pn >= 0 && pn < size ); ASSERT( pe ); ASSERT( FL_EF(pn) == E_BUSY ); memcpy( pe, FL_ED(pn), ds ); return CE_OK; } char* FLCluster::get( int32 pn ) // get element pointer or NULL if error { pn -= base; ASSERT( pn >= 0 && pn < size ); ASSERT( FL_EF(pn) == E_BUSY ); return (char*)(FL_ED(pn)); } int FLCluster::del( int32 pn ) // get element { pn -= base; ASSERT( pn >= 0 && pn < size ); ASSERT( FL_EF(pn) == E_BUSY ); FL_EF(pn) = ff; ff = pn; return CE_OK; } int FLCluster::is_used( int32 pn ) // 1 if pn element is used { return ( FL_EF(pn - base) == E_BUSY ); } int FLCluster::Realloc( int32 pn ) // expand/shrink data list { pn -= base; ASSERT( pn >= size ); if ( pn == size ) return CE_OK; char *newdata = (char*)realloc( data, pn*es ); if (!newdata) return CE_MEM; data = newdata; memset( data + size*es, 0, (pn - size)*es ); int32 z; int32 base = size > 0; for(z = size+base; z < pn; z++) FL_EF(z) = z - 1; FL_EF(size) = ff; ff = pn - 1; size = pn; return CE_OK; } void FLCluster::dump() { int32 z; printf("size: %d, ff: %d, used: %d\n", size, ff, used); for(z = 0; z < size; z++) { printf("%2d: nextfree: %2d\n", z, FL_EF(z)); } } /**************************************************************************** ** ** BASE Cluster prototype ** ****************************************************************************/ int BaseCluster::create( int p_size, int p_bsize, int p_es ) { cnt = 0; es = p_es; // size = p_size; this should be set by Realloc size = 0; bsize = p_bsize; RETURN(Realloc( p_size )); } void BaseCluster::done() { freeall(); cnt = 0; es = 0; bsize = 0; if (data) ::free(data); data = NULL; } void BaseCluster::del( int pn ) { ASSERT( pn >= 0 && pn < cnt ); if (pn < cnt - 1) memmove( data + ( pn )*es, data + ( pn + 1 )*es, ( cnt - pn )*es ); cnt--; } void BaseCluster::delall() { int z = cnt; while(z) del((z--) - 1); } void BaseCluster::free( int pn ) { ASSERT( pn >= 0 && pn < cnt ); destroy_element( (void*)(data + (pn)*es) ); del( pn ); } void BaseCluster::freeall() { int z = cnt; while(z) free((z--) - 1); } int BaseCluster::Realloc( int pn ) { if ( pn == size ) RETURN(CE_OK); char* newdata = (char*)malloc( pn*es ); if (newdata == NULL) RETURN(CE_MEM); #ifdef DEBUG memset( newdata, '+', pn*es ); #endif if (newdata && data) memcpy( newdata, data, (( pn < size ) ? pn : size)*es ); if (data) ::free(data); data = newdata; size = pn; RETURN(CE_OK); } /**************************************************************************** ** ** DATA cluster ** ****************************************************************************/ int DCluster::add( void* pe ) { RETURN(ins( cnt, pe )); } int DCluster::ins( int pn, void* pe ) { int res = 0; if ( cnt == size ) if ( (res = Realloc( size + bsize )) != 0 ) RETURN(res); if (pn < cnt) memmove( data + (pn+1)*es, data + (pn)*es, (cnt - pn)*es ); memcpy( data + (pn*es), pe, es ); cnt++; RETURN(CE_OK); } void DCluster::put( int pn, void* pe ) { ASSERT( pn >= 0 && pn < cnt ); memcpy( data + pn*es, pe, es ); } void DCluster::get( int pn, void* pe ) { ASSERT( pn >= 0 && pn < cnt ); memcpy( pe, data + pn*es, es ); } /**************************************************************************** ** ** POINTER cluster ** ****************************************************************************/ int PCluster::add( void* pe ) { RETURN(ins( cnt, pe )); } int PCluster::ins( int pn, void* pe ) { int res = 0; if ( cnt == size ) if ( (res = Realloc( size + bsize )) != 0 ) RETURN(res); if (pn < cnt) memmove( data + (pn+1)*es, data + (pn)*es, (cnt - pn)*es ); ((void**)data)[pn] = pe; cnt++; RETURN(CE_OK); } void PCluster::put( int pn, void* pe ) { ASSERT( pn >= 0 && pn < cnt ); ((void**)data)[pn] = pe; } void* PCluster::get( int pn ) { ASSERT( pn >= 0 && pn < cnt ); return ((void**)data)[pn]; } /**************************************************************************** ** ** BIT-SET cluster ** ****************************************************************************/ BSet::BSet() { data = NULL; size = 0; datasize = 0; resize( 256 ); } BSet::BSet( const BSet& b ) { data = NULL; size = 0; datasize = 0; resize( b.size ); memcpy( data, b.data, datasize ); } BSet::BSet( const char* str ) { data = NULL; size = 0; datasize = 0; resize( 256 ); set_str1( str ); } BSet::~BSet() { if( data ) free( data ); } void BSet::set1( int pn ) { if ( pn < 0 ) return; if ( pn >= size ) resize( pn + 1 ); data[pn / 8] |= 1 << (pn % 8); } void BSet::set0( int pn ) { if ( pn < 0 ) return; if ( pn >= size ) resize( pn + 1 ); data[pn / 8] &= ~(1 << (pn % 8)); } int BSet::get( int pn ) { if ( pn < 0 || pn >= size ) return 0; return (data[pn / 8] & (1 << (pn % 8))) != 0; } void BSet::set_range1( int start, int end ) // set range { char s = ( start < end ) ? start : end; char e = ( start > end ) ? start : end; for( int z = s; z <= e; z++) set1( z ); } void BSet::set_range0( int start, int end ) // set range { char s = ( start < end ) ? start : end; char e = ( start > end ) ? start : end; for( int z = s; z <= e; z++) set0( z ); } void BSet::set_str1( const char* str ) { int sl = strlen( str ); for( int z = 0; z < sl; z++ ) set1( str[z] ); } void BSet::set_str0( const char* str ) { int sl = strlen( str ); for( int z = 0; z < sl; z++ ) set0( str[z] ); } int BSet::in( const char *str ) { int sl = strlen( str ); for( int z = 0; z < sl; z++ ) if ( !in( str[z] ) ) return 0; return 1; } int BSet::resize( int p_size ) { ASSERT( p_size > 0 ); int new_size = p_size; int new_datasize = p_size / 8 + (p_size % 8 != 0); char *new_data = (char*)malloc( new_datasize ); if (new_data == NULL) return CE_MEM; memset( new_data, 0, new_datasize ); if (data) { memcpy( new_data, data, datasize < new_datasize ? datasize : new_datasize ); free( data ); data = NULL; } data = new_data; size = new_size; datasize = new_datasize; return CE_OK; } BSet& BSet::operator = ( const BSet &b1 ) { resize( b1.size ); memcpy( data, b1.data, datasize ); return *this; } BSet& BSet::operator &= ( const BSet &b1 ) { int z; for(z = 0; z < (datasize < b1.datasize ? datasize : b1.datasize ); z++) data[z] &= b1.data[z]; return *this; } BSet& BSet::operator |= ( const BSet &b1 ) { int z; for(z = 0; z < (datasize < b1.datasize ? datasize : b1.datasize ); z++) data[z] |= b1.data[z]; return *this; } BSet BSet::operator ~ () { BSet b; b = *this; int z; for(z = 0; z < b.datasize; z++) b.data[z] = ~b.data[z]; return b; } BSet operator & ( const BSet &b1, const BSet &b2 ) { BSet b; b = b1; b &= b2; return b; } BSet operator | ( const BSet &b1, const BSet &b2 ) { BSet b; b = b1; b |= b2; return b; } // eof vfu-5.09/vslib/scroll.cpp0000644000175000017500000000575414367022260013757 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #ifndef ASSERT #define ASSERT assert #endif #include "scroll.h" void ScrollPos::home() { if ( ! _size ) return; _pos = _min; _page = _min; fix(); } void ScrollPos::end() { if ( ! _size ) return; _pos = _max; fix(); } void ScrollPos::up() { if ( ! _size ) return; ASSERT( check() ); _pos--; if ( _pos < _min ) { if ( wrap ) _pos = _max; else _pos = _min; } // if ( _pos < _page ) _page--; if ( _pos < _page ) { if( int( _pagestep ) != 1 && _pos - _pagesize < 0 ) _page = 0; else { int pstep = _pagestep >= 1 ? int( _pagestep ) : _pagestep * _pagesize; _page -= pstep < 1 ? 1 : pstep; } } fix(); } void ScrollPos::down() { if ( ! _size ) return; ASSERT( check() ); _pos++; if ( _pos > _max ) { if ( wrap ) _pos = _min; else _pos = _max; } //if ( _pos > _page + _pagesize - 1 ) _page++; if ( _pos > _page + _pagesize - 1 ) { if( int( _pagestep ) != 1 && _max - _pos < _pagesize ) _page = _max - _pos; else { int pstep = _pagestep >= 1 ? int( _pagestep ) : _pagestep * _pagesize; _page += pstep < 1 ? 1 : pstep; } } fix(); } void ScrollPos::pageup() { if ( ! _size ) return; ASSERT( check() ); if ( _pos != _page) _pos = _page; else _pos -= _pagesize; fix(); } void ScrollPos::pagedown() { if ( ! _size ) return; ASSERT( check() ); if ( _pos != _page + _pagesize -1 ) _pos = _page + _pagesize - 1; else { _pos += _pagesize; if ( _page + _pagesize <= _max ) _page += _pagesize; } fix(); } void ScrollPos::go( int new_pos ) { _pos = new_pos; fix(); } void ScrollPos::fix() { if ( _pos < _min ) _pos = _min; if ( _pos > _max ) _pos = _max; if ( _page < _min ) _page = _min; if ( _page > _max ) _page = _max; if ( _pos < _page || _pos > _page + _pagesize - 1 ) { if ( _pagesize ) _page = ( _pos / _pagesize ) * _pagesize; else _page = 0; } ASSERT( check() ); } int ScrollPos::check() { if ( ! _size ) return 1; if ( _pos < _min ) return 0; if ( _pos > _max ) return 0; if ( _page < _min ) return 0; if ( _page > _max ) return 0; if ( _pos < _page ) return 0; if ( _pagesize < 0 ) return 0; if ( _pagestep < 0 ) return 0; // if ( _pos >= _page + _pagesize ) return 0; return 1; } // eof scroll.cpp vfu-5.09/vslib/eval.cpp0000644000175000017500000000775514365574240013423 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include "eval.h" /* This is *very* old code and I'm not sure if it works at all after the last rewrite :) */ int EvalResult; double Eval( const char* a_exp ) { VString exp = a_exp; str_cut_spc( exp ); int ps = 0; // pos of the +/-/:/* signs int prior = 0; // priority flag int par = 0; // brackets flag int z = 0; // pos counter while ( z < str_len( exp ) ) { switch ( exp[z] ) { case '(': par++; break; case ')': par--; break; case '+': if ((par == 0) && (prior < 20)) { prior = 20; ps = z; } break; case '-': if ((par == 0) && (prior < 20)) { prior = 20; ps = z; } break; case '*': if ((par == 0) && (prior < 10)) { prior = 10; ps = z; } break; case '/': if ((par == 0) && (prior < 10)) { prior = 10; ps = z; } break; case '%': if ((par == 0) && (prior < 10)) { prior = 10; ps = z; } break; } z++; } if (ps != 0) { VString p1; VString p2; str_copy( p1, exp, 0, ps ); str_copy( p1, exp, ps ); double res = 0.0; switch (exp[ps]) { case '+': res = (Eval(p1)+Eval(p2)); break; case '-': res = (Eval(p1)-Eval(p2)); break; case '*': res = (Eval(p1)*Eval(p2)); break; case '/': res = (Eval(p1)/Eval(p2)); break; case '%': res = (fmod(Eval(p1),Eval(p2))); break; } return res; } else { // well ... constant/function/brackets int bp = str_find( exp, '(' ); if ( bp >= 0 ) if ( bp > 0 ) { // function VString fname; str_copy( fname, exp, 0, bp ); VString p1; str_copy( p1, exp, bp+1, str_rfind( exp, ')' ) - bp - 1 ); double res = 0.0; if (strcasecmp(fname, "sin") == 0) {res = sin(Eval(p1));} else if (strcasecmp(fname, "cos") == 0) {res = cos(Eval(p1));} else if (strcasecmp(fname, "tan") == 0) {res = sin(Eval(p1))/cos(Eval(p1));} else if (strcasecmp(fname, "atan") == 0) {res = atan(Eval(p1));} else if (strcasecmp(fname, "asin") == 0) {res = asin(Eval(p1));} else if (strcasecmp(fname, "acos") == 0) {res = acos(Eval(p1));} else // degree/radians/grads conversions... if (strcasecmp(fname, "r2d") == 0) {res = Eval(p1)*180/M_PI;} else if (strcasecmp(fname, "d2r") == 0) {res = Eval(p1)*M_PI/180;} else if (strcasecmp(fname, "r2g") == 0) {res = Eval(p1)*200/M_PI;} else if (strcasecmp(fname, "g2r") == 0) {res = Eval(p1)*M_PI/200;} else if (strcasecmp(fname, "d2g") == 0) {res = Eval(p1)*400/360;} else if (strcasecmp(fname, "g2d") == 0) {res = Eval(p1)*360/400;} else if (strcasecmp(fname, "random") == 0) {res = random() % long(Eval(p1));} else if (strcasecmp(fname, "abs") == 0) {res = fabs(Eval(p1));} else if (strcasecmp(fname, "int") == 0) {res = floor(Eval(p1)+0.5);} else if (strcasecmp(fname, "sqrt") == 0) {res = sqrt(Eval(p1));} else if (strcasecmp(fname, "exp") == 0) {res = ::exp(Eval(p1));} else if (strcasecmp(fname, "ln") == 0) {res = log(Eval(p1));} else if (strcasecmp(fname, "lg") == 0) {res = log10(Eval(p1));} else // if (strcasecmp(fname, "") == 0) {} else EvalResult = 10; if (EvalResult == 10) return 0; else return res; } else { // brackets VString p1; str_copy( p1, exp, 1, str_len( exp ) - 2 ); double res = Eval( p1 ); return res; } else { // constant if ( strcasecmp( exp, "pi" ) == 0 ) { return M_PI; } else if ( strcasecmp( exp, "e" ) == 0 ) { return M_E; } else return atof ( exp ); } } //return 0; } vfu-5.09/vslib/ansiterm.cpp0000644000175000017500000001067414365574240014310 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include #include "ansiterm.h" int Ansi_MAXX = 80; int Ansi_MAXY = 25; int Ansi_X = 1; int Ansi_Y = 1; int a_fg; int a_bg; int a_ta; int ansi_o_ta; int ANSI = 0; static int colortab( int color ) // convert normal colors to ANSI ones { switch(color) { case cBLACK : return 0; case cBLUE : return 4; case cGREEN : return 2; case cCYAN : return 6; case cRED : return 1; case cMAGENTA : return 5; case cYELLOW : return 3; case cWHITE : return 7; } return 7; } int AnsiInit( int pANSI ) { if ( pANSI != -1) ANSI = pANSI; else { ANSI = 0; char *buf = getenv( "TERM" ); if ( buf && strcasecmp( buf, "ANSI" ) == 0 ) ANSI = 1; if ( buf && strcasecmp( buf, "vt100" ) == 0 ) ANSI = 1; } if (getenv( "TERMX" )) Ansi_MAXX = atoi( getenv( "TERMX" ) ); if (getenv( "TERMY" )) Ansi_MAXY = atoi( getenv( "TERMY" ) ); if ( Ansi_MAXX == 0 ) Ansi_MAXX = 80; if ( Ansi_MAXY == 0 ) Ansi_MAXY = 25; Ansi_X = 1; Ansi_Y = 1; AnsiTA( cNORMAL ); return 0; } void AnsiDone() { if (!ANSI) return; AnsiTA( cNORMAL ); } void AnsiSuspend() // suspends console (before system() for example) { if (!ANSI) return; ansi_o_ta = a_ta; AnsiTA( cNORMAL ); } void AnsiRestore() // restores console after suspend { if (!ANSI) return; AnsiTA( ansi_o_ta ); } void AnsiCE( int attr ) // clear to end-of-line { if (!ANSI) return; if ( attr != -1 ) { int save_ta = a_ta; AnsiTA( attr ); printf( "\033[K" ); AnsiTA( save_ta ); } else { printf( "\033[K" ); } } void AnsiCS( int attr ) // clear screen { if (!ANSI) return; if ( attr != -1 ) { int save_ta = a_ta; AnsiTA( attr ); printf( "\033[2J" ); AnsiTA( save_ta ); } else { printf( "\033[2J" ); } } void AnsiOut( int x, int y, const char *s ) { AnsiXY( x, y ); AnsiPuts( s ); } void AnsiOut( int x, int y, const char *s, int attr ) { AnsiXY( x, y ); AnsiPuts( s, attr ); } void AnsiPuts( const char *s ) { puts( s ); } void AnsiPuts( const char *s, int attr ) { int _ta = a_ta; AnsiTA( attr ); puts( s ); AnsiTA( _ta ); } void AnsiCHide() { return; } // cursor hide void AnsiCShow() { return; } // cursor show int AnsiMaxX() { return Ansi_MAXX; } int AnsiMaxY() { return Ansi_MAXY; } int AnsiX() { return -1; } int AnsiY() { return -1; } void AnsiFG( int color ) { AnsiTA( CONCOLOR(color, a_bg) ); } void AnsiBG( int color ) { AnsiTA( CONCOLOR( a_fg, color ) ); } void AnsiTA( int attr ) { if (!ANSI) return; a_ta = attr; a_fg = COLORFG( a_ta ); a_bg = COLORBG( a_ta ); int _l = (a_bg>7)?5:0; // blink int _h = (a_fg>7)?1:0; // hi int _f = a_fg; int _b = a_bg; if ( _f > 7 ) _f -= 8; if ( _b > 7 ) _b -= 8; _f = colortab(_f)+30; // fore _b = colortab(_b)+40; // back // hi,blink,fg,bg printf( "\033[0;%s%s%d;%dm", _h?"1;":"", _l?"5;":"", _f, _b ); } void AnsiXY( int x, int y ) // go to x,y { if (!ANSI) return; if ( x < 1 || y < 1 || x > Ansi_MAXX || y > Ansi_MAXY ) return; printf( "\033[%d;%dH", y, x ); } int AnsiKbHit() { return 1; } int AnsiGetch() { return fgetc(stdin); } void AnsiBeep() { printf( "\007" ); } #ifdef TEST3 int main() { AnsiInit(); AnsiCS(); AnsiOut( 1, 1, "<-" ); AnsiXY( 10, 10 ); AnsiTA( 79 ); AnsiPuts( " ANSI terminal Test " ); AnsiXY( 1, 11 ); int z; for ( z = 0; z < 256; z++ ) { char tmp[16]; sprintf( tmp, " %3d ", z ); AnsiTA(z); AnsiPuts( tmp ); } AnsiTA( cNORMAL ); for ( z = 1; z <= AnsiMaxX(); z++ ) AnsiOut( z, 1, "*", z ); for ( z = 1; z <= AnsiMaxX(); z++ ) AnsiOut( z, AnsiMaxY()-1, "*", z ); int c = AnsiGetch(); if (c) c = c; // :) test AnsiDone(); } #endif // eof ansiterm.cpp vfu-5.09/vslib/dlog.cpp0000644000175000017500000000376614365574240013417 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include "dlog.h" TLogFile::TLogFile() { f = NULL; log_fn[0] = 0; keep_open = 0; on_stdout = 0; on_stderr = 0; } TLogFile::~TLogFile() { close(); } void TLogFile::create( const char *fname, int pkeep_open ) { strcpy( log_fn, fname ); f = NULL; keep_open = pkeep_open; open(); fprintf( f, "\n" ); if (!keep_open) close(); } void TLogFile::open() { if ( f ) fclose( f ); f = fopen( log_fn, "at" ); } void TLogFile::close() { if ( f ) fclose( f ); f = NULL; } void TLogFile::log( const char *fname, int line, const char *msg ) { char tmp[1024]; if (!keep_open) open(); time_t now; time(&now); char stime[32]; strcpy(stime, asctime(localtime(&now))); if (stime[strlen(stime) - 1] == '\n') stime[strlen(stime) - 1] = 0; if ( fname == NULL || line == -1 ) sprintf( tmp, "%s : %s", stime, msg); else sprintf( tmp, "%s [%10s:%-5d] %s", stime, fname, line, msg); while(tmp[strlen(tmp) - 1] == '\n') tmp[strlen(tmp) - 1] = 0; strcat( tmp, "\n" ); fputs( tmp, f ); if (on_stdout) fputs( tmp, stdout ); if (on_stderr) fputs( tmp, stderr ); if (!keep_open && f != NULL) close(); } void TLogFile::log( const char *msg ) { log( NULL, -1, msg ); } void TLogFile::log( const char *msg, int n ) { char tmp[255]; sprintf( tmp, msg, n ); log( NULL, -1, tmp ); } void TLogFile::log( const char *msg, const char *arg ) { char tmp[255]; sprintf( tmp, msg, arg ); log( NULL, -1, tmp ); } void TLogFile::log( const char *fname, int line, const char *msg, int n ) { char tmp[255]; sprintf( tmp, msg, n ); log( fname, line, tmp ); } vfu-5.09/vslib/vslib.h0000644000175000017500000000127514365574240013247 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _VLIB_H_ #define _VLIB_H_ #include #include #include #include #include #include #include #include #include #include #include #include "target.h" #endif //_VLIB_H_ // eof vlib.h vfu-5.09/vslib/getopt2.h0000644000175000017500000000141114145574047013505 0ustar cadecade/* * * Copyright (C) 1994 Arno Schaefer * * AU: Prototypen und externe Variablen fuer getopt () * * PO: ANSI C * */ /* see getopt.h for changes */ #ifndef GETOPT_H #define GETOPT_H /* next line added by see getopt2.cpp for changes */ #define GETOPT(opts) while((optc = getopt2(argc, argv, opts)) != -1) /* avoid C/C++ linkage declarations conflict with */ extern "C" { extern char *optarg; extern int optind; extern int opterr; extern int optopt; extern int optc; /* * set this to `0' to avoid warning message from getopt when * reach unknown option */ extern int opterr_report; } /* extern "C" */ /* name changed to getopt2 to avoid library function mismatch */ int getopt2(int argc, char *argv[], char *optstring); #endif vfu-5.09/vslib/COPYING0000644000175000017500000004307014145574047013012 0ustar cadecade GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vfu-5.09/vslib/scroll.h0000644000175000017500000000415314365574240013424 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _SCROLL_H_ #define _SCROLL_H_ class ScrollPos { int _min; int _max; int _pos; int _page; int _pagesize; /* _pagestep -- step to change page on up/down out of the current page if _pagestep is between 0 and 1 it is considered percentage of _pagesize (with minimum step of 1) if _pagestep is 1 or above it is considered integer fixed step (limited to size of _pagesize) */ double _pagestep; int _size; void fix(); int check(); public: int wrap; // 0 -- none, else -- wrap end/begin; NOTE: works only on up/down ScrollPos() { wrap = _min = _max = _pos = _page = _pagesize = _size = 0; _pagestep = 1; }; void set_min_max( int a_min, int a_max ) { _min = a_min; _max = a_max; _size = _max - _min + 1; } void set_pos( int a_pos ) { _pos = a_pos; } void set_page( int a_page ) { _page = a_page; } void set_pagesize( int a_pagesize ) { _pagesize = a_pagesize; if( _pagesize < 0 ) _pagesize = 0; if( _pagestep >= 1 and _pagestep > _pagesize ) _pagestep = _pagesize; } void set_pagestep( double a_pagestep ) { _pagestep = a_pagestep; if( _pagestep <= 0 ) _pagestep = 1; } int min() { return _min; } int max() { return _max; } int pos() { if ( ! _size ) return 0; return _pos; } int page() { if ( ! _size ) return 0; return _page; } int pagesize() { return _pagesize; } int step() { return _pagestep; } void home(); void end(); void up(); void down(); void pageup(); void pagedown(); void ppage() { pageup(); } void npage() { pagedown(); } void go( int new_pos ); }; #endif //_SCROLL_H_ // eof scroll.h vfu-5.09/vslib/eval.h0000644000175000017500000000117214365574240013053 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _EVAL_H_ #define _EVAL_H_ // // this evaluates math expression with recursive parser etc. // (c) Vladi Belperchinov-Shabanski "Cade" 1996-2020 // extern int EvalResult; double Eval( const char* pExp ); #endif //_EVAL_H_ vfu-5.09/vslib/t/0000755000175000017500000000000014371545164012215 5ustar cadecadevfu-5.09/vslib/t/getuch.cpp0000644000175000017500000000132514365574240014201 0ustar cadecade/**************************************************************************** * * Copyright (c) 2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include #include "unicon.h" int main( int argc, char** argv ) { setlocale( LC_ALL, "" ); con_init(); wprintf( L"press CTRL+C to exit\r\n" ); wchar_t wch; while(4) { wchar_t wch = con_getwch(); if( wch == 3 ) break; wprintf( L"%lx\r\n", wch ); } con_done(); } vfu-5.09/vslib/t/getch.cpp0000644000175000017500000000123714365574240014016 0ustar cadecade/**************************************************************************** * * Copyright (c) 2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include "unicon.h" int main( int argc, char** argv ) { setlocale( LC_ALL, "" ); con_init(); printf( "press CTRL+C to exit\n" ); while(4) { int ch = con_getch(); if( ch == UKEY_CTRL_C )break; printf( "%d %x\r\n", ch, ch ); } con_done(); } vfu-5.09/vslib/t/getych.cpp0000644000175000017500000000321014365574240014200 0ustar cadecade/**************************************************************************** * * Copyright (c) 2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include #include "unicon.h" int main( int argc, char** argv ) { setlocale( LC_ALL, "" ); con_init(); wprintf( L"press CTRL+C to exit\n" ); char s[6]; wchar_t wc[6]; while(4) { wc[0] = 0; int z = 0; s[z] = 0; int ch = con_getch(); if( ch < 255 ) { int x = -1; if( (ch & 0x80) == 0x00 ) x = 0; else if( (ch & 0xE0) == 0xC0 ) x = 1; else if( (ch & 0xF0) == 0xE0 ) x = 2; else if( (ch & 0xF8) == 0xF0 ) x = 3; wprintf( L">> ch = %d %x [%x][%x][%x][%x] x = %d\r\n", ch, ch, ch & 0x80, ch & 0xE0, ch & 0xF0, ch & 0xF8, x ); s[z++] = ch; s[z] = 0; while( x > 0 ) { ch = con_getch(); wprintf( L" --- ch = %x, x = %d, z = %d\r\n", ch, x, z ); if( (ch & 0xC0) != 0x80 ) { wprintf( L" --- bad utf8 seq %x\r\n", ch ); break; } s[z++] = ch; s[z] = 0; x--; } int r = mbtowc( wc, s, MB_CUR_MAX ); wprintf( L" x = %d, r = %d, z = %d, wc = %lx\r\n", x, r, z, wc[0] ); ch = 0; } if(wc[0]==3)break; wprintf( L" got control key %x and wide char %lx [%ls]\r\n\r\n\r\n\r\n", ch, wc[0], wc ); } con_done(); } vfu-5.09/vslib/t/test.cpp0000644000175000017500000002743214371545164013710 0ustar cadecade/**************************************************************************** * * (c) Vladi Belperchinov-Shabanski "Cade" 1996-2015 * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include # include #include "vstring.h" #include "vstrlib.h" #include "vsuti.h" void test1() { VString str = "Hello"; str += " World"; // str is `Hello World' now str_reverse( str ); // str is `dlroW olleH' now str_low( str ); // lower case VArray va = str_split( " +", str ); // array contains `dlroW' at pos 0 and `olleH' at 1 va.reverse(); // array reversed: `dlroW' at pos 1 and `olleH' at 0 int z; for( z = 0; z < va.count(); z++ ) { str_reverse( va[z] ); // reverses each string element } str = str_join( va, " " ); // joins into temporary string printf( "************************ test 1 result is: %s\n", str.data() ); // this should print `hello world' } void test2() { VArray va; va.push( "hello" ); // pos 0 va.push( "world" ); // pos 1 va.ins( 1, "your" ); // pos 1 shifted va[1] = "my"; // replaces `your' va[3] = "!"; // set outside the size, array is extended VString str = va.pop(); // pops last element, str is now `!' str = str_join( va, "-" ); // joins to given string str_tr( str, "-", " " ); // replaces dashes with spaces str_replace( str, " my ", " " ); // removes ` my ' printf( "************************ test 2 result is: %s\n", str.data() ); // this should print `hello world' } void test3() { VTrie tr; // hash-like VArray va; // inserting keys and values tr[ "tralala" ] = "data1"; tr[ "opala" ] = "data2"; tr[ "keynext" ] = "data3"; // inserting elements into the array va.push( "this" ); va.push( "just" ); va.push( "test" ); va.push( "simple" ); // adding string to the first element of the array va[1] += " x2"; // the array is converted to trie (hash) and merged into `tr' tr += va; // same as: tr.merge( &va ); // clear the array--remove all elements va.undef(); // take keys from `tr' as array and store them into va, returns count // i.e. i = tr.count(); int i; va = tr.keys(); printf( "keys count = %d\n", va.count() ); // printing the array and trie data for( i = 0; i < va.count(); i++ ) { printf( "%d -> %s (%s)\n", i, va[i].data(), tr[ va[i] ].data() ); } VArray v1; printf( "--------------------\n" ); v1 = tr; // same as: v1.undef; v1.merge( &tr ); v1.print(); // print array data VRegexp re( "a([0-9]+)" ); // compiling new regexp if( re.m( "tralala85." ) ) // match against regexp printf( "sub 1 = %s\n", re[1].data() ); // re[1] returns `85' VString vs; if( re.m( "tralala85.", "(la)+" ) ) // match against regexp { printf( "sub 0 = %s\n", re[0].data() ); // `lala' printf( "sub 1 = %s\n", re[1].data() ); // `la' } printf( "--------------------\n" ); v1 = str_split( ",", "*.tralala,opala and another one" ); // splits on spaces v1.print(); printf( "joined: %s\n", (const char*)str_join( v1, "---" ) ); // join the same data back VString m1 = v1[0]; VString m2 = v1[1]; printf( "1[%s] 2[%s]\n", m1.data(), m2.data() ); printf( "--------------------\n" ); v1 = str_split( " +", "tralala opala and another one", 3 ); // splits data on spaces up to 3 elements v1.print(); printf( "--------------------\n" ); v1[1] = "hack this one here"; // set (overwrite) element 1 str_sleft( v1[2], 11 ); // reset element 2 to the left 11 chars only v1[0] = 12345; // convert integer into string v1.print(); printf( "--------------------\n" ); VArray aa[3]; // array of arrays aa[0] = str_split( " ", "this is just a simple test" ); aa[1] = str_split( " ", "never ending story" ); aa[2] = str_split( " ", "star-wars rulez" ); aa[0][1] = "was"; // first array, second element, replaces `is' with `was' aa[2][0] = "slackware"; // third array, first element, `star-wars' is now `slackware' // expands the array from 3 to 11 elements aa[1][10] = "king of the hill"; for( i = 0; i < 3; i++ ) { printf("---\n"); aa[i].print(); } printf( "---box test-----------------------------\n" ); i = 20; while( i-- ) { v1.push( "this" ); v1.push( "just" ); v1.push( "test" ); v1.push( "simple" ); } v1.print(); VArray vv = v1; // this makes vv data aliased to the data of v1 vv.print(); // actually print the v1's data which is shared right now vv.set( 0, "---" ); // vv makes own copy of the array data vv.print(); // vv's data is no more aliased to v1's VRegexp re_see( "^\\s*see\\s*=\\s*([^, \011]*)\\s*,(.*)$", "i" ); if( re_see.m( "see=*.tgz,tralala" ) ) { VString str; str = str + re_see[1] + re_see[2]; printf( "VRegexp[1+2]=[%s]\n", str.data() ); } printf( "************************ test 3 ends here\n" ); } void test4() { // this is regression test, please ignore it... int i; int ii; VArray va; ii = 20; i = ii; while( i-- ) { va = str_split( ",", "this is, just a simple. but fixed, nonsense test, voila :)" ); printf( "%d%% va count = %d\n", (100*i)/ii, va.count() ); } VString set; VString cat; VString setn; VString catn; VString sete; VString setp; i = 2000; while( i-- ) { set.set( "this is, just a simple. but fixed, nonsense test, voila :)" ); cat.cat( "this is, just a simple. but fixed, nonsense test, voila :)" ); setn.setn( "this is, just a simple. but fixed, nonsense test, voila :)", 20 ); catn.catn( "this is, just a simple. but fixed, nonsense test, voila :)", 20 ); sete = "this is, just a simple. but fixed, nonsense test, voila :)"; setp += "this is, just a simple. but fixed, nonsense test, voila :)"; } printf( "set = %ld\n", str_len( set ) ); printf( "cat = %ld\n", str_len( cat ) ); printf( "setn = %ld\n", str_len( setn ) ); printf( "catn = %ld\n", str_len( catn ) ); printf( "sete = %ld\n", str_len( sete ) ); printf( "setp = %ld\n", str_len( setp ) ); printf( "--------------------\n" ); i = 2000; while( i-- ) { set = "this is, just a simple. but fixed, nonsense test, voila :)"; setn = set; str_del( set, 20, 10 ); str_ins( set, 30, "***opa***" ); str_replace( setn, "i", "[I]" ); } printf( "set = %s\n", set.data() ); printf( "setn = %s\n", setn.data() ); printf( "---array sort-------\n" ); va.undef(); va = str_split( "[, \t]+", "this is, just a simple. but fixed, nonsense test, voila :)" ); va.sort(); va.print(); printf( "--------------------\n" ); va.sort( 1 ); va.print(); printf( "--------------------\n" ); } void test5() { VTrie tr; // hash-like VArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr.print(); tr.reverse(); tr.print(); tr.reverse(); tr.print(); VCharSet cs; cs.push( 'a' ); printf( "char_set: %d, %d\n", cs.in( 'a' ), cs.in( 'z' ) ); cs.undef( 'a' ); printf( "char_set: %d, %d\n", cs.in( 'a' ), cs.in( 'z' ) ); cs.undef(); int i = 2000; while( i-- ) { cs.push( i ); } cs.undef(); printf( "************************ test 5 ends here\n" ); } void test6() { VRegexp re; VArray va; re.comp( "^([^!]+)!(.+)=apquxz(.+)$" ); int i = re.m( "abc!pqr=apquxz.ixr.zzz.ac.uk" ); i--; while( i >= 0 ) { va.push( re[i] ); i--; } va.print(); va.undef(); va += "/this/is/samle/file.tail"; va += "/file.tail"; va += "/this/is/./samle/file.tail/"; va += "/this/..../is/../samle/.file.tail"; va += "/.file.tail"; va += "/"; const char* ps; va.reset(); while( ( ps = va.next() ) ) { printf( "------------------------------------\n" ); printf( "file is: %s\n", ps ); printf( "path is: %s\n", (const char*)str_file_path( ps ) ); printf( "name is: %s\n", (const char*)str_file_name( ps ) ); printf( "ext is: %s\n", (const char*)str_file_ext( ps ) ); printf( "n+ex is: %s\n", (const char*)str_file_name_ext( ps ) ); printf( "reduced path is: %s\n", (const char*)str_reduce_path( ps ) ); printf( "dot reduce sample is: %s\n", (const char*)str_dot_reduce( ps, 10 ) ); } va.fsave( "/tmp/a.aaa" ); va.fload( "/tmp/a.aaa" ); va.print(); } void test7() { VTrie tr; // hash-like VTrie tr2; // hash-like VArray va; // inserting keys and values tr[ "key1" ] = "data1"; tr[ "key2" ] = "data2"; tr[ "key3" ] = "data3"; tr.print(); printf( "---------------------------------1---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------2---\n" ); tr.reverse(); tr.print(); printf( "---------------------------------3---\n" ); tr2 = str_split( " ", "this is simple one way test" ); tr2.print(); printf( "---------------------------------4---\n" ); tr2 += tr; tr2.print(); printf( "---------------------------------5---\n" ); va = tr2; va.print(); printf( "---------------------------------6---\n" ); } void test8() { VString v1; VString v2; v1 = "this is simple test "; v1 *= 1024; printf( "v1 len: %ld\n", str_len( v1 ) ); v2.compact( 1 ); // makes v2 compact, i.e. it will get as much memory as it // needs. otherwise it will get fixed amount of blocks v2 = v1; // data is shared between v1 and v2. any change to v1 or v2 will // detach this data and both will get own copy v2[0] = ' '; // this will create own data for v2 str_tr( v2, "ti", "TI" ); // capitalize T and I v2 = ""; // this will free all data allocated by v2 printf( "copy 7,6: [%s]", (const char*)str_copy( v2, v1, 8, 6 ) ); printf( "copy 10: [%s]", (const char*)str_copy( v2, v1, -10 ) ); printf( "************************ test 5 ends here\n" ); } void test9() { VString fn; fn = "1'\"#\\2`&;*()[]{}~!@%^:.txt"; char t[512]; shell_escape( fn, t ); printf( "char*: [%s]\n", t ); VString v2 = shell_escape( fn.data() ); printf( "vstr: [%s]\n", v2.data() ); printf( "vstr ipl: [%s]\n", shell_escape( fn ).data() ); } int main( void ) { #if 0 char t[256] = "123456----------------------------------------9999999999999"; char T[256] = "123456----------------------------------------9999999999999"; str_trim_left( t, 3 ); printf( "%s\n", t ); for( long z; z < 300000000; z++ ) { //str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str //str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str //memmove( T, t, 222 ); //memcpy( T, t, 222 ); //str_copy( T, t, 0, 222 ); // check for overlapping borders, begin of str } #endif char t[92] = "this is simple test"; char r[92] = "1111111111111111111"; str_word( t, " ", r ); ASSERT( strcmp( t, "is simple test" ) == 0 ); ASSERT( strcmp( r, "this" ) == 0 ); strcpy( t, " opa" ); str_cut_left( t, " " ); ASSERT( strcmp( t, "opa" ) == 0 ); strcpy( t, "opa " ); str_cut_right( t, " " ); ASSERT( strcmp( t, "opa" ) == 0 ); strcpy( t, "this is good" ); str_ins( t, 8, "not " ); ASSERT( strcmp( t, "this is not good" ) == 0 ); str_del( t, 8, 4 ); ASSERT( strcmp( t, "this is good" ) == 0 ); strcpy( t, "more" ); str_mul( t, 3 ); ASSERT( strcmp( t, "moremoremore" ) == 0 ); str_copy( t+10, t, 0, 15 ); // check for overlapping borders, begin of str str_copy( t+10, t+20, 0, 15 ); // check for overlapping borders, end of str strcpy( t, "despicable me" ); str_word( t, " ", r ); ASSERT( strcmp( r, "despicable" ) == 0 ); str_word( t, " ", r ); ASSERT( strcmp( r, "me" ) == 0 ); ASSERT( t[0] == 0 ); /**/ test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); //*/ wint_t ch = getwc( stdin ); printf( "wchar: %u\n", ch ); return 0; } vfu-5.09/vslib/t/getwch.cpp0000644000175000017500000000261514365574240014206 0ustar cadecade/**************************************************************************** * * Copyright (c) 2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include int main( int argc, char** argv ) { setlocale( LC_ALL, "" ); initscr(); start_color(); qiflush(); cbreak(); noecho(); nonl(); // `return' translation off WINDOW *conio_scr=newwin(0,0,0,0); keypad(conio_scr,TRUE); // allow function keys (keypad) meta(conio_scr,TRUE); // switch to 8 bit terminal return values // intrflush(conio_scr,FALSE); // idlok(conio_scr,TRUE); // hardware line ins/del (required?) idcok(conio_scr,TRUE); // hardware char ins/del (required?) // nodelay(conio_scr,FALSE); // blocking getch() scrollok(conio_scr,TRUE); // scroll screen if required (cursor at bottom) /* Color initialization */ printf( "press CTRL+C to exit\n" ); int z; while(4) { wint_t wch; int i = wget_wch( conio_scr, &wch ); z++; if( wch == 3 || i == 3 ) break; printf( "# %d -- %x %lx -- dec: %d %ld\r\n", z, i, wch, i, wch ); } delwin(conio_scr); endwin(); } vfu-5.09/vslib/Makefile0000644000175000017500000000630314367022260013404 0ustar cadecade# use this to disable flto optimizations: # make NO_FLTO=1 # and this to enable verbose mode: # make V=1 AR?=gcc-ar STRIP?=strip RANLIB?=ranlib PKG_CONFIG?=pkg-config PCRE08_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-8) PCRE08_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-8) PCRE32_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-32) PCRE32_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-32) YASCREEN_CC?=$(shell $(PKG_CONFIG) --cflags yascreen) YASCREEN_LD?=$(shell $(PKG_CONFIG) --libs yascreen) NCURSES_CC?=$(shell $(PKG_CONFIG) --cflags ncursesw) NCURSES_LD?=$(shell $(PKG_CONFIG) --libs ncursesw) ALIBS:=libvslib.a libvscon.a libvscony.a ifeq ($(YASCREEN_LD),) ALIBS:=$(filter-out libvscony.a,$(ALIBS)) endif ifeq ($(NCURSES_LD),) ALIBS:=$(filter-out libvscon.a,$(ALIBS)) endif all: $(ALIBS) t/test SRCS:=\ ansiterm.cpp \ clusters.cpp \ conmenu.cpp \ dlog.cpp \ eval.cpp \ form_in.cpp \ getopt2.cpp \ scroll.cpp \ unicon.cpp \ vscrc.cpp \ vslib.cpp \ vsuti.cpp \ t/test.cpp OBJS:=$(SRCS:.cpp=.o) DEPS:=$(OBJS:.o=.d) $(OBJS:.o=.y.d) ifndef NO_FLTO CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign CXXFLAGS+=-flto=auto else CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign endif # some architectures do not have -mno-stackrealign HAVESREA:=$(shell if $(CXX) -mno-stackrealign -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) # old comiplers do not have -Wdate-time HAVEWDTI:=$(shell if $(CXX) -Wdate-time -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) MYCXXFLAGS:=$(CPPFLAGS) $(CXXFLAGS) $(PCRE08_CC) $(PCRE32_CC) $(YASCREEN_CC) $(NCURSES_CC) -Wall -Wextra -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIE -I. -I../vstring ifeq ("$(HAVESREA)","no") MYCXXFLAGS:=$(filter-out -mno-stackrealign,$(MYCXXFLAGS)) endif ifeq ("$(HAVEWDTI)","no") MYCXXFLAGS:=$(filter-out -Wdate-time,$(MYCXXFLAGS)) endif MYLDFLAGS:=$(MYCXXFLAGS) $(LDFLAGS) -fPIE -pie MYLIBS:=$(LIBS) $(PCRE08_LD) $(PCRE32_LD) ifeq ("$(V)","1") Q:= E:=@true else Q:=@ E:=@echo endif %.o: %.cpp $(E) DE $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_CURSES_ -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $< $(E) CXX $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_CURSES_ -c -o $@ $< %.y.o: %.cpp $(E) DE $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_YASCREEN_ -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $< $(E) CXX $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_YASCREEN_ -c -o $@ $< VSLIBOBJ:=\ clusters.o \ dlog.o \ eval.o \ getopt2.o \ scroll.o \ vslib.o \ vsuti.o \ vscrc.o libvslib.a: $(VSLIBOBJ) $(Q)rm -f $@ $(E) AR $@ $(Q)$(AR) rv $@ $+ $(E) RANLIB $@ $(Q)$(RANLIB) $@ VSCONOBJ:=\ conmenu.o \ form_in.o \ unicon.o VSCONYOBJ:=$(VSCONOBJ:.o=.y.o) libvscon.a: $(VSCONOBJ) $(Q)rm -f $@ $(E) AR $@ $(Q)$(AR) rv $@ $+ $(E) RANLIB $@ $(Q)$(RANLIB) $@ libvscony.a: $(VSCONYOBJ) $(Q)rm -f $@ $(E) AR $@ $(Q)$(AR) rv $@ $+ $(E) RANLIB $@ $(Q)$(RANLIB) $@ t/test: t/test.o ../vstring/libvstring.a libvslib.a $(E) LD $@ $(Q)$(CXX) -o $@ $(MYLDFLAGS) $< $(MYLIBS) -L../vstring -lvstring -L. -lvslib clean: $(E) CLEAN $(Q) rm -f *.a *.o *.d t/test t/*.o re: $(Q)$(MAKE) --no-print-directory clean $(Q)$(MAKE) --no-print-directory -j -include $(DEPS) .PHONY: all clean re vfu-5.09/vslib/getopt2.cpp0000644000175000017500000001076414145574047014053 0ustar cadecade/* * * Copyright (C) 1994 Arno Schaefer * * AU: Auswertung der Kommandozeile, der POSIX-Version von getopt () * nachempfunden. * * PO: ANSI C */ /* * Changed by on 12.march.1998,12.feb.2000: * when reach non option string return `+' w. optarg set to this string * instead of return `-1' * Changed code marked `+++ cade +++' and end w. `=== cade ===' * * $Id: getopt2.cpp,v 1.2 2001/10/28 13:53:02 cade Exp $ * */ #include #include #include "getopt2.h" /* Globale Variablen */ char *optarg; int optind = 1; int opterr = 1; int optopt; int optc = '?'; int opt_use_slash = 0; int opterr_report = 1; static char *nextarg = NULL; /* Funktion */ int getopt2(int argc, char *argv[], char *optstring) /* * AU: Auswertung der Kommandozeile * * VB: argc und argv sind die Parameter, die an main () uebergeben werden. * optstring ist ein String, der die Zeichen enthaelt, die als * Optionen erkannt werden. Wenn ein Zeichen von einem Doppelpunkt * gefolgt wird, hat die Option ein Argument, das direkt auf das Zeichen * folgt oder durch Space davon getrennt ist. Gueltige Optionszeichen * sind alle druckbaren Zeichen ausser '?', ' ' und ':'. * * optind ist der Index auf das naechste Element von argv[], das * bearbeitet wird. * * opterr ist ein Flag, das festlegt, ob bei Fehlern Fehlermeldungen * ausgegeben werden. * * optarg ist ein Zeiger auf das Argument, wenn eine Option ein * Argument hat. * * optopt enthaelt bei Fehlern das Optionszeichen, das den Fehler aus- * geloest hat. * * NB: Rueckgabewert ist das jeweils naechste Optionszeichen, oder -1 am * Ende der Optionsliste. * * Die Optionsliste ist zu Ende, wenn argv[optind] NULL ist, oder * argv[optind] nicht mit '-' (oder '/') beginnt, oder argv[optind] * ein einzelnes "-" ist. In diesem Fall wird optind nicht erhoeht. * Das Ende der Optionsliste kann mit "--" erzwungen werden, dann ist * argv[optind] das erste Argument nach "--". * * FB: Ein '?' wird zurueckgegeben, wenn ein Optionszeichen nicht in * optstring enthalten war oder ein ungueltiges Optionszeichen * uebergeben wurde ('?' oder ':'). Ausserdem bei einem fehlenden * Argument, wenn das erste Zeichen von optstring kein ':' ist. * * Ein ':' wird zurueckgegeben bei einem fehlenden Argument, wenn * das erste Zeichen von optstring ein ':' ist. */ { char *search; optarg = NULL; if (nextarg == NULL) { nextarg = argv[optind]; if (nextarg == NULL) { return (-1); } if (*nextarg != '-') { /* +++ cade +++ */ optarg = nextarg; nextarg = NULL; optind++; return('+'); /* === cade === */ /* return (-1); // this is the original code */ } nextarg++; } /* if */ optopt = *nextarg++; if (optopt == 0) { return (-1); } optind++; if (optopt == '-' && *nextarg == 0) { return (-1); } if (optopt == ':' || optopt == '?') { if (opterr) { if (opterr_report) fprintf ( stderr, "%s: illegal option -- %c\n", argv[0], optopt ); } return ('?'); } /* if */ search = strchr (optstring, optopt); if (search == NULL) { if (opterr) { if (opterr_report) fprintf ( stderr, "%s: illegal option -- %c\n", argv[0], optopt ); } return ('?'); } /* if */ if (*nextarg == 0) { nextarg = NULL; } if (search[1] != ':') { if (nextarg != NULL) { optind--; } return (optopt); } if (nextarg != NULL) { optarg = nextarg; nextarg = NULL; return (optopt); } optarg = argv[optind]; if (optind == argc) { if (opterr) { if (opterr_report) fprintf ( stderr, "%s: option requires an argument -- %c\n", argv[0], optopt ); } /* if */ if (optstring[0] == ':') { return (':'); } else { return ('?'); } } /* if */ else { optind++; } return (optopt); } /* getopt () */ vfu-5.09/vslib/THANKS.TO0000644000175000017500000000062114145574047013206 0ustar cadecade I'd like to thank the following people for helping me for vslib: - Ivo Baylov for the KMP search hint. - Larry Wall for Perl. :) (most work done on this library is about the Perl-like structures) - Philip Hazel and University of Cambridge for the fantastic pcre library! - Arno Schaefer for (still perfectly working getopt) :) vfu-5.09/vslib/vslib.cpp0000644000175000017500000000062114365574240013574 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ // eof vslib.cpp vfu-5.09/vslib/vscrc.cpp0000644000175000017500000001214514365574240013601 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "vsuti.h" /*###########################################################################*/ /* CRC functions */ const unsigned long crc_32_tab[256] = { 0x00000000l, 0x77073096l, 0xee0e612cl, 0x990951bal, 0x076dc419l, 0x706af48fl, 0xe963a535l, 0x9e6495a3l, 0x0edb8832l, 0x79dcb8a4l, 0xe0d5e91el, 0x97d2d988l, 0x09b64c2bl, 0x7eb17cbdl, 0xe7b82d07l, 0x90bf1d91l, 0x1db71064l, 0x6ab020f2l, 0xf3b97148l, 0x84be41del, 0x1adad47dl, 0x6ddde4ebl, 0xf4d4b551l, 0x83d385c7l, 0x136c9856l, 0x646ba8c0l, 0xfd62f97al, 0x8a65c9ecl, 0x14015c4fl, 0x63066cd9l, 0xfa0f3d63l, 0x8d080df5l, 0x3b6e20c8l, 0x4c69105el, 0xd56041e4l, 0xa2677172l, 0x3c03e4d1l, 0x4b04d447l, 0xd20d85fdl, 0xa50ab56bl, 0x35b5a8fal, 0x42b2986cl, 0xdbbbc9d6l, 0xacbcf940l, 0x32d86ce3l, 0x45df5c75l, 0xdcd60dcfl, 0xabd13d59l, 0x26d930acl, 0x51de003al, 0xc8d75180l, 0xbfd06116l, 0x21b4f4b5l, 0x56b3c423l, 0xcfba9599l, 0xb8bda50fl, 0x2802b89el, 0x5f058808l, 0xc60cd9b2l, 0xb10be924l, 0x2f6f7c87l, 0x58684c11l, 0xc1611dabl, 0xb6662d3dl, 0x76dc4190l, 0x01db7106l, 0x98d220bcl, 0xefd5102al, 0x71b18589l, 0x06b6b51fl, 0x9fbfe4a5l, 0xe8b8d433l, 0x7807c9a2l, 0x0f00f934l, 0x9609a88el, 0xe10e9818l, 0x7f6a0dbbl, 0x086d3d2dl, 0x91646c97l, 0xe6635c01l, 0x6b6b51f4l, 0x1c6c6162l, 0x856530d8l, 0xf262004el, 0x6c0695edl, 0x1b01a57bl, 0x8208f4c1l, 0xf50fc457l, 0x65b0d9c6l, 0x12b7e950l, 0x8bbeb8eal, 0xfcb9887cl, 0x62dd1ddfl, 0x15da2d49l, 0x8cd37cf3l, 0xfbd44c65l, 0x4db26158l, 0x3ab551cel, 0xa3bc0074l, 0xd4bb30e2l, 0x4adfa541l, 0x3dd895d7l, 0xa4d1c46dl, 0xd3d6f4fbl, 0x4369e96al, 0x346ed9fcl, 0xad678846l, 0xda60b8d0l, 0x44042d73l, 0x33031de5l, 0xaa0a4c5fl, 0xdd0d7cc9l, 0x5005713cl, 0x270241aal, 0xbe0b1010l, 0xc90c2086l, 0x5768b525l, 0x206f85b3l, 0xb966d409l, 0xce61e49fl, 0x5edef90el, 0x29d9c998l, 0xb0d09822l, 0xc7d7a8b4l, 0x59b33d17l, 0x2eb40d81l, 0xb7bd5c3bl, 0xc0ba6cadl, 0xedb88320l, 0x9abfb3b6l, 0x03b6e20cl, 0x74b1d29al, 0xead54739l, 0x9dd277afl, 0x04db2615l, 0x73dc1683l, 0xe3630b12l, 0x94643b84l, 0x0d6d6a3el, 0x7a6a5aa8l, 0xe40ecf0bl, 0x9309ff9dl, 0x0a00ae27l, 0x7d079eb1l, 0xf00f9344l, 0x8708a3d2l, 0x1e01f268l, 0x6906c2fel, 0xf762575dl, 0x806567cbl, 0x196c3671l, 0x6e6b06e7l, 0xfed41b76l, 0x89d32be0l, 0x10da7a5al, 0x67dd4accl, 0xf9b9df6fl, 0x8ebeeff9l, 0x17b7be43l, 0x60b08ed5l, 0xd6d6a3e8l, 0xa1d1937el, 0x38d8c2c4l, 0x4fdff252l, 0xd1bb67f1l, 0xa6bc5767l, 0x3fb506ddl, 0x48b2364bl, 0xd80d2bdal, 0xaf0a1b4cl, 0x36034af6l, 0x41047a60l, 0xdf60efc3l, 0xa867df55l, 0x316e8eefl, 0x4669be79l, 0xcb61b38cl, 0xbc66831al, 0x256fd2a0l, 0x5268e236l, 0xcc0c7795l, 0xbb0b4703l, 0x220216b9l, 0x5505262fl, 0xc5ba3bbel, 0xb2bd0b28l, 0x2bb45a92l, 0x5cb36a04l, 0xc2d7ffa7l, 0xb5d0cf31l, 0x2cd99e8bl, 0x5bdeae1dl, 0x9b64c2b0l, 0xec63f226l, 0x756aa39cl, 0x026d930al, 0x9c0906a9l, 0xeb0e363fl, 0x72076785l, 0x05005713l, 0x95bf4a82l, 0xe2b87a14l, 0x7bb12bael, 0x0cb61b38l, 0x92d28e9bl, 0xe5d5be0dl, 0x7cdcefb7l, 0x0bdbdf21l, 0x86d3d2d4l, 0xf1d4e242l, 0x68ddb3f8l, 0x1fda836el, 0x81be16cdl, 0xf6b9265bl, 0x6fb077e1l, 0x18b74777l, 0x88085ae6l, 0xff0f6a70l, 0x66063bcal, 0x11010b5cl, 0x8f659effl, 0xf862ae69l, 0x616bffd3l, 0x166ccf45l, 0xa00ae278l, 0xd70dd2eel, 0x4e048354l, 0x3903b3c2l, 0xa7672661l, 0xd06016f7l, 0x4969474dl, 0x3e6e77dbl, 0xaed16a4al, 0xd9d65adcl, 0x40df0b66l, 0x37d83bf0l, 0xa9bcae53l, 0xdebb9ec5l, 0x47b2cf7fl, 0x30b5ffe9l, 0xbdbdf21cl, 0xcabac28al, 0x53b39330l, 0x24b4a3a6l, 0xbad03605l, 0xcdd70693l, 0x54de5729l, 0x23d967bfl, 0xb3667a2el, 0xc4614ab8l, 0x5d681b02l, 0x2a6f2b94l, 0xb40bbe37l, 0xc30c8ea1l, 0x5a05df1bl, 0x2d02ef8dl }; /* should start with `0xffffffff' for `crc' and result is crc = ~crc; */ inline crc32_t update_crc32( const unsigned char octet, const crc32_t crc ) { return (crc_32_tab[(unsigned char)((unsigned char)crc ^ octet)] ^ ((crc >> 8) & 0x00FFFFFFl)); } crc32_t mem_crc32( const void* buff, int size ) { crc32_t crc = 0xffffffff; int z; for ( z = 0; z < size; z++ ) crc = update_crc32( ((unsigned char*)buff)[z], crc ); return ~crc; } crc32_t str_crc32( const char *s ) { return mem_crc32( s, strlen(s) ); } crc32_t file_crc32( FILE *f, long buffsize ) /* return CRC32NULL for error */ { ASSERT( f ); char *buff = (char*)malloc( buffsize ); if (buff == NULL) return CRC32NULL; crc32_t crc = CRC32NULL; while(42) { long res = fread( buff, 1, buffsize, f ); if (res == -1) { fclose( f ); return CRC32NULL; } long z; for ( z = 0; z < res; z++ ) crc = update_crc32( buff[z], crc ); if ( res != buffsize ) break; } free( buff ); return ~crc; } crc32_t file_crc32( const char *fname, long buffsize ) // return `0xffffffff' for error { FILE *f = fopen( fname, "rb" ); if (!f) return CRC32NULL; crc32_t crc = file_crc32( f, buffsize ); fclose(f); return crc; } /*###########################################################################*/ /* eof vscrc.cpp */ vfu-5.09/vslib/vsuti.h0000644000175000017500000001361614371545164013304 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _VSUTI_H_ #define _VSUTI_H_ #include #include #include #include #include #include #include #include #include "target.h" #ifdef _TARGET_UNIX_ #include #include #include #ifdef _TARGET_LINUX_ #include #define MAX_PATH PATH_MAX #endif #endif #include #ifndef ASSERT #define ASSERT assert #endif #include "vstring.h" /*###########################################################################*/ /* MISC defines */ #ifdef _TARGET_LINUX_ #include #define MAX_PATH PATH_MAX #endif /* max filename length */ #ifndef MAX_PATH #define MAX_PATH 512 #endif /*###########################################################################*/ /* CRC functions */ typedef unsigned long int crc32_t; #define CRC32NULL (0xffffffff) /* should start with `0xffffffff' for `crc' and result is crc = ~crc; */ inline crc32_t update_crc32( const unsigned char octet, const crc32_t crc ); crc32_t mem_crc32( const void* buff, int size ); crc32_t str_crc32( const char *s ); crc32_t file_crc32( FILE *f, long buffsize = 256*1024 ); crc32_t file_crc32( const char *fname, long buffsize = 256*1024 ); /*###########################################################################*/ typedef unsigned long int adler32_t; adler32_t adler32(adler32_t adler, const char *buf, unsigned int len); adler32_t mem_adler32( const void* buff, int size ); adler32_t str_adler32( const char *s ); adler32_t file_adler32( FILE *f, long buffsize = 256*1024 ); adler32_t file_adler32( const char *fname, long buffsize = 256*1024 ); /*###########################################################################*/ /* FILE functions */ off_t file_size( const char *fname ); off_t file_size( FILE *f ); int file_load( FILE *f, void *buff, int size = -1 ); int file_save( FILE *f, void *buff, int size = -1 ); int file_load( const char* fname, void *buff, int size = -1 ); int file_save( const char* fname, void *buff, int size = -1 ); int file_load_crc32( const char* fname, void *buff, int size ); int file_save_crc32( const char* fname, void *buff, int size ); int file_is_link( const char* fname ); int file_is_dir( const char* fname ); int file_is_dir( struct stat st ); int file_exists( const char* fname ); /***************************************************************************** ** ** tilde_expand() expands ~/path and ~name/path to real pathname. ** it uses $HOME environment variable for ~ substitution. ** *****************************************************************************/ VString tilde_expand( const char* a_path ); /***************************************************************************** ** ** make_path() create new directory including non-existing path entries. ** It can create /a/b/c/d/e/ without existing of `/a/' for example. ** return 0 for success ** *****************************************************************************/ int make_path( const char *s, long mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH ); /***************************************************************************** ** ** expand_path() resolves symlinks etc. ** *****************************************************************************/ char* expand_path( const char *src, char *dest ); VString expand_path( const char* src ); /***************************************************************************** ** ** shell_escape() escapes shell special characters ** *****************************************************************************/ char* shell_escape( const char *src, char *dest ); VString& shell_escape( VString &dest ); VString shell_escape( const char* src ); /***************************************************************************** ** ** dosstat() is fast stat() designed for DOS FAT filesystems under DJGPP. ** *****************************************************************************/ #ifdef _TARGET_GO32_ #include #include int dosstat( DIR *dir, struct stat *stbuf ); #endif /***************************************************************************** ** ** ftwalk() traverses directory tree and calls func() for every entri it ** encounters. It supports DOS FAT filesystems under DJGPP. ** *****************************************************************************/ #define FTWALK_F 1 /* file (regular) */ #define FTWALK_D 2 /* dir */ #define FTWALK_DX 3 /* call on exit directory */ #define FTWALK_NS 4 /* stat() failed */ /* func() should return 0 for ok, -1 */ int ftwalk( const char *origin_dir, int (*func)( const char* origin, /* origin path */ const char* fname, /* full file name */ const struct stat* st, /* stat struture or NULL */ int is_link, /* 1 if link */ int flag ), int level = -1 ); /***************************************************************************** ** ** get_rc_directory() return application rc directory (and possibly create it) ** returned dir is $HOME/.dir_prefix or $HOME/$RC_PREFIX/dir_prefix depending ** on $RC_PREFIX existence. ** *****************************************************************************/ VString get_rc_directory( const char* dir_prefix ); /***************************************************************************** ** ** EOF ** *****************************************************************************/ #endif /* _VUTILS_H_ */ vfu-5.09/vslib/ansiterm.h0000644000175000017500000000413314365574240013746 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _ANSITERM_H_ #define _ANSITERM_H_ /////////////////////////////////////////////////////////////////////////// // // COLOR defines // #ifndef CONCOLOR // to avoid duplicates with UNICON #define CONCOLOR(f,b) (b*16+f) #define COLORFG(t) (t % 16) #define COLORBG(t) (t / 16) #endif // CONCOLOR #ifndef cNORMAL // to avoid duplicates with UNICON #define cNORMAL 7 #define cBOLD 8 #define cBLACK 0 #define cBLUE 1 #define cGREEN 2 #define cCYAN 3 #define cRED 4 #define cMAGENTA 5 #define cBROWN 6 #define cYELLOW 6 #define cLGRAY 7 #define chBLACK 7 #define cWHITE 7 #define cDGRAY 8 #define chBLUE 9 #define chGREEN 10 #define chCYAN 11 #define chRED 12 #define chMAGENTA 13 #define chYELLOW 14 #define chWHITE 15 #endif // cNORMAL int AnsiInit( int pANSI = -1 ); // 0=off, 1=on, -1=auto (TERM env.var) void AnsiDone(); void AnsiSuspend(); // suspends console (before system() for example) void AnsiRestore(); // restores console after suspend void AnsiCE( int attr = -1 ); // clear to end-of-line void AnsiCS( int attr = -1 ); // clear screen void AnsiOut( int x, int y, const char *s ); void AnsiOut( int x, int y, const char *s, int attr ); void AnsiPuts( const char *s ); void AnsiPuts( const char *s, int attr ); void AnsiCHide(); // cursor hide void AnsiCShow(); // cursor show int AnsiMaxX(); int AnsiMaxY(); int AnsiX(); int AnsiY(); void AnsiFG( int color ); void AnsiBG( int color ); void AnsiTA( int attr ); void AnsiXY( int x, int y ); // go to x,y int AnsiKbHit(); int AnsiGetch(); void AnsiBeep(); #endif //_ANSITERM_H_ // eof ansiterm.h vfu-5.09/vslib/dlog.h0000644000175000017500000000202514365574240013047 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _DLOG_H_ #define _DLOG_H_ #include #define LOG( what ) log( __FILE__, __LINE__, what ) #define LOGN( what, n ) log( __FILE__, __LINE__, what, n ) class TLogFile { FILE *f; char log_fn[255]; int keep_open; public: int on_stdout; int on_stderr; TLogFile(); ~TLogFile(); void create( const char *fname, int pkeep_open ); void open(); void close(); void log( const char *fname, int line, const char *msg ); void log( const char *fname, int line, const char *msg, int n ); void log( const char *msg ); void log( const char *msg, int n ); void log( const char *msg, const char *arg ); }; #endif //_DLOG_H_ vfu-5.09/vslib/AUTHORS0000644000175000017500000000017214145574047013023 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com vfu-5.09/vslib/conmenu.cpp0000644000175000017500000002177514367022260014126 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include "conmenu.h" ConMenuInfo con_default_menu_info; int con_toggle_box( int x, int y, const wchar_t *title, ToggleEntry* toggles, ConMenuInfo *menu_info ) { ScrollPos scroll; int z; if( x < 0 ) x = con_max_x() + x; if( y < 0 ) y = con_max_y() + y; int w = -1; int h = -1; int maxlen = 0; int count = 0; while( toggles[count].key != 0 ) { int sl = str_len( toggles[count].name ); if (sl > maxlen) maxlen = sl; count++; } maxlen += 6+3; // 6 for state string and 3 for " key " string in the front if (w == -1) w = maxlen; if (h == -1) h = count; z = str_len( title ); if (w < z) w = z; if (h > count) h = count; if (h == 0) h = 1; // FIXME: those should be fixed!!! if (x + w > con_max_x() ) w = con_max_x() - x - 1; if (y + h > con_max_y()-4) h = con_max_y() - y - 5; WString str; WString str1; WString hots = L""; for(z = 0; z < count; z++) if ( wcsncmp( L"--", toggles[z].name, 2 ) ) str_add_ch( hots, toggles[z].key ); else str_add_ch( hots, L' ' ); con_xy(x,y); str = L""; str = title; str_pad( str,-w, menu_info->bo ? L'-' : L' ' ); if (str_len(str) > w) str_sleft(str,w); if (menu_info->bo) str = L".-" + str + L"-."; else str = L" " + str + L" "; con_out(x,y,VString(str),menu_info->ti); y++; scroll.wrap = 1; scroll.set_min_max( 0, count-1 ); scroll.set_pagesize( h ); scroll.go( 0 ); while(1) { for( z = 0; z < scroll.pagesize(); z++ ) { if (scroll.page() + z >= count) { str = L" ~"; str_pad( str, -(w+2), L' '); } else { int sep = (wcsncmp(L"--", toggles[scroll.page()+z].name, 2) == 0); if (sep) { str = L""; str += toggles[scroll.page()+z].name; str_pad( str, -w, L'-'); str += L"--"; } else { str = L" "; str_add_ch( str, toggles[scroll.page()+z].key ); str += L" "; str += toggles[scroll.page()+z].name; str_pad( str, -(w-6), L' '); str1 = toggles[scroll.page()+z].states[*(toggles[scroll.page()+z].data)]; str_pad( str1, 6, L' '); str1 += L" "; str += L" " + str1; } } if (menu_info->bo) str = L"|" + str + L"|"; else str = L" " + str + L" "; // if (str.len() > w) StrSLeft(str,w); // str = " " + str + " "; con_out( x, y+z, VString(str), ( scroll.page()+z != scroll.pos() ) ? menu_info->cn : menu_info->ch ); } if (menu_info->bo) { str = L""; str_pad( str, w+2, L'-' ); str = L"`" + str + L"'"; con_out( x, y+scroll.pagesize(), VString(str), menu_info->cn ); } wchar_t wch = con_getwch(); menu_info->ec = wch; if ( wch == UKEY_UP ) scroll.up(); if ( wch == UKEY_DOWN ) scroll.down(); if ( wch == UKEY_PGUP ) scroll.ppage(); if ( wch == UKEY_PGDN ) scroll.npage(); if ( wch == UKEY_HOME ) scroll.home(); if ( wch == UKEY_END ) scroll.end(); if ( wch == 27 || wch == 8 || wch == UKEY_BACKSPACE ) return 0; // exit on ESC or BS // if ( ch < 0 || ch > 255 ) continue; //FIXME: unicode? if ( wch == 13 /* && wcsncmp(L"--", toggles[scroll.pos].name, 2) */ ) return 1; z = ( wch == L' ' ) ? scroll.pos() : str_find( hots, wch ); if ( z > 0 && wcsncmp(L"--", toggles[z].name, 2) ) { int state = *(toggles[z].data) + 1; if (toggles[z].states[state] == NULL) state = 0; *(toggles[z].data) = state; } } return -1; } int con_menu_box( int x, int y, const wchar_t *title, WArray *wa, int hotkeys, ConMenuInfo *menu_info ) { ScrollPos scroll; int z; if( x < 0 ) x = con_max_x() + x; if( y < 0 ) y = con_max_y() + y; int w = -1; int h = -1; if (w == -1) w = wa->max_len(); if (h == -1) h = wa->count(); z = str_len(title); if (w < z) w = z; if (h > wa->count()) h = wa->count(); if (h == 0) h = 1; // FIXME: those should be fixed!!! if (x + w > con_max_x() ) w = con_max_x() - x - 4; if (y + h > con_max_y()-4) h = con_max_y() - y - 4; WString str; WString hots = L""; if ( hotkeys > -1 ) { for(z = 0; z < wa->count(); z++) if (wcsncmp(L"--", wa->get(z), 2)) str_add_ch( hots, wchar_t(((const wchar_t*)(wa->get(z)))[hotkeys]) ); else str_add_ch( hots,L' ' ); str_up(hots); } con_xy(x,y); str = L" "; str += title; str += L" "; str_pad( str,-(w), menu_info->bo ? L'-' : L' ' ); if (str_len(str) > w) str_sleft(str,w); if (menu_info->bo) str = L".-" + str + L"-."; else str = L" " + str + L" "; con_out(x,y,VString(str),menu_info->ti); y++; scroll.wrap = 1; scroll.set_min_max( 0, wa->count()-1 ); scroll.set_pagesize( h ); scroll.go( 0 ); while(1) { for( z = 0; z < scroll.pagesize(); z++ ) { str = (scroll.page()+z >= wa->count())? L"~" : wa->get(scroll.page()+z); /* if ( menu_info->hide_magic[0] ) { int i = str_rfind( str, menu_info->hide_magic ); if ( i != -1) str_sleft( str, i ); } */ str_pad( str,-w , (wcsncmp(L"--", str, 2) == 0)?L'-':L' '); if (str_len(str) > w) str = str_dot_reduce( str, w ); if (menu_info->bo) str = L"| " + str + L" |"; else str = L" " + str + L" "; con_out( x, y+z, VString(str), ( scroll.page()+z != scroll.pos() ) ? menu_info->cn : menu_info->ch ); } if (menu_info->bo) { str = L""; str_pad( str, w+2, L'-' ); str = L"`" + str + L"'"; con_out( x, y+scroll.pagesize(), VString(str), menu_info->cn ); } wchar_t wch = con_getwch(); menu_info->ec = wch; if ( wch == UKEY_UP ) scroll.up(); if ( wch == UKEY_DOWN ) scroll.down(); if ( wch == UKEY_PGUP ) scroll.ppage(); if ( wch == UKEY_PGDN ) scroll.npage(); if ( wch == UKEY_HOME ) scroll.home(); if ( wch == UKEY_END ) scroll.end(); if ( wch == 27 || wch == 8 || wch == UKEY_BACKSPACE ) { // exit on ESC or BS menu_info->ac = 0; return -1; } // if ( ch < 0 || ch > 255 ) continue; //FIXME: unicode? if ( wch == 13 ) { if (wcsncmp(L"--", wa->get(scroll.pos()), 2) != 0) // ako e "--" e separator { menu_info->ec = hots[scroll.pos()]; menu_info->ac = 0; return scroll.pos(); } } if ( menu_info->ac > 0 && wch == menu_info->ac ) { if (wcsncmp( L"--", wa->get(scroll.pos()), 2) != 0) // ako e "--" e separator { menu_info->ec = menu_info->ac; // menu_info->ec = hots[scroll.pos()]; menu_info->ac = 0; return scroll.pos(); } } z = str_find( hots, towupper(wch) ); if ( z > -1 ) { menu_info->ec = hots[z]; menu_info->ac = 0; return z; } } menu_info->ac = 0; return -1; } int con_full_box( int x __attribute__((unused)), int y __attribute__((unused)), const wchar_t *title, WArray *wa, ConMenuInfo *menu_info ) { ScrollPos scroll; scroll.wrap = 0; scroll.set_min_max( 0, wa->count()-1 ); scroll.set_pagesize( con_max_y() - 3 ); /* one title and two status */ scroll.go( 0 ); wchar_t pos_str[32]; con_xy( 1, 1 ); con_puts( VString( title ), menu_info->ti ); con_ce( menu_info->ti ); while(4) { WString str; int z; for( z = 0; z < scroll.pagesize(); z++ ) { if ( scroll.page() + z < wa->count() ) str = wa->get( scroll.page() + z ); else str = L"~"; str = str_dot_reduce( str, con_max_x()-1 ); con_xy( 1, z + 2 ); int c = ( scroll.page() + z == scroll.pos() ) ? menu_info->ch : menu_info->cn; con_puts( VString(str), c ); con_ce( c ); } swprintf( pos_str, (sizeof(pos_str)/sizeof(wchar_t))-1, L" %5d of %5d", scroll.pos()+1, scroll.max()+1 ); con_out( con_max_x() - 15, 1, VString(pos_str), menu_info->ti ); int wch; switch( (wch = con_getwch()) ) { case 27 : menu_info->ec = 27; return -1; break; case 8 : menu_info->ec = 8; return -1; break; case UKEY_BACKSPACE : menu_info->ec = UKEY_BACKSPACE; return -1; break; case 13 : menu_info->ec = 13; return scroll.pos(); break; case UKEY_UP : scroll.up(); break; case UKEY_DOWN : scroll.down(); break; case UKEY_PGUP : scroll.ppage(); break; case UKEY_PGDN : scroll.npage(); break; case UKEY_HOME : scroll.home(); break; case UKEY_END : scroll.end(); break; default: if ( towlower( wch ) == towlower( menu_info->ac ) ) { menu_info->ec = menu_info->ac; return -2; } break; } } } vfu-5.09/vslib/README0000644000175000017500000000210414145574047012630 0ustar cadecade VSLIB Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com Some files are taken "as is" from other packages and/or software implementations. All the appropriate credits are intact. All files are under the GPL license. Some file names and/or function names are changed just to keep this library namespace clean where needed. To build VSLIB you need just: make To clean after build (including output library): make clean To clean and rebuild you need just: make re VSLIB uses PCRE regexp library: http://www.pcre.org/ With permission VSLIB has own version of PCRE but it is not used by default. Try to build with usual: make if system pcre installation doas not work or is missing you can use VSLIB's version of PCRE: make -C pcre re make re CCDEF=-Ipcre LDDEF=-Lpcre this will rebuild VSLIB using the included version of PCRE LICENSE AGREEMENT: GPL, SEE `COPYING' FOR THE FULL TEXT. vfu-5.09/vslib/vsuti.cpp0000644000175000017500000003716414444676600013644 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2022 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "vsuti.h" #include "vstring.h" #include "vstrlib.h" /*###########################################################################*/ /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-1996 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #define BASE 65521L /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /*---------------------------------------------------------------------------*/ unsigned long adler32(unsigned long adler, const char *buf, unsigned int len) { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { DO16(buf); buf += 16; k -= 16; } if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } /*---------------------------------------------------------------------------*/ adler32_t mem_adler32( const void* buff, int size ) { return adler32( adler32( 0, NULL, 0 ), (const char *)buff, size ); } /*---------------------------------------------------------------------------*/ adler32_t str_adler32( const char *s ) { return mem_adler32( s, strlen(s) ); } /*---------------------------------------------------------------------------*/ adler32_t file_adler32( FILE *f, long buffsize ) { ASSERT( f ); char *buff = (char*)malloc( buffsize ); if (buff == NULL) return 0; adler32_t adler = adler32( 0, NULL, 0 ); while(42) { long res = fread( buff, 1, buffsize, f ); if (res == -1) { fclose( f ); return 0; } adler = adler32( adler, buff, res ); if ( res != buffsize ) break; } free( buff ); return adler; } /*---------------------------------------------------------------------------*/ adler32_t file_adler32( const char *fname, long buffsize ) { FILE *f = fopen( fname, "rb" ); if (!f) return 0; adler32_t adler = file_adler32( f, buffsize ); fclose(f); return adler; } /*###########################################################################*/ /* FILE functions */ off_t file_size( const char *fname ) { struct stat st; if (stat( fname, &st )) return -1; return st.st_size; } /*---------------------------------------------------------------------------*/ off_t file_size( FILE *f ) { int res = 0; off_t opos = ftello( f ); if (opos == -1) return -1; if (fseeko( f, 0, SEEK_END )) res++; off_t size = ftello( f ); res += (size == -1); if (fseeko( f, opos, SEEK_SET )) res++; if (res) return -1; return size; } /*---------------------------------------------------------------------------*/ int file_load( FILE *f, void *buff, int size ) { return ( fread( buff, 1, size, f ) != (size_t)size); } /*---------------------------------------------------------------------------*/ int file_save( FILE *f, void *buff, int size ) { return (fwrite( buff, 1, size, f ) != (size_t)size); } /*---------------------------------------------------------------------------*/ int file_load( const char* fname, void *buff, int size ) { FILE *f = fopen( fname, "rb" ); if (!f) return 1; int res = file_load( f, buff, size ); fclose( f ); return res; } /*---------------------------------------------------------------------------*/ int file_save( const char* fname, void *buff, int size ) { FILE *f = fopen( fname, "wb" ); if (!f) return 1; int res = file_save( f, buff, size ); fclose( f ); return res; } /*---------------------------------------------------------------------------*/ int file_load_crc32( const char* fname, void *buff, int size ) { crc32_t crc; FILE *f = fopen( fname, "rb" ); if (!f) return 1; int res = 0; res += ( fread( buff, 1, size, f ) != (size_t)size ); res += ( fread( &crc, 1, sizeof(crc), f ) != sizeof(crc) ); fclose(f); res += ( crc != mem_crc32( buff, size ) ); return res; } /*---------------------------------------------------------------------------*/ int file_save_crc32( const char* fname, void *buff, int size ) { crc32_t crc = mem_crc32( buff, size ); FILE *f = fopen( fname, "wb" ); if (!f) return 1; int res = 0; res += ( fwrite( buff, 1, size, f ) != (size_t)size ); res += ( fwrite( &crc, 1, sizeof(crc), f ) != sizeof(crc) ); fclose(f); return res; } int file_is_link( const char* fname ) { #ifdef _TARGET_GO32_ return 0; #else struct stat st; if (lstat( fname, &st )) return 0; /* consider it not link */ return !!( S_ISLNK(st.st_mode) ); #endif } /*---------------------------------------------------------------------------*/ int file_is_dir( const char* fname ) { struct stat st; if (stat( fname, &st )) return 0; /* consider it not link */ return !!( S_ISDIR(st.st_mode) ); } /*---------------------------------------------------------------------------*/ int file_is_dir( struct stat st ) { return !!( S_ISDIR(st.st_mode) ); } int file_exists( const char* fname ) { return access( fname, F_OK ) == 0; } /***************************************************************************** ** ** tilde_expand() expands ~/path and ~name/path to real pathname. ** it uses $HOME environment variable for ~ substitution. ** *****************************************************************************/ VString tilde_expand( const char* a_path ) { #ifdef _TARGET_UNIX_ VString path; struct passwd* pwd; if ( !a_path || !a_path[0] || a_path[0] != '~' ) return VString( a_path ); int z = 1; // first after ~ while( a_path[z] != '/' && a_path[z] != 0 ) str_add_ch( path, a_path[z++] ); if ( path == "" ) path = getenv( "USER" ); if ( path != "" ) { pwd = getpwnam( path ); if (!pwd) return VString( a_path ); path = pwd->pw_dir; } else { char* pw_dir = getenv("HOME"); if (!pw_dir) return VString( a_path ); path = pw_dir; } // get rid of trailing `/' if exists str_fix_path( path ); str_chop( path ); path += a_path + z; return path; #else //_TARGET_UNIX_ VString path = a_path; return path; #endif //_TARGET_UNIX_ } /***************************************************************************** ** ** make_path() create new directory including non-existing path entries. ** It can create /a/b/c/d/e/ without existing of `/a/' for example. ** return 0 for success ** *****************************************************************************/ int make_path( const char *s, long mode ) { char str[MAX_PATH]; char tmp[MAX_PATH]; strcpy( tmp, s ); str_fix_path( tmp ); int l = strlen( tmp ); strcpy( str, tmp ); // to hold original expanded path while(1) { while ( l >= 0 && tmp[l] != '/' ) l--; ASSERT( l < 0 || tmp[l] == '/' ); if ( l < 0 ) break; else tmp[l+1] = 0; if ( access( tmp, F_OK ) == 0 ) break; l--; } while(1) { l++; while ( str[l] != 0 && str[l] != '/' ) l++; if ( str[l] == 0 ) break; strncpy( tmp, str, l ); tmp[l] = 0; int res = mkdir( tmp, mode ); if (res) return res; } return 0; } /***************************************************************************** ** ** expand_path() resolves symlinks etc. ** *****************************************************************************/ char* expand_path( const char *src, char *dest ) { if( realpath( src, dest ) == 0 ) strcpy( dest, src ); return dest; } VString expand_path( const char* src ) { char temp[MAX_PATH]; VString dest = expand_path( src, temp ); return dest; } /***************************************************************************** ** ** shell_escape() escapes shell special characters '\"`&;*()[]{}!^: ** ** this seems to work fine with bash, zsh, csh, tcsh, fish, ksh, dash *****************************************************************************/ const char* shell_special_chars = "'\"\\`;&*()[]{}!^:?$<> |%"; char* shell_escape( const char *src, char *dest ) { int sl = strlen( src ); int z = 0; for( int i = 0; i < sl; i++ ) { if( strchr( shell_special_chars, src[i] ) ) dest[z++] = '\\'; dest[z++] = src[i]; } dest[z] = 0; return dest; } VString& shell_escape( VString &dest ) { int sl = strlen( dest ); for( int i = 0; i < sl; i++ ) { if( ! strchr( shell_special_chars, dest[i] ) ) continue; str_ins_ch( dest, i, '\\' ); sl++; i++; } return dest; } VString shell_escape( const char* src ) { VString dest; dest = src; shell_escape( dest ); return dest; } /***************************************************************************** ** ** dosstat() is fast stat() designed for DOS FAT filesystems under DJGPP. ** *****************************************************************************/ #ifdef _TARGET_GO32_ /* This is specific to djgpp/libc 2.01 -- if it changes later this must be changed too... */ struct ___DIR { int num_read; char *name; int flags; struct ffblk ff; struct dirent de; int need_fake_dot_dotdot; /* 0=no, 1=.., 2=. */ } /* Convert file date and time to time_t value suitable for struct stat fields. */ time_t _file_time_stamp(unsigned int dos_ftime) { struct tm file_tm; memset(&file_tm, 0, sizeof(struct tm)); file_tm.tm_isdst = -1; /* let mktime() determine if DST is in effect */ file_tm.tm_sec = (dos_ftime & 0x1f) * 2; file_tm.tm_min = (dos_ftime >> 5) & 0x3f; file_tm.tm_hour = (dos_ftime >> 11) & 0x1f; file_tm.tm_mday = (dos_ftime >> 16) & 0x1f; file_tm.tm_mon = ((dos_ftime >> 21) & 0x0f) - 1; /* 0 = January */ file_tm.tm_year = (dos_ftime >> 25) + 80; return mktime(&file_tm); } int dosstat( DIR *dir, struct stat *statbuf ) { #define ff_blk (((___DIR*)(dir))->ff) #define READ_ACCESS (S_IRUSR | S_IRGRP | S_IROTH) #define WRITE_ACCESS S_IWUSR #define EXEC_ACCESS (S_IXUSR | S_IXGRP | S_IXOTH) memset(statbuf, 0, sizeof(struct stat)); unsigned dos_ftime = 0; dos_ftime = ( (unsigned short)ff_blk.ff_fdate << 16 ) + (unsigned short)ff_blk.ff_ftime; statbuf->st_uid = getuid(); statbuf->st_gid = getgid(); statbuf->st_nlink = 1; statbuf->st_size = ff_blk.ff_fsize; statbuf->st_mode |= READ_ACCESS; if ( !(ff_blk.ff_attrib & 0x07) ) /* no R, H or S bits set */ statbuf->st_mode |= WRITE_ACCESS; if (ff_blk.ff_attrib & 0x10) statbuf->st_mode |= (S_IFDIR | EXEC_ACCESS); /* Set regular file bit. */ statbuf->st_mode |= S_IFREG; /* Time fields. */ statbuf->st_atime = statbuf->st_mtime = statbuf->st_ctime = _file_time_stamp(dos_ftime); if ( ! strcmp(ff_blk.lfn_magic,"LFN32") ) { unsigned xtime; xtime = *(unsigned *)&ff_blk.lfn_ctime; if(xtime) /* May be zero if file written w/o lfn active */ statbuf->st_ctime = _file_time_stamp(xtime); xtime = *(unsigned *)&ff_blk.lfn_atime; if(xtime > dos_ftime) /* Accessed time is date only, no time */ statbuf->st_atime = _file_time_stamp(xtime); } return 0; #undef ff_blk } #endif /* _TARGET_GO32_ */ /***************************************************************************** ** ** ftwalk() traverses directory tree and calls func() for every entri it ** encounters. It supports DOS FAT filesystems under DJGPP. ** *****************************************************************************/ int __ftwalk_process( const char *origin, const char *path, int (*func)( const char* origin, /* origin path */ const char* fname, /* full file name */ const struct stat* st, /* stat or NULL */ int is_link, /* 1 if link */ int flag ), int level = -1 ) { DIR *dir; struct dirent *de; struct stat st; int flag; if ( level != -1 && level == 0) return 0; /* required level reqched */ VString this_path = path; int this_path_len = str_len( this_path ); dir = opendir( this_path ); if (!dir) return 0; /* consider it ok */ while( (de = readdir(dir)) ) { if ( strcmp( de->d_name, "." ) == 0 || strcmp(de->d_name, "..") == 0 ) continue; this_path += de->d_name; int is_link = file_is_link( this_path ); #ifdef _TARGET_GO32_ if (dosstat(dir, &st)) /* dosstat() will never return != 0 */ #else if (stat(this_path, &st)) #endif flag = FTWALK_NS; else if (S_ISDIR(st.st_mode)) flag = FTWALK_D; else flag = FTWALK_F; int r = func( origin, this_path, &st, is_link, flag ); if ( r ) { closedir(dir); return r; } if ( flag == FTWALK_D && !is_link ) { this_path += "/"; r = __ftwalk_process( origin, this_path, func, level - 1 ); if ( r ) { closedir(dir); return r; } str_trim_right( this_path, 1 ); /* remove trailing `/' */ int r = func( origin, this_path, &st, is_link, FTWALK_DX ); if ( r ) { closedir(dir); return r; } } str_sleft( this_path, this_path_len ); } /* while readdir(dir) */ closedir(dir); return 0; } int ftwalk( const char *origin, int (*func)( const char* origin, /* origin path */ const char* fname, /* full file name */ const struct stat* st, /* stat struture or NULL */ int is_link, /* 1 if link */ int flag ), int level ) { int r; if ( !origin || !func || !origin[0] ) return 255; VString o = origin; str_fix_path( o ); if ( !file_is_dir( o ) ) return 255; r = __ftwalk_process( o, o, func, level ); return r; } /***************************************************************************** ** ** get_rc_directory() return application rc directory (and possibly create it) ** returned dir is $HOME/.dir_prefix or $HOME/$RC_PREFIX/dir_prefix depending ** on $RC_PREFIX existence. ** *****************************************************************************/ VString get_rc_directory( const char* dir_prefix ) { VString rc_dir; rc_dir = getenv("HOME"); if ( rc_dir == "" ) rc_dir = "/tmp/"; #ifdef _TARGET_GO32_ str_tr( rc_dir, "\\", "/" ); #endif str_fix_path( rc_dir ); int rcprefix = 1; if (getenv("RC_PREFIX")) rc_dir += getenv("RC_PREFIX"); else rcprefix = 0; str_fix_path( rc_dir ); if ( dir_prefix && dir_prefix[0] ) { if ( rcprefix ) rc_dir += dir_prefix; else { #ifdef _TARGET_GO32_ rc_dir += "_"; #else rc_dir += "."; #endif rc_dir += dir_prefix; } str_fix_path( rc_dir ); } make_path( rc_dir ); return rc_dir; } /***************************************************************************** ** ** EOF ** *****************************************************************************/ vfu-5.09/vfu/0000755000175000017500000000000014444676602011436 5ustar cadecadevfu-5.09/vfu/vfutools.h0000644000175000017500000000123714365574242013472 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUTOOLS_H_ #define _VFUTOOLS_H_ #include "vfu.h" void vfu_tool_classify(); void vfu_tool_rename(); void vfu_tool_seq_rename(); void vfu_tool_replace_sym_org( int swap = 0 ); #endif //_VFUTOOLS_H_ // eof vfutools.h vfu-5.09/vfu/vfuuti.cpp0000644000175000017500000003540314444676602013471 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfu.h" #include "vfuuti.h" #include "vfufiles.h" #include "vfumenu.h" #include "vfusys.h" #include "vfudir.h" #include "vfuopt.h" #include "vfuview.h" /*---------------------------------------------------------------------------*/ VString vfu_readlink( const char* fname ) { fname_t t; t[0] = 0; int l = readlink( fname, t, MAX_PATH - 1 ); if ( l != -1 ) t[l] = 0; VString res = t; return res; } /*---------------------------------------------------------------------------*/ int vfu_update_shell_line( VString &a_line, VString &a_options ) { VString out; VString s; int i = 0; int full = 0; // place-holders while( a_line[i] ) { if ( a_line[i] == '%' ) { char ch = a_line[i+1]; if( ! ch ) break; if( ch == 'h' || ch == 'H' ) // auto-sensing F or G depending on selection count ch = sel_count > 0 ? ch == 'H' ? 'G' : 'g' : ch == 'H' ? 'F' : 'f'; switch( ch ) { case 'r' : /* rescan files after */ case 'R' : a_options += "r"; break; case 'f' : /* file name */ case 'F' : full = ch == 'F'; if( work_mode == WM_ARCHIVE ) { s = files_list_get(FLI)->name(); } else if( full ) { s = files_list_get(FLI)->full_name(); } else { s = files_list_get(FLI)->name(); } out += shell_escape( s ); break; case 'g' : /* list selected filenames */ case 'G' : full = ch == 'G'; int z; for( z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( ! fi->sel ) continue; /* if not one and not selected -- skip */ if( work_mode == WM_ARCHIVE ) { s = files_list_get(z)->name(); } else if( full ) { s = files_list_get(z)->full_name(); } else { s = files_list_get(z)->name(); } out += shell_escape( s ) + " "; } break; case 'e' : /* name only */ case 'E' : /* extension only */ if ( a_line[i+1] == 'e' ) s = str_file_name( files_list_get(FLI)->name() ); else s = files_list_get(FLI)->ext(); out += shell_escape( s ); break; case 's' : /* current file size */ sprintf( s, "%.0f", files_list_get(FLI)->size() ); out += s; break; case '?' : /* prompt user for argument */ if (vfu_get_str( "Enter parameter:", s, HID_SHELL_PAR )) out += s; else return 3; break; case 'd' : /* prompt user for directory */ s = ""; if (vfu_get_dir_name( "Enter directory:", s, 0 )) out += s; else return 3; break; case 'c' : /* current path */ s = work_path; out += shell_escape( s ); break; case 'C' : /* startup dir */ s = startup_path; out += shell_escape( s ); break; case 'a' : /* Archive name */ s = archive_name; out += shell_escape( s ); break; case 'A' : /* Archive path */ s = archive_path; out += shell_escape( s ); break; case 'w' : case 'W' : a_options += "w"; break; case 'i' : a_options += "i"; break; case 'n' : a_options += "n"; break; case '!' : a_options += "!"; break; default : /* chars not recognized are accepted "as is" */ str_add_ch( out, a_line[i+1] ); break; } i += 2; } else { str_add_ch( out, a_line[i] ); i++; } } a_line = out; return 0; } /*---------------------------------------------------------------------------*/ int vfu_break_op() { if (con_kbhit()) if (con_getwch() == 27) { say2( "Press ENTER to cancel or other key to continue..." ); int key = con_getwch(); say2( "" ); if ( key == 13 ) return 1; } return 0; } /*---------------------------------------------------------------------------*/ fsize_t vfu_update_sel_size( int one ) // used before copy/move to calc estimated size { fsize_t size = 0; int z; int need_size_cache_sort = 0; for( z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( one && z != FLI ) continue; /* if one and not current -- skip */ if ( !one && !fi->sel ) continue; /* if not one and not selected -- skip */ if ( fi->is_link() ) continue; /* links does not have own size -- skip */ if ( fi->is_dir() ) { /* this is directory */ fsize_t dir_size = vfu_dir_size( fi->name(), 0 ); need_size_cache_sort = 1; if ( dir_size == -1 ) { /* dir size calculation has been canceled */ size = -1; break; } fi->set_size( dir_size ); size += dir_size; } else { /* this is regular file */ size += fi->size(); } } /* for */ if( need_size_cache_sort ) size_cache_sort(); update_status(); /* */ vfu_redraw(); /* just to redraw before copy/move/etc... */ vfu_redraw_status(); /* just to redraw before copy/move/etc... */ return size; } /*---------------------------------------------------------------------------*/ wchar_t vfu_ask( const wchar_t *prompt, const wchar_t *allowed ) { wchar_t wch = 0; say1( VString( prompt ) ); while(4) { wch = con_getwch(); if( wch == 27 ) return wch; if( str_find( allowed, wch ) >= 0 ) return wch; } return 0; } /*---------------------------------------------------------------------------*/ VString& vfu_expand_mask( VString& mask ) { if ( str_count( mask, "*?" ) > 0 ) return mask; mask += "*"; if ( mask[0] == '.' ) str_ins( mask, 0, "*" ); str_replace( mask, "**", "*" ); return mask; } /*---------------------------------------------------------------------------*/ char* time_str_compact( const time_t tim, char* buf ) { ASSERT( buf ); time_t timenow = time( NULL ); tm tim_tm; localtime_r( &tim, &tim_tm ); const char* tfm; long int tdiff = timenow - tim; if ( tdiff > 6L * 30L * 24L * 60L * 60L /* older than 6 months */ || tdiff < - 23L * 60L * 60L) /* in the future, past next 23 hours */ { tfm = "%b %d %Y"; } else if( abs( tdiff ) < 23L * 60L * 60L ) { /* in the near 23 hours */ tfm = "%a %H:%M:%S"; } else { tfm = "%b %d %H:%M"; } strftime( buf, 16, tfm, &tim_tm ); return buf; } /*---------------------------------------------------------------------------*/ VString size_str_compact( const fsize_t siz ) { char buf[32]; const char* size_str; int units_size = opt.use_si_sizes ? 1000 : 1024; if ( siz < units_size ) { sprintf( buf, "%.0f", siz ); size_str = " B"; } else if ( siz < 1.0 * units_size * units_size ) { sprintf( buf, "%.0f", siz/units_size ); size_str = opt.use_si_sizes ? " KB " : " KiB"; } else if ( siz < 1.0 * units_size * units_size * units_size ) { sprintf( buf, "%.0f", siz/( units_size * units_size ) ); size_str = opt.use_si_sizes ? " MB " : " MiB"; } else if ( siz < 100.0 * units_size * units_size * units_size ) { sprintf( buf, "%.3f", siz/( units_size * units_size * units_size ) ); size_str = opt.use_si_sizes ? " GB " : " GiB"; } else { sprintf( buf, "%.0f", siz/( units_size * units_size * units_size ) ); size_str = opt.use_si_sizes ? " GB " : " GiB"; } vfu_str_comma( buf ); strcat( buf, size_str ); return VString( buf ); } /*---------------------------------------------------------------------------*/ void vfu_beep() { if ( opt.allow_beep ) { con_beep(); } } /*###########################################################################*/ static char hist_menu_hotkeys[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; #define MAXHIST 128 // max history items per id #define HISTIDPAD 8 void vfu_hist_add( int hist_id, const char* str ) { VString hstr = hist_id; str_pad( hstr, HISTIDPAD ); hstr += ","; hstr += str; int z; z = vfu_hist_index( hist_id, str ); if ( z != -1 ) vfu_hist_remove( hist_id, z ); z = vfu_hist_count( hist_id ); while (z >= MAXHIST) { z--; vfu_hist_remove( hist_id, z ); } if (z) z++; history.ins( 0, hstr ); } const char* vfu_hist_get( int hist_id, int index ) { VString hstr = hist_id; str_pad( hstr, HISTIDPAD ); hstr += ","; int i = 0; int z; for ( z = 0; z < history.count(); z++ ) if ( strncmp( hstr, history[z], HISTIDPAD+1 ) == 0 ) { if ( index == -1 || index == i ) return history.get( z ) + HISTIDPAD+1; i++; } return NULL; } char* vfu_hist_get( int hist_id, int index, char* str ) { str[0] = 0; const char* pstr = vfu_hist_get( hist_id, index ); if ( pstr ) strcpy( str, pstr ); return str; } int vfu_hist_index( int hist_id, const char* value ) { int z; int cnt = vfu_hist_count( hist_id ); for ( z = 0; z < cnt; z++ ) if ( strcmp( value, vfu_hist_get( hist_id, z ) ) == 0 ) return z; return -1; } int vfu_hist_count( int hist_id ) { VString hstr = hist_id; str_pad( hstr, HISTIDPAD ); hstr += ","; int cnt = 0; int z; for ( z = 0; z < history.count(); z++ ) cnt += ( strncmp( hstr, history[z], HISTIDPAD+1 ) == 0 ); return cnt; } // use hist_id=-1 and/or index=-1 to remove all void vfu_hist_remove( int hist_id, int index ) { VString hstr = hist_id; str_pad( hstr, HISTIDPAD ); hstr += ","; int i = 0; int z = 0; while( z < history.count() ) { if ( hist_id != -1 && strncmp( hstr, history[z], HISTIDPAD+1 ) != 0 ) { z++; continue; } if ( index != -1 && index != i ) { z++; i++; continue; } history.del( z ); if ( index != -1 ) break; } } int vfu_hist_menu( int x, int y, const wchar_t* title, int hist_id ) { VString str; mb.undef(); int z; int cnt = vfu_hist_count( hist_id ); if ( cnt < 1 ) return -1; if ( cnt > con_max_y() - 9 ) cnt = con_max_y() - 9; // limit to visible space for ( z = 0; z < cnt; z++ ) { // ASSERT( z < str_len( hist_menu_hotkeys ) ); str = ""; str_add_ch( str, z < str_len( hist_menu_hotkeys ) ? hist_menu_hotkeys[z] : ' ' ); str = str + " " + vfu_hist_get( hist_id, z ); mb.push( WString( str ) ); } return vfu_menu_box( x, y, title ); } /*---------------------------------------------------------------------------*/ int __vfu_get_str_hist_id; /* used to keep history id passed here... */ void vfu_get_str_history( int key, WString &w, int &pos ) { if ( __vfu_get_str_hist_id <= 0 ) return; if ( key != UKEY_PGUP && key != UKEY_PGDN ) return; con_chide(); int z = vfu_hist_menu( 5, 5, L"Line History", __vfu_get_str_hist_id ); con_cshow(); if ( z == -1 ) return; w = WString( mb.get(z) + 2 ); str_cut_spc( w ); pos = str_len( w ); } int vfu_get_str( const char *prompt, VString& target, int hist_id, int x, int y ) { if ( x == -1 ) x = 1; if ( y == -1 ) y = con_max_y(); int len = con_max_x() - x; /* FIXME: this is not correct if x and y are specified */ if ( prompt && prompt[0] ) say1( prompt ); say2( "" ); __vfu_get_str_hist_id = hist_id; if ( strcmp( target, "" ) == 0 && vfu_hist_get( hist_id, 0 ) ) target = vfu_hist_get( hist_id, 0 ); WString www = target; int r = TextInput( x, y, "", 1024, len, www, vfu_get_str_history ); target = www; say1( "" ); say2( "" ); __vfu_get_str_hist_id = 0; if( r ) vfu_hist_add( hist_id, target ); return ( r != 0 ); } /*---------------------------------------------------------------------------*/ fname_t vfu_temp_filename; const char* vfu_temp() { strcpy( vfu_temp_filename, tmp_path + "vfu.XXXXXX" ); int fd = mkstemp( vfu_temp_filename ); if( fd >= 0 ) close( fd ); else vfu_temp_filename[0] = 0; return vfu_temp_filename; } fname_t vfu_temp_dirname; const char* vfu_temp_dir() { strcpy( vfu_temp_dirname, tmp_path + "vfu.XXXXXX" ); char* r = mkdtemp( vfu_temp_dirname ); if( ! r ) vfu_temp_dirname[0] = 0; return vfu_temp_dirname; } /*---------------------------------------------------------------------------*/ char* vfu_str_comma( char* target ) { return str_comma( target, COMMA_TYPES[opt.comma_type][0] ); } VString& vfu_str_comma( VString& target ) { return str_comma( target, COMMA_TYPES[opt.comma_type][0] ); } VString vfu_str_comma( fsize_t size ) { VString str; str.fi( size ); return str_comma( str, COMMA_TYPES[opt.comma_type][0] ); } /*###########################################################################*/ #define WCHAR_CTRL_LIST L"\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F" #define WCHAR_PRNT_LIST L"\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240A\u240B\u240C\u240D\u240E\u240F\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241A\u241B\u241C\u241D\u241E\u241F" WString __vfu_translate_controls( const wchar_t *s ) { WString str = s; // 00..1f --> 2400..241f, replace non-printable chars str_tr( str, WCHAR_CTRL_LIST, WCHAR_PRNT_LIST ); return str; } void vfu_con_out( int x, int y, const char *s ) { con_out( x, y, VString( __vfu_translate_controls( WString( s ) ) ) ); } void vfu_con_out( int x, int y, const char *s, int attr ) { con_out( x, y, VString( __vfu_translate_controls( WString( s ) ) ), attr ); } void vfu_con_out( int x, int y, const wchar_t *s ) { con_out( x, y, VString( __vfu_translate_controls( WString( s ) ) ) ); } void vfu_con_out( int x, int y, const wchar_t *s, int attr ) { con_out( x, y, VString( __vfu_translate_controls( WString( s ) ) ), attr ); } /*###########################################################################*/ /* eof vfuuti.cpp */ vfu-5.09/vfu/vfudir.cpp0000644000175000017500000007774314444676602013463 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfudir.h" #include "vfuopt.h" #include "vfuuti.h" #include "vfusys.h" #include "vfufiles.h" #include "vfuview.h" #include "vfumenu.h" VArray size_cache; int size_cache_count; int size_cache_sorted_by_name; /*###########################################################################*/ void __glob_gdn( const char* a_path, const char* a_fnpattern, VArray &a_va, int type = 'D' ) // glob getdirname, type = 'F'ile, 'D'ir, 'A'ny { VString pat = a_fnpattern; pat += "*"; ASSERT( type == 'F' || type == 'D' || type == 'A' ); a_va.undef(); DIR *dir; dirent *de; if ( !a_path || a_path[0] == 0 ) dir = opendir("."); else dir = opendir( a_path ); if (dir) { while ( (de = readdir(dir)) ) { if ( strcmp( de->d_name, "." ) == 0 || strcmp( de->d_name, ".." ) == 0 ) continue; int match_ok = 0; if( opt.no_case_glob ) match_ok = FNMATCH_NC( pat, de->d_name) == 0; else match_ok = FNMATCH( pat, de->d_name) == 0; if ( a_fnpattern[0] == 0 || match_ok ) { VString str; // = a_path; str += de->d_name; if( file_is_dir( a_path + str ) ) { str += "/"; if( type != 'F' ) a_va.push(str); } else { if( type != 'D' ) a_va.push(str); } } } closedir(dir); } } int vfu_get_dir_name( const char *prompt, VString &target_in, int should_exist, int type ) { int res = -1; /* #ifdef _TARGET_UNIX_ leaveok(stdscr, FALSE); #endif */ VArray dir_list; say1( prompt ); say2( "" ); int pos = 0; int page = 0; int wch = 0; int insert = 1; int firsthit = 1; WString target = target_in; pos = str_len( target ); //------------------------------------------------------------------ con_cshow(); // say2( target, firsthit ? cINPUT2 : cINPUT ); while(1) { int mx = con_max_x() - 1; WString target_out = target; if ( (pos < page) || (pos+1 > page + mx) || (page > 0 && pos == page) ) page = pos - mx / 2; if ( page < 0 ) page = 0; str_trim_left( target_out, page ); str_sleft( target_out, mx ); str_pad( target_out, -mx ); if ( page > 0 ) str_set_ch( target_out, 0, L'<' ); if ( str_len( target ) - page > mx ) str_set_ch( target_out, mx-1, L'>' ); say2( VString( target_out ).data(), firsthit ? cINPUT2 : cINPUT ); con_xy( pos-page+1, con_max_y() ); if ( wch == 0 ) wch = con_getwch(); if ( wch == L'\\') wch = L'/'; /* dos hack :)) */ if ( wch == L'/' && str_find( target, L'/' ) == -1 && target[0] == L'~' ) { target.set( tilde_expand( VString( target ) ) ); str_fix_path( target ); pos = str_len( target ); wch = 0; } if ( ( wch == 8 || wch == UKEY_BACKSPACE ) && pos > 0 ) { pos--; str_del( target, pos, 1 ); } else if ( wch == UKEY_CTRL_A && str_len( target ) > 0) { int z = str_len( target )-1; if ( str_get_ch(target, z) == L'/' ) z--; while ( z > 0 && str_get_ch(target,z) != L'/' ) z--; z++; str_sleft(target,z); pos = z; } else if ( wch == 9 && str_len( target ) > 0 ) { int z = -1; dir_list.undef(); VString dmain; /* main/base path */ VString dtail; /* item that should be expanded/glob */ dmain.set( str_file_path( target ) ); dtail.set( str_file_name_ext( target ) ); /* int lastslash = str_rfind(target, '/'); if ( lastslash == -1 ) { dmain = ""; dtail = target; } else { dmain = target; dtail = target; str_sleft( dmain, lastslash+1 ); str_trim_left( dtail, lastslash+1 ); } */ int dtlen = str_len( dtail ); __glob_gdn( dmain, dtail, dir_list, type ); if (dir_list.count()) { if ( dir_list.count() > 1) { int li = 0; // counter int ll = 0; // longest directory entry int xm = 0; // exact match entry for ( li = 0; li < dir_list.count(); li++ ) { int len = strlen( dir_list[li] ); if( len > ll ) ll = len; VString current_dtail; if( dtail != str_copy( current_dtail, dir_list[li], 0, dtlen ) ) continue; xm = li; break; } int mc = 0; // match count int mi = dtlen; // match letter index while(4) { mc = 0; for ( li = 0; li < dir_list.count(); li++ ) { char ch1 = str_get_ch( dir_list[xm], mi ); char ch2 = str_get_ch( dir_list[li], mi ); if( opt.no_case_glob ) { ch1 = toupper( ch1 ); ch2 = toupper( ch2 ); } if ( ch1 == ch2 ) mc++; } if ( mc != dir_list.count() ) break; mi++; // if( dir_list[xm][mi] == 0 || dir_list[li][mi] == 0 ) // break; if( mi >= ll ) break; } VString exact_dtail_max; str_copy( exact_dtail_max, dir_list[xm], 0, mi ); target = dmain + exact_dtail_max; pos = str_len( target ); say2( VString( target ), cINPUT ); con_xy( pos+1, con_max_y() ); vfu_beep(); wch = con_getwch(); if ( wch != 9 ) { dir_list.undef(); continue; } dir_list.sort(); WArray w_dir_list; int di; for( di = 0; di < dir_list.count(); di++ ) { WString ws; ws = dir_list[di]; w_dir_list.push( ws ); } con_chide(); z = vfu_menu_box( 10, 5, L"Complete...", &w_dir_list ); con_cshow(); wch = 0; } else { z = 0; wch = 0; } if ( z >= 0 ) { int sp = str_rfind( target, L'/' ); if( sp < 0 ) target.undef(); else str_copy( target, target, 0, sp + 1 ); target += dir_list[z]; } pos = str_len( target ); dir_list.undef(); if ( wch != 0 ) continue; } else { /* no match found -- cannot complete */ vfu_beep(); } } else if ( wch == 13 ) { res = 1; break; } else if ( wch == 27 ) { target.undef(); res = 0; break; } if ( wch == UKEY_CTRL_U ) { target.undef(); pos = 0; } else if ( wch == UKEY_CTRL_X ) { if ( target[0] == L'~' ) target.set( tilde_expand( VString( target ) ) ); target.set( expand_path( VString( target ) ) ); str_fix_path( target ); pos = str_len( target ); } else if ( wch >= 32 && wch != 8 && wch != UKEY_BACKSPACE && ( ! UKEY_IS_WIDE_CTRL( wch ) ) ) { if( firsthit ) { target.undef(); pos = 0; } if ( ! insert ) str_del( target, pos, 1 ); str_ins_ch( target, pos, wch ); pos++; } else if( wch == UKEY_LEFT ) { if (pos > 0) pos--; } else if( wch == UKEY_RIGHT ) { if (pos < str_len( target )) pos++; } else if ( wch == UKEY_INS ) insert = !insert; else if ( wch == UKEY_HOME ) { if( opt.smart_home_end && pos == 0 ) { int sp = str_len( target ) - 1; if( sp > 0 ) { while( sp > 0 && target[sp] == L'/' ) sp--; while( sp > 0 && target[sp] != L'/' ) sp--; if( sp > 0 ) pos = sp + 1; } } else { pos = 0; } } else if ( wch == UKEY_END ) pos = str_len(target); else if ( wch == UKEY_DEL && pos < str_len(target) ) str_del( target, pos, 1 ); else if ( wch == UKEY_PGUP || wch == UKEY_PGDN ) { con_chide(); int zz = vfu_hist_menu( 5, 5, ( wch == UKEY_PGUP ) ? L"Dir Entry History" : L"ChDir History", ( wch == UKEY_PGUP ) ? HID_GETDIR : HID_CHDIR ); con_cshow(); if (zz != -1) { const char* pc = vfu_hist_get( ( wch == UKEY_PGUP ) ? HID_GETDIR : HID_CHDIR, zz ); if ( pc ) { target = pc; pos = str_len( target ); } } } else if ( wch == UKEY_CTRL_O ) { mb.undef(); mb.push( L"A Add current DATE+TIME" ); mb.push( L"D Add current DATE only" ); mb.push( L"N Add current directory name" ); vfu_menu_box( 20, -10, L" Directory helper" ); int ec = menu_box_info.ec; if( ec == 'A' or ec == 'D' ) { char time_str[32]; time_t timenow = time( NULL ); tm tmnow; localtime_r( &timenow, &tmnow ); if( strftime( time_str, sizeof(time_str) - 1, ec == L'A' ? "%Y%m%d_%H%M%S" : "%Y%m%d", &tmnow ) ) { if( target[-1] != '/' ) target += "_"; target += time_str; } } else if( ec == 'N' ) { VString str = work_path; str_cut_right( str, "/" ); str = str_file_name_ext( str ); str_cut_right( target, L"/" ); target += "/" + WString( str ); } pos = str_len( target ); } wch = 0; firsthit = 0; } con_chide(); //------------------------------------------------------------------ str_cut_spc( target ); if ( res == 1 && target[0] == L'~' ) { target.set( tilde_expand( VString( target ) ) ); str_fix_path( target ); } /* #ifdef _TARGET_UNIX_ leaveok(stdscr, TRUE); #endif */ if ( res == 1 && str_len( target ) > 0 && should_exist && type == 'D' && !dir_exist( VString( target ) )) { vfu_beep(); wchar_t wch = towlower( vfu_ask( L"Directory does not exist! Create? " L"( ENTER=Yes, ESC=cancel )", L"\r" )); if ( wch == 27 ) { res = 0; target = ""; } else if ( wch == 13 ) if ( make_path( VString( target ) )) { if(vfu_ask( L"Cannot create path! ( ESC=cancel, C=continue-anyway )", L"Cc" ) == 27 ) { res = 0; target.undef(); } } } say1(" "); say2(" "); str_cut_spc( target ); if ( str_len( target ) > 0 ) { if( file_is_dir( VString( target ) ) ) str_fix_path( target ); vfu_hist_add( HID_GETDIR, VString( target ) ); } target_in = target; ASSERT( res == 0 || res == 1 ); return res; } /*-----------------------------------------------------------------------*/ void vfu_chdir( const char *a_new_dir ) { fname_t t; VString target; if ( a_new_dir && a_new_dir[0] ) { target = a_new_dir; str_fix_path( target ); } else { target = vfu_hist_get( HID_CHDIR, 0 ); if (!vfu_get_dir_name( "ChDir to? (use keys: TAB, PageUp, PageDown, Ctrl+X, Ctrl+A)", target, 0 )) return; /* get_dir_name canceled */ } /* get_dir_name confirmed */ /* if ( work_path[0] != target[0] && DirTreeChanged && opt.AutoTree ) SaveTree(); */ if ( work_mode == WM_ARCHIVE ) { archive_name = ""; archive_path = ""; } vfu_hist_add( HID_CHDIR, work_path ); char ch = work_path[0]; if (opt.tree_cd) if (!dir_exist( target )) { int z = 0; if ( dir_tree.count() == 0 ) tree_load(); mb.undef(); z = tree_find( target, &mb ); if (z > 1) { z = vfu_menu_box( 10, 5, L"Change dir to..." ); if (z > -1) target = mb.get(z); else return; } else if (z == 1) target = mb.get(0); } VString str = target; if (str[0] == '/') { /* root directory */ target = str; } else { str = work_path + str; str_fix_path( str ); target = str_reduce_path( str ); } if (chdir( target ) != 0) { snprintf( t, sizeof(t), "chdir: %s", target.data() ); say1( t ); say2errno(); return; } else { work_path = target; if ( work_mode == WM_ARCHIVE ) work_mode = WM_NORMAL; } if ( ch != work_path[0] ) tree_drop(); /* drop tree--it is for another drive*/ vfu_read_files(); } /*-----------------------------------------------------------------------*/ void vfu_chdir_history() { int z = vfu_hist_menu( 5, 5, L"ChDir History", HID_CHDIR ); if (z == -1) return; do_draw = 1; //strcpy(opt.LastDir, CPath); vfu_chdir( vfu_hist_get( HID_CHDIR, z ) ); } /*###########################################################################*/ void tree_load() { if( dir_tree.fload( filename_tree ) ) say1( "DirTree load error." ); else { say1( "DirTree loaded ok." ); dir_tree_changed = 0; } } /*-----------------------------------------------------------------------*/ void tree_save() { if( dir_tree.fsave( filename_tree ) ) say1( "DirTree save error." ); else { say1( "DirTree saved ok." ); dir_tree_changed = 0; } } /*-----------------------------------------------------------------------*/ void tree_drop() { if ( dir_tree_changed ) tree_save(); dir_tree.undef(); dir_tree_changed = 0; } /*-----------------------------------------------------------------------*/ void tree_fix() { int z; for( z = dir_tree.count() - 1; z >= 0; z-- ) { VString s1 = dir_tree[z]; VString s2; if (z < dir_tree.count() - 1) s2 = dir_tree[z+1]; else s2 = ""; int i = -1; int n = str_count( s1, "/" ); int p = 0; while(n > 2) { i = str_find( s1, '/', i+1 ); int q = 0; if ( str_len( s2 ) > i ) q = s1[i] != s2[i]; if ( q || ( str_count(s2,"/",i+1) < 2)) { p = 1; str_set_ch(s1, i, '\\'); } n--; } if ( p ) dir_tree.set( z, s1 ); } } /*-----------------------------------------------------------------------*/ fsize_t __tree_rebuild_process( const char* path ) { if ( vfu_break_op() ) return -1; DIR* dir; dirent* de; struct stat st; fsize_t size = 0; fname_t new_name; dir = opendir( path ); if ( !dir ) return 0; while( (de = readdir(dir)) ) { if ( strcmp( de->d_name, "." ) == 0 || strcmp( de->d_name, ".." ) == 0 ) continue; snprintf( new_name, sizeof(new_name), "%s%s", path, de->d_name ); lstat(new_name, &st); int is_link = int(S_ISLNK(st.st_mode)); if (is_link) continue; stat(new_name, &st); int is_dir = S_ISDIR(st.st_mode); if ( is_dir ) { /* directory */ strcat( new_name, "/" ); int z; int trim = 0; for ( z = 0; z < trim_tree.count(); z++ ) { VString trim_temp = trim_tree[z]; str_fix_path( trim_temp ); if ( pathcmp(trim_temp, new_name) == 0 ) { /* trim_tree item found */ trim = 1; break; } } if (trim) continue; /* cut this branch */ int pos = dir_tree.count(); fsize_t dir_size = __tree_rebuild_process( new_name ); if ( dir_size < 0 ) { /* canceled */ closedir(dir); return -1; } dir_tree.ins( pos, new_name ); size_cache_set( new_name, dir_size ); size += dir_size; } else { /* file */ size += st.st_size; } } closedir(dir); /* show some progress :) */ say2( str_dot_reduce( path, con_max_x()-1 ) ); return size; } void tree_rebuild() { dir_tree.undef(); size_cache.undef(); say1( "Rebuilding tree..." ); __tree_rebuild_process( "/" ); tree_fix(); dir_tree_changed = 1; tree_save(); } /*-----------------------------------------------------------------------*/ void tree_draw_item( int page, int index, int hilite ) { if ( page + index >= dir_tree.count() ) return; VString s1 = dir_tree[page+index]; str_trim_right(s1,1); VString s2 = s1; int j = str_rfind( s1,'/'); str_trim_right(s1,str_len(s2)-j-1); str_trim_left(s2,j+1); for(j = 0; j < str_len(s1); j++) { if (s1[j] == '/') str_set_ch(s1,j, '|'); else if (s1[j] == '\\') str_set_ch(s1,j, '\\'); else str_set_ch(s1,j, '+'); } if (opt.tree_compact) { str_replace(s1,"+", ""); str_replace(s1,"|", "| "); str_replace(s1,"\\"," "); str_trim_right(s1,2); s1 += "--"; } else { str_replace(s1,"+", " "); str_replace(s1,"\\", " "); s1 += "--"; } VString str = dir_tree[page+index]; str_tr( str,"\\", "/" ); VString sz; sz.fi( size_cache_get( str ) ); if ( sz == "-1" ) sz = "n/a"; else vfu_str_comma( sz ); str_pad( sz, 14 ); s1 = sz + " " + s1; int m = con_max_x() - 1; /* doesn't speed the code... :) */ if ( str_len( s1 ) > m ) { str_sleft( s1, m ); s2 = ""; } else if ( str_len( s1 ) + str_len( s2 ) > m ) { str_sleft( s2, m - str_len( s1 ) ); } con_xy(1,3+1+index); if (hilite) { con_puts( s1, cBAR ); con_puts( s2, cBAR ); con_ce( cBAR ); } else { con_puts( s1, cSTATUS ); con_puts( s2, cMESSAGE ); con_ce( cSTATUS ); } } /*-----------------------------------------------------------------------*/ void tree_draw_page( ScrollPos &scroll ) { VString str = " "; str_mul( str, con_max_x() ); str = " SiZE DiRECTORY" + str; str_sleft( str, con_max_x()-16 ); vfu_con_out(1,3, str, cHEADER ); int z = 0; for(z = 0; z < scroll.pagesize(); z++) { if (scroll.page() + z <= scroll.max()) { tree_draw_item( scroll.page(), z ); } else { con_xy( 1, 3+1+z ); con_puts( "~", cCYAN ); con_ce( cCYAN ); } } } /*-----------------------------------------------------------------------*/ void tree_draw_pos( ScrollPos &scroll, int opos ) { int z = scroll.pos() - scroll.page(); if ( opos != -1 ) tree_draw_item( scroll.page(), opos ); tree_draw_item( scroll.page(), z, 1 ); VString str; str = dir_tree[scroll.pos()]; str_tr( str,"\\", "/" ); VString sz; sz.fi( size_cache_get( str ) ); vfu_str_comma( sz ); str_pad( sz, 14 ); str = sz + " " + str; str = str_dot_reduce( str, con_max_x()-1 ); say1( str, cINFO ); say2( " Help: R Rebuild, S Incremental search, Z Recalc directory size", cINFO ); show_pos( scroll.pos()+1, scroll.max()+1 ); } /*-----------------------------------------------------------------------*/ void tree_view() { VString str; if (dir_tree.count() == 0) { tree_load(); if (dir_tree.count() == 0) tree_rebuild(); } say1( " " ); int new_pos = tree_index( work_path ); if ( new_pos == -1 ) new_pos = 0; VString search_set; str_add_ch_range( search_set, 'a', 'z' ); str_add_ch_range( search_set, 'A', 'Z' ); str_add_ch_range( search_set, '0', '9' ); search_set.cat( "._-~?*>[]" ); ScrollPos scroll; scroll.set_min_max( 0, dir_tree.count()-1 ); scroll.set_pagesize( FLPS + 2 ); scroll.set_pagestep( OPT_SCROLL_PAGESTEP(opt.scroll_pagestep) ); scroll.go( new_pos ); int key = 0; int opos = -1; int opage = -1; while( key != 27 && key != 13 && key != '-' && toupper( key ) != 'Q' && toupper( key ) != 'X' && key != UKEY_ALT_X ) { if ( key >= 'A' && key <= 'Z' ) key = tolower( key ); if ( key == 's' ) { str = ""; say1( "Enter search pattern: ( use TAB to advance )" ); key = con_getch(); while( str_find( search_set, key ) >= 0 || key == 8 || key == UKEY_BACKSPACE || key == 9 ) { if ( key == 8 || key == UKEY_BACKSPACE ) str_trim_right( str, 1 ); else if ( key != 9 ) str_add_ch( str, key ); say2( str ); if ( dir_tree.count() == 0 ) { key = con_getch(); continue; } int z; if ( key == 9 ) { z = scroll.pos() + 1; if (z > scroll.max() ) z = scroll.min(); } else z = scroll.pos(); int direction = 1; int found = 0; int loops = 0; VString s_mask = str; vfu_expand_mask( s_mask ); while(1) { if ( z > scroll.max() ) z = scroll.min(); if ( z < scroll.min() ) z = scroll.max(); VString str1 = dir_tree[z]; str_trim_right( str1, 1 ); int j = str_rfind(str1,'/'); if (j < 0) str1 = ""; else str_trim_left( str1, j+1 ); found = ( FNMATCH( s_mask, str1 ) == 0 ); if ( found ) break; z += direction; if ( loops++ > dir_tree.count() ) break; } if (found) { scroll.go(z); tree_draw_page( scroll ); tree_draw_pos( scroll, opos ); } key = con_getch(); } say1( "" ); say2( "" ); } else switch( key ) { case UKEY_UP : scroll.up(); break; case UKEY_DOWN : scroll.down(); break; case UKEY_PGUP : scroll.ppage(); break; case UKEY_PGDN : scroll.npage(); break; case UKEY_HOME : scroll.home(); break; case UKEY_END : scroll.end(); break; case 'r' : tree_rebuild(); scroll.set_min_max( 0, dir_tree.count()-1 ); scroll.home(); say1( "Rebuild done." ); break; case 'w' : tree_save(); break; case 'l' : tree_load(); scroll.set_min_max( 0, dir_tree.count()-1 ); scroll.home(); break; case 'z' : case UKEY_CTRL_Z : str = dir_tree[scroll.pos()]; str_tr( str, "\\", "/" ); size_cache_set( str, vfu_dir_size( str ) ); tree_draw_page( scroll ); tree_draw_pos( scroll, opos ); say1( "Done." ); break; } if (opage != scroll.page()) tree_draw_page( scroll ); if (opos != scroll.pos() - scroll.page() || opage != scroll.page()) tree_draw_pos( scroll, opos ); opos = scroll.pos() - scroll.page(); opage = scroll.page(); key = con_getch(); } if ( key == 13 ) { str = dir_tree[scroll.pos()]; str_tr( str, "\\", "/" ); vfu_chdir( str ); } do_draw = 2; } /*###########################################################################*/ #define SIZE_CACHE_OFFSET 15 #define SIZE_CACHE_OFFSET_CLEAN (SIZE_CACHE_OFFSET+8+1) #define SIZE_CACHE_OFFSET_STR "15" void size_cache_load() { size_cache_sorted_by_name = 0; size_cache.set_block_size( 1024*1024 ); size_cache.undef(); size_cache_count = 0; size_cache.fload( filename_size_cache ); // removes old-style size cache entries if( size_cache.count() > 0 && ( size_cache[0][SIZE_CACHE_OFFSET] != '|' || size_cache[0][SIZE_CACHE_OFFSET_CLEAN] != '|' ) ) size_cache.undef(); size_cache_count = size_cache.count(); } void size_cache_save() { size_cache.fsave( filename_size_cache ); } int size_cache_cmp( const char* s1, const char* s2 ) { int off = size_cache_sorted_by_name ? SIZE_CACHE_OFFSET_CLEAN : SIZE_CACHE_OFFSET; /* const unsigned char *ss1 = (const unsigned char *) s1 + off; const unsigned char *ss2 = (const unsigned char *) s2 + off; unsigned char c1, c2; do { c1 = (unsigned char) *ss1++; c2 = (unsigned char) *ss2++; if ( c1 == '\0' ) return c1 - c2; } while (c1 == c2); if( c1 == '/' ) return -1; if( c2 == '/' ) return +1; return c1 - c2; */ return strcmp( s1 + off, s2 + off ); } VString size_cache_compose_key( const char *s, fsize_t size ) { const char *ps; fname_t ss; expand_path( s, ss ); str_fix_path( ss ); // replace / with 0x01 to properly pass strcmp (using / is wrong!) str_tr( ss, "/\x0a\x0b\x0c\x0d", "\x01...." ); ps = ss; char s_adler[16]; char s_size[32]; // MUST BE SIZE_CACHE_OFFSET! sprintf( s_size, "%0" SIZE_CACHE_OFFSET_STR ".0f", size ); sprintf( s_adler, "%08X", (unsigned int)str_adler32( ps ) ); VString str; str = str + s_size + "|" + s_adler + "|" + ps; return str; } int size_cache_index( const char *s ) { if ( size_cache.count() == 0 || size_cache_count == 0 ) return -1; VString str = size_cache_compose_key( s, 0 ); int l = 0; int h = size_cache_count - 1; int m = h; while(4) { int r = size_cache_cmp( size_cache[m], str ); if ( l == m && r != 0 ) return -1; if ( r > 0 ) h = m; else if ( r < 0 ) l = m; else return m; m = ( l + h ) / 2; } } fsize_t size_cache_get( const char *s ) { int z = size_cache_index( s ); if ( z != -1 ) { VString str = size_cache[z]; str_sleft( str, SIZE_CACHE_OFFSET ); return atof( str ); } else return -1; } void size_cache_set( const char *s, fsize_t size, int sort ) { int z = size_cache_index( s ); if ( z != -1 ) { size_cache.set( z, size_cache_compose_key( s, size ) ); } else { size_cache.push( size_cache_compose_key( s, size ) ); if( sort ) size_cache_sort(); } } fsize_t size_cache_get_pending( const char *s ) { VString str = size_cache_compose_key( s, 0 ); for( int i = size_cache_count; i < size_cache.count(); i++ ) { if( size_cache_cmp( size_cache[i], str ) ) continue; str = size_cache[i]; str_sleft( str, SIZE_CACHE_OFFSET ); return atof( str ); } return -1; } // this function is used to add quickly entries to the cache // it REQUIRES that size_cache_sort() is called after it! void size_cache_append( const char *s, fsize_t size, int sort ) { size_cache.push( size_cache_compose_key( s, size ) ); if( sort ) size_cache_sort(); } /* deletes all *s's subdir sizes */ void size_cache_clean( const char *s ) { VString str = size_cache_compose_key( s, 0 ); ssize_t qc = str_len( str ); str_trim_left( str, SIZE_CACHE_OFFSET_CLEAN ); int sl = str_len( str ); int z = 0; int in = 0; while( z < size_cache_count ) { const char* ps = size_cache[z].data() + SIZE_CACHE_OFFSET_CLEAN; if ( ( str_len( size_cache[z] ) >= qc && (ps[sl] == 1 || ps[sl] == 0) && strncmp( ps, str, sl ) == 0 ) ) // ps[sl] == 1 is alternative for "/" see size_cache_compose_key() { size_cache.del( z ); size_cache_count--; if ( size_cache_sorted_by_name ) in = 1; } else { if( size_cache_sorted_by_name && in ) return; z++; } } } void size_cache_sort( int use_names ) { size_cache_sorted_by_name = !!use_names; size_cache.sort( 0, size_cache_cmp ); size_cache_count = size_cache.count(); } /*###########################################################################*/ /* return index in the dir_tree of directory named `s' or -1 if not found */ int __tree_index_last_cache = 0; /* cache-like -- keeps the last found index */ int tree_index( const char *s ) { int z = 0; int i = 0; int sl1; int sl2; if ( dir_tree.count() == 0 ) return -1; const char *s1 = s; sl1 = strlen( s1 ); z = __tree_index_last_cache + 1; while(4) { if ( z >= dir_tree.count() ) z = 0; if ( z == __tree_index_last_cache ) break; const char *s2 = dir_tree[z]; sl2 = strlen( s2 ); if ( sl1 == sl2 ) { i = sl1; // or sl2 ... while ( i >= 0 && (s1[i] == s2[i] || (s1[i] == '/' && s2[i] == '\\')) ) i--; if ( i < 0 ) { __tree_index_last_cache = z; return z; } } ASSERT( z < dir_tree.count() ); ASSERT( z >= 0 ); z++; } return -1; } /*-----------------------------------------------------------------------*/ const char* tree_find( const char *s ) // return full path by dirname { VString str; int z = 0; int sl = strlen( s ); for ( z = 0; z < dir_tree.count(); z++ ) { str = dir_tree[z]; if ( str_len( str ) < sl ) continue; str_sright( str, sl ); if (pathcmp( (const char*)str, s ) == 0) { return dir_tree[z]; } } return NULL; } /*-----------------------------------------------------------------------*/ /* return count of found dirnames and stores them to va */ int tree_find( const char *s, WArray *wa ) { VString str; int z = 0; int sl = strlen( s ); for ( z = 0; z < dir_tree.count(); z++ ) { str = dir_tree[z]; if ( str_len( str ) < sl ) continue; str_sright( str, sl ); if (pathcmp( str, s ) == 0) { str = dir_tree[z]; str_tr( str, "\\", "/" ); wa->push( WString( str ) ); } } return wa->count(); } /*###########################################################################*/ VString DirSizeInfo::str() { VString str; VString ds; // dirs count VString fs; // files count VString ls; // links count VString ts; // files count ds.i( dirs_count ); fs.i( files_count ); ls.i( links_count ); ts.fi( size ); vfu_str_comma( ds ); vfu_str_comma( fs ); vfu_str_comma( ls ); vfu_str_comma( ts ); sprintf( str, "Dirs: %s | Files: %s | Links: %s | Size: %s ( %s )", ds.data(), fs.data(), ls.data(), fsize_fmt( size, opt.use_gib_usage ).data(), ts.data() ); return str; } fsize_t __dir_size_process( const char* path, int mode, dev_t src_dev = 0, DirSizeInfo* size_info = NULL ) { if ( vfu_break_op() ) return -1; DIR* dir; dirent* de; struct stat st; fsize_t size = 0; fname_t new_name; dir = opendir( path ); if ( !dir ) return 0; while( (de = readdir(dir)) ) { if ( strcmp( de->d_name, "." ) == 0 || strcmp( de->d_name, ".." ) == 0 ) continue; snprintf( new_name, sizeof(new_name), "%s%s", path, de->d_name ); lstat(new_name, &st); int is_link = int(S_ISLNK(st.st_mode)); memset( &st, 0, sizeof(st) ); stat(new_name, &st); int is_dir = int(S_ISDIR(st.st_mode)); if ( is_link ) { if ( size_info ) size_info->links_count++; if ( ! ( mode & DIR_SIZE_FOLLOWSYMLINKS ) ) continue; /* skip links */ } if ( is_dir ) { if ( size_info ) size_info->dirs_count++; if ( src_dev && src_dev != st.st_dev ) continue; strcat( new_name, "/" ); fsize_t dir_size = __dir_size_process( new_name, mode, src_dev, size_info ); if ( dir_size == -1 ) { // break operation received, return now closedir(dir); return -1; } else { size += dir_size; size_cache_append( new_name, dir_size ); } } else { fsize_t fs = st.st_size; if ( size_info ) { size_info->files_count++; size_info->size += fs; } size += fs; } } closedir(dir); /* show some progress :) */ say2( str_dot_reduce( path, con_max_x()-1 ) ); return size; } fsize_t vfu_dir_size( const char *s, int sort, int mode, DirSizeInfo* size_info ) { fname_t ss; expand_path( s, ss ); str_fix_path( ss ); if( ! strncmp( ss, "/proc/", 6 ) ) return 0; size_cache_clean( ss ); int src_dev = 0; if( mode & DIR_SIZE_SAMEDEVONLY ) { struct stat st; stat( s, &st); src_dev = st.st_dev; } fsize_t size = __dir_size_process( ss, mode, src_dev, size_info ); if( size == -1 ) return -1; size_cache_append( ss, size, sort ); return size; } /*###########################################################################*/ /* eof vfudir.cpp */ vfu-5.09/vfu/vfuview.cpp0000644000175000017500000003134014444676602013636 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfu.h" #include "vfufiles.h" #include "vfuview.h" #include "vfuopt.h" #include "vfuuti.h" #include "vfusys.h" int sel_mark_pos; int tag_mark_pos; int get_item_color( TF *fi ) { VString str; int z; ASSERT( fi ); if ( ! opt.use_colors ) return cNORMAL; /* don't use colors */ /* try to find file type color first */ str = fi->type_str(); str = "." + str + "."; if ( str != ".." ) { for ( z = cBLACK; z <= chWHITE; z++ ) if ( str_find( ext_colors[z], str ) != -1 ) return z; } /* regular file, try to find extension color */ str = fi->name(); if ( str_get_ch( str, 0 ) == '.' ) str = ".dotfiles"; else { str = fi->ext(); if ( str == "" ) str = "."; } str += "."; if ( opt.lower_case_ext_config ) str_low( str ); // lowercase extension if ( str != ".." ) { for ( z = cBLACK; z <= chWHITE; z++ ) if ( str_find( ext_colors[z], str ) != -1) return z; } /* type string not found too return std color */ return cNORMAL; } /*-----------------------------------------------------------------------*/ VString fsize_fmt( fsize_t fs, int use_gib ) /* return commified number */ { int units_size = opt.use_si_sizes ? 1000 : 1024; fsize_t th = use_gib ? 1024*1024*1024 : 99999999999.0; VString str; if( fs > th ) // 99_999_999_999 11 positions + 3 comma = 14 chars { fsize_t ns = fs / ( units_size * units_size ); if( ns > 99999999.0 || use_gib ) // 99_999_999_MIB 8 positions + 2 commas + units = 14 chars { ns = fs / ( units_size * units_size * units_size ); if( ns > 99999 ) str.fi( int( ns ) ); else sprintf( str, "%.3f", ns ); vfu_str_comma( str ); str += opt.use_si_sizes ? " GB " : " GiB"; } else { if( ns > 99999 ) str.fi( int( ns ) ); else sprintf( str, "%.3f", ns ); vfu_str_comma( str ); str += opt.use_si_sizes ? " MB " : " MiB"; } } else { str.fi( fs ); vfu_str_comma( str ); } return str; } /*-----------------------------------------------------------------------*/ void show_pos( int curr, int all ) { char t[64]; sprintf( t, "%5d of %5d", curr, all ); vfu_con_out( con_max_x() - 13, 3, t, cHEADER ); int x = con_max_x(); int y1 = 4; int y2 = con_max_y() - 2; int y = y2 - y1; if( all <= y ) all = 0; // hide scroller int ss = all > 0 ? y * y / all : 0; // scroller size in scroll space if( ss < 1 ) ss = 1; // min scroller size int vss = y - ss - 1; // available scroll space without scroller size int s1 = all > 0 ? vss * curr / all : 0; int s2 = s1 + ss; // fprintf( stderr, "y %d, ss %d, vss %d, s1 %d, s2 %d\n", y, ss, vss, s1, s2 ); for( int z = 0; z < y; z++ ) { vfu_con_out( x, y1 + z, " ", ( all > 0 && s1 <= z and z <= s2 ) ? CONCOLOR(cBLACK,cCYAN) : cNORMAL ); //vfu_con_out( x, y1 + z, ( s1 <= z and z <= s2 ) ? " " : ".", ( s1 <= z and z <= s2 ) ? CONCOLOR(cBLACK,cCYAN) : cCYAN ); } } /*#######################################################################*/ void vfu_drop_all_views() { for( int z = 0; z < files_list_count(); z++ ) files_list_get( z )->drop_view(); do_draw++; } /*#######################################################################*/ void vfu_draw( int n ) { if ( n < FLP || n > FLP + FLPS ) return; /* we are out of screen -- don't draw */ TF* fi = files_list_get(n); int c = fi->color(); /* color to be used */ VString view = fi->view(); if ( fi->sel ) { str_set_ch( view, sel_mark_pos, '#' ); c = CONCOLOR( cBLACK, cWHITE ); } if ( n == FLI ) { str_set_ch( view, tag_mark_pos , TAGMARKS[opt.tag_mark_type][0] ); str_set_ch( view, tag_mark_pos+1, TAGMARKS[opt.tag_mark_type][1] ); c += cBOLD; /* this is a hack, can be removed w/o warning :) -- more visibility */ if ( c == 120 ) c = cTAG; // 116; // 123; // 63; // 123 //str_replace( view, " ", "_" ); //c += A_UNDERLINE; //c += A_STANDOUT; //str_replace( view, " ", "-" ); //c = CONCOLOR(cWHITE,cBLUE); } vfu_con_out( 1, n - FLP + 4, view, c ); // con_ce( c ); } /*#######################################################################*/ extern const wchar_t *FTIMETYPE[]; /* in vfuopt.cpp */ void vfu_redraw() /* redraw file list and header */ { fname_t t; VString str; str = "Mask: "; str += files_mask; vfu_con_out( 1, 1, VString(str), cINFO ); con_ce( cINFO ); if ( work_mode == WM_ARCHIVE ) vfu_con_out( con_max_x()-34, 1, " [-ARCHIVE-] ", cWARNING ); vfu_con_out( con_max_x()-17, 1, "Press H for help", cINFO); vfu_con_out( con_max_x()-20, 1, "VFU " VFU_VERSION " for help",cINFO); str = "Path: "; str += work_path; if ( work_mode == WM_ARCHIVE ) str += "[" + archive_name + "]/" + archive_path; /* NOTE: to simulate root dir visually */ str = str_dot_reduce( str, con_max_x()-1 ); vfu_con_out( 1, 2, str, cINFO ); con_ce( cINFO ); str = ""; if ( opt.sort_order == 'N' ) str = "NAME"; if ( opt.sort_order == 'M' ) str = "NAME#"; if ( opt.sort_order == 'E' ) str = "EXT"; if ( opt.sort_order == 'A' ) str = "MODE"; if ( opt.sort_order == 'O' ) str = "OWNER"; if ( opt.sort_order == 'G' ) str = "GROUP"; if ( opt.sort_order == 'T' ) str = "MTiME"; if ( opt.sort_order == 'H' ) str = "CTiME"; if ( opt.sort_order == 'C' ) str = "ATiME"; if ( opt.sort_order == 'S' ) str = "SiZE"; if ( opt.sort_order == 'Y' ) str = "TYPE"; str += opt.sort_direction == 'A' ? "+" : "-"; str = "(SORT:" + str + ")"; vfu_con_out( con_max_x() - str_len( str ) + 1, 2, str, cHEADER ); str = ""; t[0] = 0; char *spos = t; if (opt.sort_order == 'D') opt.sort_order = 'T'; /* hack anyway */ if (!opt.long_name_view) { //if ( opt.f_mode ) spos += sprintf( spos, "%10s ", MODE_STRING ); if ( opt.f_mode ) spos += sprintf( spos, "MODE " ); if ( opt.f_owner ) spos += sprintf( spos, " OWNER " ); if ( opt.f_group ) spos += sprintf( spos, " GROUP " ); if ( opt.f_time ) spos += sprintf( spos, " %s TiME ", VString( FTIMETYPE[opt.f_time_type] ).data() ); if ( opt.f_size ) spos += sprintf( spos, " SiZE " ); }; if ( opt.f_mode + opt.f_owner + opt.f_group + opt.f_time + opt.f_size + opt.f_type == 0 ) opt.f_type = 1; /* a hack really :) if all fields are off -- turn on type one */ if ( opt.f_type || opt.long_name_view ) spos += sprintf( spos, "TY" ); tag_mark_pos = strlen( t ); sel_mark_pos = tag_mark_pos + 2; /* spos += */ sprintf( spos, " #NAME %s", opt.long_name_view ? "( long name view, press 0 to disable )" : "" ); str_pad( t, - con_max_x() ); str_sleft( t, con_max_x() ); vfu_con_out( 1, 3, t, cHEADER ); show_pos( FLI+1, files_list_count() ); int z; for ( z = 0; z < FLPS; z++ ) { ASSERT( FLP + z >= 0 ); if ( FLP + z >= files_list_count() || files_list_is_empty( FLP + z ) ) { vfu_con_out( 1, z + 4, "~", cPLAIN ); con_ce( cPLAIN ); } else vfu_draw( FLP + z ); } if ( files_list_count() <= 0 ) vfu_con_out( ( con_max_x() - 20 ) / 2, 10, " *** No files found *** ", cHEADER); } /*-----------------------------------------------------------------------*/ void vfu_redraw_status() /* redraw bottom status, total,free,selected... */ { VString s1; VString s2; VString tmp; /* first line here */ s1 = "Select:"; tmp = sel_count; vfu_str_comma(tmp); str_pad(tmp,15); s1 += tmp; s1 += " Free: "; //tmp = size_str_compact( fs_free ); tmp = fsize_fmt( fs_free, opt.use_gib_usage ); str_pad( tmp, 14 ); s1 += tmp; if (fs_total == 0 || fs_free > fs_total) tmp = " n/a%"; else sprintf( 64, tmp, "%4.1f%%", (double)100 * ((double)fs_free / (double)fs_total)); s1 += " " + tmp + " FSize:"; //tmp = size_str_compact( files_size ); tmp = fsize_fmt( files_size ); str_pad( tmp, 15 ); s1 += tmp; if (fs_total == 0 || files_size > fs_total) tmp = " n/a%"; else sprintf( 64, tmp,"%4.1f%%", (double)100 * ((double)files_size / (double)fs_total)); s1 += " " + tmp; /* second line here */ s2 = "S.Size:"; //tmp = size_str_compact( sel_size ); tmp = fsize_fmt( sel_size ); str_pad(tmp,15); s2 += tmp; s2 += " Total:"; //tmp = size_str_compact( fs_total ); tmp = fsize_fmt( fs_total, opt.use_gib_usage ); str_pad( tmp, 14 ); s2 += tmp; tmp = fs_block_size; str_pad( tmp, 5 ); s2 += " [" + tmp + "]"; tmp = " "; tmp = tmp + user_id_str + "." + group_id_str + "@" + host_name_str; str_padw( tmp, con_max_x() - str_len( s2 ) - 1 ); s2 += tmp; str_pad( s1, - con_max_x() ); str_pad( s2, - con_max_x() ); vfu_con_out( 1, con_max_y()-3, s1, cINFO2 ); vfu_con_out( 1, con_max_y()-2, s2, cINFO2 ); } /*#######################################################################*/ void vfu_nav_up() { if ( files_list_count() == 0) return; if ( FLI == 0 ) return; int old_flp = FLP; file_list_index.up(); if ( old_flp != FLP ) do_draw = 1; else { vfu_draw(FLI+1); vfu_draw(FLI); } } /*-----------------------------------------------------------------------*/ void vfu_nav_down() { if ( files_list_count() == 0 ) return; if ( FLI == files_list_count() - 1 ) return; int old_flp = FLP; file_list_index.down(); if ( old_flp != FLP ) do_draw = 1; else { vfu_draw( FLI-1 ); vfu_draw( FLI ); } } /*-----------------------------------------------------------------------*/ void vfu_nav_ppage() { if ( files_list_count() == 0 ) return; if ( FLP == 0 && FLI == 0 ) return; int old_fli = FLI; int old_flp = FLP; file_list_index.pageup(); if ( old_flp != FLP ) do_draw = 1; else { vfu_draw( old_fli ); vfu_draw( FLI ); } } /*-----------------------------------------------------------------------*/ void vfu_nav_npage() { if ( files_list_count() == 0 ) return; if ( FLP >= files_list_count() - FLPS && FLI == files_list_count() - 1 ) return; int old_fli = FLI; int old_flp = FLP; file_list_index.pagedown(); if ( old_flp != FLP ) do_draw = 1; else { vfu_draw( old_fli ); vfu_draw( FLI ); } } /*-----------------------------------------------------------------------*/ void vfu_nav_home() { if ( files_list_count() == 0 ) return; ASSERT( FLI >= 0 && FLI <= files_list_count() - 1 ); if ( opt.sort_top_dirs && opt.smart_home_end && FLI == 0 ) { int z = 0; while( z < files_list_count() ) { TF *fi = files_list_get(z); if( ! fi->is_dir() ) break; z++; } if( z < files_list_count() ) FLGO( z ); } else FLGO( 0 ); vfu_nav_update_pos(); do_draw = 1; /* if ( files_list_count() == 0 ) return; if ( FLI == 0 ) return; FLGO( 0 ); vfu_nav_update_pos(); do_draw = 1; */ } /*-----------------------------------------------------------------------*/ void vfu_nav_end() { if ( files_list_count() == 0 ) return; ASSERT( FLI >= 0 && FLI <= files_list_count() - 1 ); if ( opt.sort_top_dirs && opt.smart_home_end && FLI == files_list_count() - 1 ) { int z = FLI; while( z >= 0 ) { TF *fi = files_list_get(z); if( fi->is_dir() ) break; z--; } if( z >= 0 ) FLGO( z ); } else FLGO( files_list_count() - 1 ); vfu_nav_update_pos(); do_draw = 1; /* if ( files_list_count() == 0 ) return; if ( FLI >= files_list_count() - 1 ) return; FLGO( files_list_count() - 1 ); vfu_nav_update_pos(); do_draw = 1; */ } /*-----------------------------------------------------------------------*/ void vfu_nav_select() { if ( files_list_count() == 0 ) return; TF *fi = files_list_get(FLI); fsize_t f = fi->size(); if ( f < 0 ) f = 0; /* dirs w/o size i.e. -1 */ if ( fi->sel ) { /* unselect */ sel_count --; sel_size -= f; } else { /* select */ sel_count ++; sel_size += f; } fi->sel = - ( fi->sel - 1 ); /* I know -- !!(fi->sel) ... :) */ vfu_draw( FLI ); vfu_nav_down(); vfu_draw( FLI ); do_draw_status = 1; } /*-----------------------------------------------------------------------*/ void vfu_nav_update_pos() { ASSERT( files_list_count() >= 0 ); if ( FLI < 0 ) FLGO( 0 ); if ( files_list_count() == 0 ) FLGO( 0 ); if ( files_list_count() > 0 && FLI > files_list_count() - 1 ) FLGO( files_list_count() - 1 ); } /* eof vfuview.cpp */ vfu-5.09/vfu/vfu.h0000644000175000017500000002234114444676602012411 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFU_H_ #define _VFU_H_ /*############################################ INCLUDE's ###############*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #if defined(__OpenBSD__) #include #else #include #endif #include #else #if defined(__APPLE__) #include #include #else #include #endif #endif #include "vfusetup.h" #include "vfusys.h" // for attrs_t /*############################################ COMPATIBILITY DEF's #####*/ #define FNMATCH_FLAGS 0 #define FNCASE 1 #define PATH_DELIMITER ":" #define FNMATCH(p,s) sfn_match((p),(s),FNMATCH_FLAGS) #define FNMATCH_NC(p,s) sfn_match((p),(s),SFN_CASEFOLD) #define FNMATCH_OC(p,s,n) sfn_match((p),(s),(n)?SFN_CASEFOLD:FNMATCH_FLAGS) #define pathcmp strcmp #define pathncmp strncmp typedef double fsize_t; /* used as big integer */ typedef char fname_t[MAX_PATH]; /* */ /*############################################ GLOBAL DEFINES #########*/ /* history id's */ #define HID_GREP 10 #define HID_GS_MASK 20 #define HID_GS_GREP 30 #define HID_MKPATH 40 #define HID_FFMASK 50 #define HID_FFPATH 60 #define HID_FFGREP 70 #define HID_EE_TIME 80 // entry edit #define HID_EE_OWNER 90 // entry edit #define HID_SHELL_PAR 100 #define HID_FMASK 110 #define HID_COMMANDS 120 #define HID_GETDIR 130 #define HID_CHDIR 140 #define HID_SEQ_PREFIX 150 #define HID_SEQ_SUFFIX 160 #define HID_SEQ_DIGPOS 170 #define HID_SEQ_START 180 #define HID_OMODE 190 // octal mode /*######################################################################*/ /* file class type */ class TF { VString _name; /* name with extension ( ref. _fname ) */ VString _name_ext; /* extension ( ref. _fname ) */ VString _ext; /* extension ( ref. _fname ) */ struct stat _st; char _type_str[3]; int _is_link; int _is_dir; mode_str_t _mode_str; fsize_t _size; VString _view; /* up to screen width */ int _color; /* view colors */ /* !!! this is used when full name required */ /* and this is not thread-safe :) */ VString _full_name; void reset(); /* reset -- NULL all fields */ void refresh_view(); /* this is called by view() only! */ public: TF(); TF( const char* a_name, const struct stat* a_stat, int a_is_link ); ~TF(); const char* full_name( int fix = 0 ); const char* name() { return (const char*)_name; } const char* name_ext() { return (const char*)_name_ext; } const char* ext() { return (const char*)_ext; } void set_name( const char* a_new_name ); const char* view(); void drop_view(); void update_stat( const struct stat* a_new_stat = NULL, int a_is_link = -1 ); const char* type_str() { return (const char*)_type_str; } const char* mode_str() { return (const char*)_mode_str; } const struct stat* st() { return (const struct stat*)&_st; } void set_size( fsize_t a_new_size ); fsize_t size() { if ( _is_dir && _size == -1 ) return 0; else return _size; } int is_link() { return _is_link; } int is_dir() { return _is_dir; } int color(); /* public member variables */ int sel; /* this saves set/get_sel() functions :) */ int x; /* misc used extra field */ }; /*######################################################################*/ #define WM_NORMAL 0 #define WM_ARCHIVE 1 /* work context */ extern int work_mode; extern VString work_path; /* archive context */ extern VString archive_name; extern VString archive_path; extern VArray archive_extensions; extern VString external_panelizer; extern VArray list_panelizer; /* file list statistics */ extern fsize_t files_size; extern int sel_count; extern fsize_t sel_size; /* file system statistics */ extern fsize_t fs_free; extern fsize_t fs_total; extern fsize_t fs_block_size; /* index in the files list */ /* NOTE: following defines are kept for historical reasons :) */ extern ScrollPos file_list_index; #define FLI (file_list_index.pos()) #define FLP (file_list_index.page()) #define FLPS (file_list_index.pagesize()) #define FLMIN (file_list_index.min()) #define FLMAX (file_list_index.max()) #define FLGO(n) (file_list_index.go(n)) #define FLGET(n) (files_list_get(n)) #define FLCUR (files_list_get(FLI)) /* some world wide variables */ extern VString startup_path; extern VString home_path; extern VString tmp_path; extern VString rc_path; /* files masks */ extern VString files_mask; extern VArray files_mask_array; /* misc */ extern int print_help_on_exit; extern VString last_inc_search; /*############################################ GLOBAL STRUCTS #########*/ extern VArray dir_tree; extern int dir_tree_changed; extern WArray file_find_results; // filefind results extern VArray path_bookmarks; /*######################################################################*/ extern VArray user_externals; extern VArray history; extern VArray see_filters; extern VArray panelizers; extern WArray mb; /* menu boxes */ extern VArray trim_tree; extern VArray view_profiles; extern VString view_profile; /*############################################ CONFIG SETTINGS #########*/ extern VString ext_colors[16]; extern VString shell_browser; extern VString shell_editor; extern VString shell_diff; extern VString shell_prog; extern VString user_id_str; extern VString group_id_str; extern VString host_name_str; extern VString filename_opt; extern VString filename_conf; extern VString filename_history; extern VString filename_tree; extern VString filename_size_cache; extern VString filename_ffr; /* file find results */ /*######################################################################*/ extern int do_draw; extern int do_draw_status; /*######################################################################*/ /* Message issues */ void say( int line, int attr, const char* format, ... ); void say1(const char *a_str, int attr = cMESSAGE ); void say2(const char *a_str, int attr = cMESSAGE ); void say2errno(); void saycenter( int line, int attr, const char *a_str ); void say1center(const char *a_str, int attr = cMESSAGE ); void say2center(const char *a_str, int attr = cMESSAGE ); void log_debug( const char* format, ... ); /* Main things */ void vfu_help(); void vfu_help_cli(); void vfu_init(); void vfu_run(); void vfu_cli(); void vfu_done(); void vfu_reset_screen(); void vfu_signal( int sig ); void vfu_exit_path( const char *a_path ); int vfu_exit( const char* a_path ); void vfu_options(); void vfu_toggle_view_fields( wchar_t wch ); /* Support op's */ void vfu_shell( const char* a_command, const char* a_options ); void vfu_tools(); void vfu_command(); void vfu_file_find( int menu ); void vfu_file_find_results(); void vfu_directory_sizes( wchar_t wch ); void vfu_change_file_mask( const char* a_new_mask ); void bookmark_goto( wchar_t wch ); void bookmark_set( int a_n, const char* a_path ); const char* bookmark_get( int a_n ); void bookmark_hookup(); void update_status(); int vfu_user_external_find( wchar_t key, const char* ext, const char* type, VString *shell_line ); void vfu_user_external_exec( wchar_t key ); void vfu_user_menu(); void vfu_inc_search( int use_last_one = 0 ); void vfu_goto_filename( const char* fname ); /* Main files op's */ int vfu_edit_attr( char *attrs ); void vfu_edit_entry( ); void vfu_rename_file_in_place(); void vfu_browse( const char* a_fname, int no_filters = 0 ); void vfu_browse_selected_files(); void vfu_edit( const char* a_fname ); void vfu_action_plus( wchar_t ); void vfu_action_minus( int mode = 0 ); void vfu_global_select(); void vfu_sort_menu(); void vfu_read_files_menu(); void vfu_clipboard( int act ); void vfu_jump_to_mountpoint( int all ); #endif//_VFU_H_ vfu-5.09/vfu/vfudir.h0000644000175000017500000000472514365574242013115 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUDIR_H_ #define _VFUDIR_H_ #include "vfu.h" extern VArray size_cache; extern int size_cache_count; /*###########################################################################*/ int vfu_get_dir_name( const char *prompt, VString &target, int should_exist = 1, int type = 'D' ); void vfu_chdir( const char *a_new_dir ); void vfu_chdir_history(); /*###########################################################################*/ void tree_view(); void tree_load(); void tree_save(); void tree_drop(); void tree_rebuild(); void tree_fix(); void tree_draw_item( int page, int index, int hilite = 0 ); void tree_draw_page( ScrollPos &scroll ); void tree_draw_pos( ScrollPos &scroll, int opos ); /*###########################################################################*/ int tree_index( const char *s ); const char* tree_find( const char *s ); /* return full path by dirname */ /* return count of found dirnames and stores them to sc */ int tree_find( const char *s, WArray *wa ); void size_cache_load(); void size_cache_save(); VString size_cache_compose_key( const char *s, fsize_t size ); int size_cache_index( const char *s ); fsize_t size_cache_get( const char *s ); void size_cache_set( const char *s, fsize_t size, int sort = 1 ); void size_cache_append( const char *s, fsize_t size, int sort = 0 ); fsize_t size_cache_get_pending( const char *s ); void size_cache_clean( const char *s ); void size_cache_sort( int use_names = 0 ); /*###########################################################################*/ #define DIR_SIZE_NORMAL 0 #define DIR_SIZE_FOLLOWSYMLINKS 2 #define DIR_SIZE_SAMEDEVONLY 4 #define DIR_SIZE_NO_CACHECLEAN 128 struct DirSizeInfo { DirSizeInfo() { reset(); }; void reset() { dirs_count = 0; files_count = 0; links_count = 0; size = 0; }; int dirs_count; int files_count; int links_count; fsize_t size; VString str(); }; fsize_t vfu_dir_size( const char *s, int sort = 1, int mode = DIR_SIZE_NORMAL, DirSizeInfo* size_info = NULL ); #endif //_VFUDRI_H_ vfu-5.09/vfu/vfumenu.cpp0000644000175000017500000000255514365574242013635 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include #include "vfu.h" #include "vfuopt.h" #include "vfumenu.h" #include "vfuview.h" int vfu_toggle_box( int x, int y, const wchar_t *title, ToggleEntry* toggles ) { menu_box_info.bo = opt.menu_borders; menu_box_info.cn = cMENU_CN; menu_box_info.ch = cMENU_CH; menu_box_info.ti = cMENU_TI; int z = con_toggle_box( x, y, title, toggles, &menu_box_info ); vfu_redraw(); return z; } int vfu_menu_box( int x, int y, const wchar_t *title, WArray *wa ) { menu_box_info.bo = opt.menu_borders; menu_box_info.cn = cMENU_CN; menu_box_info.ch = cMENU_CH; menu_box_info.ti = cMENU_TI; int z = con_menu_box( x, y, title, wa, 0, &menu_box_info ); vfu_redraw(); return z; } int vfu_menu_box( const wchar_t* title, const wchar_t* menustr, int row ) { WArray wmb = str_split( L",", menustr ); if ( row == -1 ) row = con_max_y() - 5 - wmb.count(); return vfu_menu_box( 50, row, title, &wmb ); } // eof vfumenu.cpp vfu-5.09/vfu/vfucopy.cpp0000644000175000017500000010320614444676602013637 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfu.h" #include "vfudir.h" #include "vfumenu.h" #include "vfufiles.h" #include "vfuview.h" #include "vfusys.h" #include "vfucopy.h" #include /**************************************************************************** ** ** globals ** ****************************************************************************/ const char *CM_DESC[] = { "COPY", "MOVE", "SYMLINK" }; char *copy_buff = NULL; int ignore_copy_errors = 0; /* actually it is used for copy/move/erase */ /* clipboard ***************************************************************/ VTrie clipboard; CopyInfo clipboard_copy_info; /**************************************************************************** ** ** utilities ** ****************************************************************************/ fsize_t device_free_space( const char *target ) { struct statfs stafs; int res = statfs( str_file_path( target ), &stafs ); if( res == 0 ) return ((fsize_t)(stafs.f_bsize)) * stafs.f_bfree; return 0; } fsize_t device_avail_space( const char *target ) { struct statfs stafs; int res = statfs( str_file_path( target ), &stafs ); if( res == 0 ) return ((fsize_t)(stafs.f_bsize)) * stafs.f_bavail; return 0; } /* * return 0 if src and dst are actually the same file */ int file_is_same( const char *src, const char *dst ) { struct stat st1; struct stat st2; if(stat( src, &st1 )) return 1; if(stat( dst, &st2 )) return 1; return !( st1.st_dev == st2.st_dev && /* same device */ st1.st_ino == st2.st_ino ); /* same inode */ } /* return 0 if src and dst are on the same device */ int device_is_same( const char *src, const char *dst ) { char *ch; struct stat st1; struct stat st2; fname_t _f1; fname_t _f2; strcpy( _f1, src ); ch = strrchr( _f1, '/' ); if (ch == NULL) _f1[0] = 0; else ch[1] = 0; strcat( _f1, "." ); strcpy( _f2, dst ); ch = strrchr( _f2, '/' ); if (ch == NULL) _f2[0] = 0; else ch[1] = 0; strcat( _f2, "." ); if(stat( _f1, &st1 )) return 1; if(stat( _f2, &st2 )) return 1; return !( st1.st_dev == st2.st_dev ); } /*###########################################################################*/ void show_copy_pos( fsize_t a_fc, /* file under copy current pos */ fsize_t a_fa, /* file under copy all size */ long a_et, /* elapsed time for current file copy */ CopyInfo *copy_info ) /* totals info */ { char t[128]; fsize_t c1 = a_fc; fsize_t a1 = a_fa; fsize_t c2 = copy_info->current_size; fsize_t a2 = copy_info->files_size; long t1 = a_et; long t2 = copy_info->elapsed_time; long eta = ( ( t1 + t2 ) * a2 ) / ( c1 + c2 ) - ( t1 + t2 ); int eta_h = eta / ( 60*60 ); int eta_m = eta / 60; int eta_v; char eta_c; if( eta_h > 0 ) { eta_v = eta_h; eta_c = 'h'; } else if( eta > 99 && eta_m > 0 ) { eta_v = eta_m; eta_c = 'm'; } else { eta_v = eta; eta_c = 's'; } int speed = -1; // if( t1 > 0 ) speed = ( c1 / ( 1024 * 1024 ) ) / t1; // current MiB/s if( t1 + t2 > 0 ) speed = ( ( c1 + c2 ) / ( 1024 * 1024 ) ) / ( t1 + t2 ); // total MiB/s ASSERT( a1 >= 0 && a2 >= 0 ); if ( a1 < 1 ) a1 = 1; if ( a2 < 1 ) a2 = 1; if ( c1 == a1 ) /* hack, every single 100% each is not meaningfull really */ sprintf( t, " %%%5.1f @%3dM/s%3d%c ", (100.0*(c1+c2))/a2, speed, eta_v, eta_c ); else sprintf( t, "%5.1f%%%5.1f @%3dM/s%3d%c ", (100.0*c1)/a1, (100.0*(c1+c2))/a2, speed, eta_v, eta_c ); vfu_con_out( con_max_x() - 24, con_max_y(), t, cSTATUS2 ); } /*###########################################################################*/ /* this will return 1 if copy should proceed and 0 if should not if destination exists it asks for interaction or proceed by the last answer... if dest. doesn't exist -- always return 1 */ int over_if_exist( const char* src, const char *dst, CopyInfo* copy_info ) { if ( access( dst, F_OK) ) return 1; /* doesn't exist -- copy... */ if ( copy_info->over_mode == OM_NEVER ) return 0; /* skip it */ if ( copy_info->over_mode == OM_ALWAYS ) return 1; /* overwrite! */ struct stat stat_src; struct stat stat_dst; stat( src, &stat_src ); stat( dst, &stat_dst ); if ( copy_info->over_mode == OM_ALWAYS_IF_MTIME && stat_src.st_mtime > stat_dst.st_mtime ) return 1; /* newer mtime, do it! */ int wch = 0; while(4) { vfu_redraw(); vfu_redraw_status(); VString str; char sttime[32]; char s_t = (stat_src.st_mtime == stat_dst.st_mtime)?'*':' '; // same time char s_s = (stat_src.st_size == stat_dst.st_size)?'*':' '; // same size fname_t t; time_str_compact( stat_src.st_mtime, sttime); str = stat_src.st_size; vfu_str_comma(str); str_pad( str, 15 ); snprintf( t, sizeof(t), "SRC: %s%c %s%c %s", sttime, s_t, str.data(), s_s, src ); say1( t ); time_str_compact(stat_dst.st_mtime, sttime); str = stat_dst.st_size; vfu_str_comma(str); str_pad( str, 15 ); snprintf( t, sizeof(t), "DST: %s%c %s%c %s", sttime, s_t, str.data(), s_s, dst ); say2( t ); vfu_beep(); vfu_menu_box( L"Overwrite", L"Y Yes,N No,A Always overwrite,V Never overwrite,I If newer (MODIFY),W Always if newer (MODIFY),D View differences, Abort (ESC)", -1 ); wch = menu_box_info.ec; if( wch == L'D' ) { VString diff = vfu_temp(); VString cmd; cmd = shell_diff + " " + shell_escape( dst ) + " " + shell_escape( src ) + " > " + diff; if(system( cmd ) != 0) { say1( "Cannot execute command: " + cmd ); say2errno(); } else { vfu_browse( diff ); unlink( diff ); } continue; } break; } say1( "" ); say2( "" ); switch (wch) { case L'Y' : return 1; case L'N' : return 0; case L'A' : copy_info->over_mode = OM_ALWAYS; return 1; case L'V' : copy_info->over_mode = OM_NEVER; return 0; case L'I' : return ( stat_src.st_mtime > stat_dst.st_mtime ); break; case L'W' : copy_info->over_mode = OM_ALWAYS_IF_MTIME; return 0; default : copy_info->abort = 1; return 0; } return 1; } /*###########################################################################*/ int vfu_copy_mode( const char* src, const char* dst ) { struct stat st; if (stat( src, &st )) return 1; /* FIXME: or silent? */ /* copy mode */ mode_str_t mode_str; file_get_mode_str( st.st_mode, mode_str ); file_set_mode_str( dst, mode_str ); /* copy access/modify time */ utimbuf utb = { 0, 0 }; utb.actime = st.st_atime; utb.modtime = st.st_mtime; utime( dst, &utb ); /* copy owner/group */ if (opt.copy_keep_mode) if (chown( dst, st.st_uid, st.st_gid )) say1( "Cannot change file mode" ); return 0; } /*************************************************************************** ** ** COPY/MOVE/SYMLINK ** ****************************************************************************/ /* copy/move ***************************************************************/ // return 0 for ok int __vfu_file_copy( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ fsize_t size = file_size( src ); if ( size == -1 ) return 1; if ( ! over_if_exist( src, dst, copy_info ) ) { copy_info->current_size += size; /* consider it ok */ copy_info->skipped_count++; return copy_info->abort ? CR_ABORT : CR_SKIP; /* ok */ } if ( file_exist( dst ) ) { /* destination file exists */ if ( file_is_same( src, dst ) == 0 ) { copy_info->skipped_count++; return CR_SKIP; /* dst is src actually */ } __vfu_file_erase( dst ); /* overwrite! */ } /* progress report */ VString str = dst; str = str_dot_reduce( str, con_max_x() - 10 ); str = "COPY TO: " + str; say1( str ); vfu_con_out( 1, con_max_y(), copy_info->description, cMESSAGE ); if ( ! copy_info->no_free_check && ! copy_info->no_info ) { fsize_t dev_free = device_avail_space( dst ); if (size > dev_free ) { vfu_beep(); say1( VString() + "Insufficient disk space! Free: " + vfu_str_comma( dev_free ) + ", Required: " + vfu_str_comma( size ) ); say2( dst ); vfu_menu_box( L"Error prompt", L"C Continue anyway,S Skip file,N No free space check, Abort (ESC)", -1 ); switch (menu_box_info.ec) { case L'C' : break; case L'S' : copy_info->skipped_count++; return CR_SKIP; break; /* skip it */ case L'N' : copy_info->no_free_check = 1; break; default : copy_info->abort = 1; return CR_ABORT; break; /* abort! */ } } } ASSERT( copy_buff ); if ( copy_buff == NULL ) return 1; FILE *f1 = NULL; FILE *f2 = NULL; f1 = fopen( src, "rb" ); if (!f1) return 1; f2 = fopen( dst, "wb" ); if (!f2) { fclose(f1); return 1; } long z = 0; fsize_t cp = 0; /* current position in file */ int aborted = 0; time_t timer_start = time(NULL); long elapsed_time = 0; long elapsed_break = 0; do { time_t timer_break = time(NULL); if ( vfu_break_op() ) { aborted = 1; break; } elapsed_break += time(NULL) - timer_break; z = fread( copy_buff, 1, COPY_BUFFER_SIZE, f1 ); if (z > 0) z = fwrite( copy_buff, 1, z, f2 ); if (z == -1) { fclose(f1); fclose(f2); unlink( dst ); /* remove dst if partially copied */ return 1; } cp += z; //ASSERT( cp <= size ); elapsed_time = time(NULL) - timer_start - elapsed_break; show_copy_pos( cp, size, elapsed_time, copy_info ); } while ( z == COPY_BUFFER_SIZE ); fclose(f1); fclose(f2); if ( cp < size ) { unlink( dst ); /* remove dst if partially copied */ if ( aborted ) return CR_ABORT; return 1; } //ASSERT( cp == size ); if ( vfu_copy_mode( src, dst ) ) return 1; copy_info->current_size += size; copy_info->elapsed_time += elapsed_time; show_copy_pos( 1, 1, 0, copy_info ); return 0; } /*---------------------------------------------------------------------------*/ int __vfu_file_move( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ if ( device_is_same( src, dst ) == 0 ) { /* same device */ if ( ! over_if_exist( src, dst, copy_info ) ) { copy_info->skipped_count++; return copy_info->abort ? CR_ABORT : CR_SKIP; /* ok */ } if ( file_exist( dst ) ) { if ( file_is_same( src, dst ) == 0 ) { copy_info->skipped_count++; return CR_SKIP; /* dst is src actually */ } /* FIXME: what if dst is symlink? */ if ( __vfu_file_erase( dst ) ) return 1; } if ( rename( src, dst ) ) return 1; } else { /* src and dst devices are different */ say2( "MOVING FILE" ); int r; r = __vfu_file_copy( src, dst, copy_info ); if ( r == CR_SKIP ) return CR_SKIP; if ( r == CR_ABORT ) return CR_ABORT; if ( r ) return 1; r = __vfu_file_erase( src ); if ( r && r != CR_SKIP ) return 1; } return 0; } /*---------------------------------------------------------------------------*/ int __vfu_dir_copy( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ VString fname_src; /* used for directory entries */ VString fname_dst; /* used for directory entries */ if ( vfu_break_op() ) return CR_ABORT; /* canceled */ if ( file_is_same( src, dst ) == 0 ) return CR_SKIP; if ( make_path( dst ) ) { if ( ignore_copy_errors ) return CR_ABORT; /* cancel operation */ say1( dst ); say2errno(); vfu_menu_box( L"Create dir error", L"C Continue anyway,I Ignore further errors, Abort (ESC)", -1 ); if ( menu_box_info.ec != L'C' ) return CR_ABORT; /* cancel operation */ if ( menu_box_info.ec != L'I' ) { ignore_copy_errors = 1; return CR_ABORT; /* cancel operation */ } } DIR *dir; dirent *de; dir = opendir( src ); if (!dir) return 1; /* FIXME: report error? */ while( (de = readdir(dir)) ) { if (strcmp( de->d_name, ".") == 0) continue; if (strcmp( de->d_name, "..") == 0) continue; fname_src = src; fname_src += "/"; fname_src += de->d_name; fname_dst = dst; fname_dst += "/"; fname_dst += de->d_name; while(4) { int r = __vfu_copy( fname_src, fname_dst, copy_info ); if ( r == CR_ABORT ) { closedir(dir); return CR_ABORT; } if ( r && r != CR_SKIP ) { if ( ignore_copy_errors ) break; vfu_beep(); say1( fname_dst ); say2errno(); vfu_menu_box( L"Copy/Move/SymLink error", L"T Try again,S Skip/continue,I Ignore further errors, Abort (ESC)" ); if ( menu_box_info.ec == L'T' ) continue; /* while(4) */ else if ( menu_box_info.ec == L'S' ) { copy_info->skipped_count++; break; /* consider it ok */ }; if ( menu_box_info.ec == L'I' ) { ignore_copy_errors = 1; break; } else { closedir(dir); return CR_ABORT; } } else break; /* copy (r) is ok -- exit error (while(4)) loop */ } /* while(4) */ } /* readdir() */ closedir( dir ); if ( vfu_copy_mode( src, dst ) ) return 1; return 0; } /*---------------------------------------------------------------------------*/ int __vfu_dir_move( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ if ( file_is_same( src, dst ) == 0 ) return CR_SKIP; if ( device_is_same( src, dst ) == 0 ) { /* same device */ if ( rename( src, dst ) ) return 1; } else { /* different devices */ if ( __vfu_dir_copy( src, dst, copy_info ) || copy_info->skipped_count > 0 ) { vfu_beep(); say1( "There were errors or files were skipped! You have to erase dir manually." ); vfu_menu_box( L"Copy/Move error", L"C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != L'C' ) return CR_ABORT; else return CR_SKIP; } /* NOTE: whatever __vfu_dir_copy() returns it is considered as error even if it is CR_SKIP!, i.e. directory never erased unless everything went ok */ if ( __vfu_dir_erase( src ) ) return 1; } return 0; } /*---------------------------------------------------------------------------*/ int __vfu_link_copy( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ if ( ! over_if_exist( src, dst, copy_info ) ) { copy_info->skipped_count++; return copy_info->abort ? CR_ABORT : CR_SKIP; /* ok */ } if ( file_exist( dst ) ) { /* destination file exists */ if ( file_is_same( src, dst ) == 0 ) { copy_info->skipped_count++; return CR_SKIP; /* dst is src actually */ } __vfu_file_erase( dst ); /* overwrite! */ } fname_t t; int z = readlink( src, t, sizeof(t)-1); if (z < 1) return 1; t[z] = 0; if (symlink( t, dst ) == -1) return 1; /* FIXME: should we keep src mode? does links have this? */ return 0; } /*---------------------------------------------------------------------------*/ int __vfu_link_move( const char* src, const char* dst, CopyInfo* copy_info ) { int r; r = __vfu_link_copy( src, dst, copy_info ); if ( r == CR_SKIP ) return CR_SKIP; if ( r ) return 1; r = __vfu_link_erase( src ); if ( r && r != CR_SKIP ) return 1; return 0; } /* erase *******************************************************************/ // return 0 for ok, CR_ABORT for cancel, else for error int __vfu_dir_erase( const char* target, fsize_t* bytes_freed ) { errno = 0; /* clear error status */ VString fname; /* used for directory entries */ VString s; if ( vfu_break_op() ) return CR_ABORT; /* canceled */ if (!file_exist( target )) return 1; /* make it writeable so we can erase files in it */ file_set_mode_str( target, MODE_WRITE_ON ); DIR *dir; dirent *de; dir = opendir( target ); if (!dir) return 1; /* FIXME: report error? */ while( (de = readdir(dir)) ) { if (strcmp( de->d_name, ".") == 0) continue; if (strcmp( de->d_name, "..") == 0) continue; fname = target; fname += "/"; fname += de->d_name; while(4) { /* progress report */ say1( fname ); int r = __vfu_erase( fname, bytes_freed ); if ( r == CR_ABORT ) { closedir(dir); return CR_ABORT; } if ( r && r != CR_SKIP ) { if ( ignore_copy_errors ) break; vfu_beep(); say1( fname ); say2errno(); vfu_menu_box( L"Erase error", L"T Try again,S Skip/continue,I Ignore further errors, Abort (ESC)" ); if ( menu_box_info.ec == 'T' ) continue; /* while(4) */ else if ( menu_box_info.ec == 'S' ) break; /* consider it ok */ if ( menu_box_info.ec == 'I' ) { ignore_copy_errors = 1; break; } else { closedir(dir); return CR_ABORT; } } else break; } /* while(4) */ } /* readdir() */ closedir( dir ); /* show bytes freed if required */ if ( bytes_freed ) { VString t; t.fi( *bytes_freed ); vfu_str_comma( t ); VString tt; if( *bytes_freed > 1024*1024 ) { tt = fsize_fmt( *bytes_freed, opt.use_gib_usage ).data(); tt = "( " + tt + " ) "; } t = "ERASE: " + t + " bytes " + tt + "freed."; say2(t); } say1( target ); return ( rmdir( target ) != 0 ); } /*---------------------------------------------------------------------------*/ int __vfu_file_erase( const char* target, fsize_t* bytes_freed ) { errno = 0; /* clear error status */ fsize_t target_size = 0; if ( bytes_freed ) target_size = file_size( target ); int r = unlink( target ); if ( r == 0 && bytes_freed ) *bytes_freed += target_size; return (r != 0); } /*---------------------------------------------------------------------------*/ int __vfu_link_erase( const char* target, fsize_t* bytes_freed __attribute__((unused)) ) { errno = 0; /* clear error status */ return (unlink( target ) != 0); } /* shells, call __*_*_*() above ********************************************/ int __vfu_copy( const char* src, const char* dst, CopyInfo* copy_info ) { int r = 0; if ( file_is_link( src ) ) r = __vfu_link_copy( src, dst, copy_info ); /* symlink */ else if ( file_is_dir( src ) ) r = __vfu_dir_copy( src, dst, copy_info ); /* directory */ else r = __vfu_file_copy( src, dst, copy_info ); /* regular file */ return r; } /*---------------------------------------------------------------------------*/ int __vfu_move( const char* src, const char* dst, CopyInfo* copy_info ) { int r = 0; if ( file_is_link( src ) ) r = __vfu_link_move( src, dst, copy_info ); /* symlink */ else if ( file_is_dir( src ) ) r = __vfu_dir_move( src, dst, copy_info ); /* directory */ else r = __vfu_file_move( src, dst, copy_info ); /* regular file */ return r; } /*---------------------------------------------------------------------------*/ int __vfu_symlink( const char* src, const char* dst, CopyInfo* copy_info ) { errno = 0; /* clear error status */ if ( ! over_if_exist( src, dst, copy_info ) ) { copy_info->skipped_count++; return copy_info->abort ? CR_ABORT : CR_SKIP; /* ok */ } if ( file_exist( dst ) ) { if ( file_is_same( src, dst ) == 0 ) { copy_info->skipped_count++; return CR_SKIP; /* dst is src actually */ } /* FIXME: what if dst is symlink? */ if ( __vfu_file_erase( dst ) ) return 1; } int r = symlink( src, dst ); return ( r != 0 ); } /*---------------------------------------------------------------------------*/ int __vfu_erase( const char* target, fsize_t* bytes_freed ) { int r = 0; if ( file_is_link( target ) ) r = __vfu_link_erase( target, bytes_freed ); /* symlink */ else if ( file_is_dir( target ) ) r = __vfu_dir_erase( target, bytes_freed ); /* directory */ else r = __vfu_file_erase( target, bytes_freed ); /* regular file */ return r; } /* high-level interface functions ******************************************/ void __copy_calc_totals( CopyInfo ©_info, int a_one ) { if ( opt.copy_calc_totals ) { say1( "Calculating files size. Press ESCAPE to cancel calculation." ); copy_info.files_size = vfu_update_sel_size( a_one ); copy_info.files_count = a_one ? 1 : sel_count; /* not used */ copy_info.current_size = 0; copy_info.current_count = 0; /* not used */ if ( copy_info.files_size == -1 ) copy_info.no_info = 1; } else copy_info.no_info = 1; } void __copy_calc_totals_clipboard( CopyInfo ©_info ) { if ( opt.copy_calc_totals ) { say1( "Calculating files size. Press ESCAPE to cancel calculation." ); copy_info.files_size = 0; VArray va = clipboard.keys(); for( int i = 0; i < va.count(); i++ ) { struct stat _st; stat( va[i], &_st ); int _is_link = S_ISLNK(_st.st_mode ); int _is_dir = S_ISDIR(_st.st_mode ); if( _is_dir && ! _is_link ) copy_info.files_size += vfu_dir_size( va[i], 0 ); else if( ! _is_link ) copy_info.files_size += _st.st_size; } copy_info.files_count = clipboard.count(); copy_info.current_size = 0; copy_info.current_count = 0; /* not used */ if ( copy_info.files_size == -1 ) copy_info.no_info = 1; } else copy_info.no_info = 1; } void vfu_copy_files( int a_one, int a_mode ) { ignore_copy_errors = 0; if ( files_list_count() == 0 ) { say1( "No files" ); return; } fname_t t; ASSERT( a_mode == CM_COPY || a_mode == CM_MOVE || a_mode == CM_LINK ); CopyInfo copy_info; if ( opt.copy_calc_totals == 2 ) /* PRELIMINARY copy calc totals */ __copy_calc_totals( copy_info, a_one ); VString target; if( opt.default_copy_to_cwd ) target = work_path; else target = opt.last_copy_path[ a_mode ]; VString str = CM_DESC[ a_mode ]; if ( a_one ) str = str + " '" + files_list_get(FLI)->name_ext() + "' to:"; else str += " SELECTED FILES/DIRS to:"; if ( !vfu_get_dir_name( str, target ) ) return; expand_path( target, t ); /* str_reduce_path( NULL, t ); */ str_fix_path( t ); str_trim_right( t, 1 ); if ( file_exists( t ) && !file_is_dir( t ) ) { vfu_beep(); say1( "Target is file, not directory!" ); say2( t ); vfu_menu_box( L"Error prompt", L"C Continue anyway,E Erase first, Abort (ESC)", -1 ); if ( menu_box_info.ec == L'E' ) { unlink( t ); menu_box_info.ec = L'C'; } if ( menu_box_info.ec != L'C' ) return; /* abort */ } str_fix_path( t ); target = t; strcpy( opt.last_copy_path[ a_mode ], target ); if ( opt.copy_calc_totals == 1 ) /* copy calc totals if not PRELIMINARY */ __copy_calc_totals( copy_info, a_one ); copy_info.no_free_check = !opt.copy_free_space_check; copy_info.over_mode = OM_ASK; /* 0 */ copy_info.abort = 0; VString dev_free_str; if ( !copy_info.no_free_check && !copy_info.no_info ) { fsize_t dev_free = device_avail_space( target ); dev_free_str = size_str_compact( dev_free ); dev_free_str = " | TARGET FREE: " + dev_free_str; if (copy_info.files_size > dev_free ) { vfu_beep(); say1( VString() + "Insufficient disk space! Free: " + vfu_str_comma( dev_free ) + ", Required: " + vfu_str_comma( copy_info.files_size ) ); say2( target ); vfu_menu_box( L"Error prompt", L"C Continue anyway, Abort (ESC)", -1 ); if ( menu_box_info.ec != L'C' ) return; /* abort */ } } /* free space check */ copy_info.description = "FILE "; copy_info.description += CM_DESC[ a_mode ]; copy_info.description += ": "; sprintf( t, "%.0f", copy_info.files_size ); vfu_str_comma( t ); copy_info.description += t; copy_info.description += " bytes."; // copy_info.description += dev_free_str; ASSERT( !copy_buff ); copy_buff = new char[1024*1024]; ASSERT( copy_buff ); if (copy_buff == NULL) { say1( "Copy error: cannot allocate copy buffer" ); return; } int z; for ( z = 0; z < files_list_count(); z++ ) { if ( vfu_break_op() ) break; /* cancel operation */ TF *fi = files_list_get(z); if ( a_one && z != FLI ) continue; /* if one and not current -- skip */ if ( !a_one && !fi->sel ) continue; /* if not one and not selected -- skip */ /* copy logic: src -- is full current item path dst -- is target + name-ext only! */ VString src = fi->full_name(); VString dst = target; dst += fi->name_ext(); int r = 0; if ( a_mode == CM_COPY ) r = __vfu_copy( src, dst, ©_info ); else if ( a_mode == CM_MOVE ) r = __vfu_move( src, dst, ©_info ); else if ( a_mode == CM_LINK ) r = __vfu_symlink( src, dst, ©_info ); else ASSERT(!"Bad copy mode"); if ( r == 0 ) { if ( a_mode == CM_MOVE ) files_list_del(z); } else if ( r != CR_SKIP && r != CR_ABORT ) { say1( target ); say2errno(); vfu_menu_box( L"Copy/Move error", L"C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != L'C' ) r = CR_ABORT; } if ( r == CR_ABORT ) break; /* cancel operation */ } /* for files_list[] */ if ( a_mode == CM_MOVE ) files_list_pack(); update_status(); do_draw = 2; ASSERT( copy_buff ); delete [] copy_buff; copy_buff = NULL; say1( "" ); /* show bytes copied */ if ( copy_info.current_size > 0 ) { /* i.e. only if there *are* some bytes copied :) */ str = copy_info.description; str += " DONE: " + vfu_str_comma( copy_info.current_size ) + " bytes."; str += " SKIPPED:" + vfu_str_comma( copy_info.skipped_count ); } else { str = copy_info.description; str += " DONE"; } say2( str + " TARGET AVAIL: " + size_str_compact( device_avail_space( target ) ) + " (FREE: " + size_str_compact( device_free_space( target ) ) + ")" ); ignore_copy_errors = 0; } /*---------------------------------------------------------------------------*/ void vfu_erase_files( int a_one ) { ignore_copy_errors = 0; if ( files_list_count() == 0 ) { say1( "No files" ); return; } fsize_t bytes_freed = 0; fsize_t *bytes_freed_ptr = opt.bytes_freed ? &bytes_freed : NULL; VString str; say1( "Calculating files size to be ERASED! Press ESCAPE to cancel calculation." ); fsize_t erase_size = vfu_update_sel_size( a_one ); if ( erase_size != -1 ) { str.fi( erase_size ); vfu_str_comma( str ); } else { str = "( NO INFO! )"; } vfu_beep(); str = "ERASE: " + str + " bytes in: ( ENTER to confirm, other key to cancel )"; say1( str ); if ( a_one ) say2( files_list_get(FLI)->full_name() ); else say2( "SELECTED FILES/DIRS" ); char ch = con_getch(); say1(""); say2(""); if (ch != 13) return; int z; for ( z = 0; z < files_list_count(); z++ ) { if ( vfu_break_op() ) break; /* cancel operation */ TF *fi = files_list_get(z); if ( a_one && z != FLI ) continue; if ( ! a_one && ! fi->sel ) continue; VString target = fi->full_name(); int r = __vfu_erase( target, bytes_freed_ptr ); if ( r == 0 ) { if ( fi->is_dir() ) { str_fix_path( target ); size_cache_clean( target ); } files_list_del( z ); } else if ( r != CR_ABORT && !ignore_copy_errors ) { vfu_beep(); say1( target ); say2errno(); vfu_menu_box( L"Erase error", L"C Continue operation,I Ignore further errors, Abort (ESC)" ); if ( menu_box_info.ec != L'C' && menu_box_info.ec != L'I' ) r = CR_ABORT; if ( menu_box_info.ec == L'I' ) ignore_copy_errors = 1; } if ( r == CR_ABORT ) break; /* cancel operation */ } /* for files_list[] */ files_list_pack(); update_status(); do_draw = 2; say1(""); /* show bytes freed if required */ if ( opt.bytes_freed ) { str.fi( bytes_freed ); vfu_str_comma( str ); str = "ERASE DONE: " + str + " bytes freed."; say2( str ); } else say2( "ERASE DONE" ); ignore_copy_errors = 0; } /*************************************************************************** ** ** CLIPBOARD ** ****************************************************************************/ void clipboard_add_del( int del ) { if( files_list_count() == 0 ) { say( 1, cINFO, "CLIPBOARD: no files to add, %d files already in clipboard", clipboard.count() ); return; } VString keep = "1"; int za = 0; if( sel_count == 0 ) { if( del ) { if( clipboard.exists( files_list_get(FLI)->full_name() ) ) za++; clipboard.del( files_list_get(FLI)->full_name() ); } else { clipboard[ files_list_get(FLI)->full_name() ] = keep; za++; } } else { for ( int z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( !fi->sel ) continue; if( del ) { if( clipboard.exists( fi->full_name() ) ) za++; clipboard.del( fi->full_name() ); } else { clipboard[ fi->full_name() ] = keep; za++; } } } __copy_calc_totals_clipboard( clipboard_copy_info ); clipboard_copy_info.no_free_check = !opt.copy_free_space_check; clipboard_copy_info.over_mode = OM_ASK; /* 0 */ clipboard_copy_info.abort = 0; VString fsize = clipboard_copy_info.files_size; vfu_str_comma( fsize ); say( 1, cINFO, "CLIPBOARD: %d file(s) %s. %d in the clipboard now = %s bytes.", za, del ? "removed" : "added", clipboard.count(), fsize.data() ); say2( "" ); } void clipboard_paste( int mode ) { VArray va = clipboard.keys(); ASSERT( mode == CM_COPY || mode == CM_MOVE || mode == CM_LINK ); ASSERT( !copy_buff ); copy_buff = new char[1024*1024]; ASSERT( copy_buff ); if (copy_buff == NULL) { say1( "Copy error: cannot allocate copy buffer" ); return; } va.reset(); const char* ps; while( (ps = va.next()) ) { VString dst = work_path + str_file_name_ext( ps ); int r; if ( mode == CM_LINK ) r = __vfu_symlink( ps, dst, &clipboard_copy_info ); else if ( mode == CM_MOVE ) r = __vfu_move( ps, dst, &clipboard_copy_info ); else // if ( mode == CM_COPY ) r = __vfu_copy( ps, dst, &clipboard_copy_info ); if ( r == 0 ) clipboard_copy_info.ok_count++; if ( r != 0 && r != CR_SKIP && r != CR_ABORT ) { vfu_beep(); say1( dst + r ); say2errno(); vfu_menu_box( L"Copy/Move/Symlink error", L"C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != L'C' ) r = CR_ABORT; } if ( r == CR_ABORT ) break; // cancel operation } ASSERT( copy_buff ); delete [] copy_buff; copy_buff = NULL; vfu_rescan_files( 0 ); say( 1, cINFO, "CLIPBOARD: %s: %d of %d files processed ok", CM_DESC[ mode ], clipboard_copy_info.ok_count, clipboard_copy_info.files_count ); if ( mode == CM_MOVE ) clipboard_clear(); } void clipboard_clear() { if( clipboard_copy_info.files_count ) say( 2, cINFO, "CLIPBOARD: %d files removed", clipboard_copy_info.files_count ); else say( 2, cINFO, "CLIPBOARD: empty now" ); clipboard.undef(); clipboard_copy_info.reset(); } void clipboard_view() { VArray ca = clipboard.keys(); int z; for( z = 0; z < ca.count(); z++ ) { WString ws; ws.set_failsafe( ca[z] ); mb.push( ws ); } if( mb.count() == 0 ) { say2( "CLIPBOARD: empty" ); return; } vfu_menu_box( 5, 5, L"File Clipboard Content" ); } void clipboard_menu( wchar_t act ) { if ( act == 0 ) { mb.undef(); mb.push( L"A clear & Add files to the clipboard" ); mb.push( L"+ Add files to the clipboard" ); mb.push( L"- Remove files from the clipboard" ); mb.push( L"P Copy files here" ); mb.push( L"O Move files here" ); mb.push( L"L Symlink files here" ); mb.push( L"E Clear clipboard" ); mb.push( L"V View clipboard" ); VString fcnt = clipboard_copy_info.files_count; vfu_str_comma( fcnt ); VString fsize = clipboard_copy_info.files_size; vfu_str_comma( fsize ); mb.push( L"--- " + WString( fcnt ) + L" files, " + WString( fsize ) + L" bytes" ); if ( vfu_menu_box( 5, 5, L"File Clipboard " + WString( fcnt ) + " files, " + WString( fsize ) + " bytes" ) == -1 ) return; act = menu_box_info.ec; } act = towupper( act ); switch( act ) { case L'A': clipboard_clear(); clipboard_add_del(); break; case L'+': clipboard_add_del(); break; case L'-': clipboard_add_del( 1 ); break; case L'P': clipboard_paste( CM_COPY ); break; case L'O': clipboard_paste( CM_MOVE ); break; case L'L': clipboard_paste( CM_LINK ); break; case L'E': clipboard_clear(); break; case L'V': clipboard_view(); break; } } /*---------------------------------------------------------------------------*/ vfu-5.09/vfu/vfu.cpp0000644000175000017500000024626614444676602012762 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include #include "vfu.h" #include "vfuopt.h" #include "vfufiles.h" #include "vfucopy.h" #include "vfudir.h" #include "vfuview.h" #include "vfumenu.h" #include "vfuuti.h" #include "vfusys.h" #include "vfuarc.h" #include "vfutools.h" #include "stdarg.h" #include "see.h" #include "locale.h" /*######################################################################*/ /* work context */ int work_mode; VString work_path; /* archive context */ VString archive_name; VString archive_path; VArray archive_extensions; VString external_panelizer; VArray list_panelizer; /* file list statistics */ fsize_t files_size; int sel_count; fsize_t sel_size; /* file system statistics */ fsize_t fs_free; fsize_t fs_total; fsize_t fs_block_size; /* some world wide variables */ VString startup_path; VString home_path; VString tmp_path; VString rc_path; /* files masks */ VString files_mask; VArray files_mask_array; /* misc */ int print_help_on_exit; VString last_inc_search; /*############################################ GLOBAL STRUCTS #########*/ VArray dir_tree; int dir_tree_changed; VString dir_tree_file; WArray file_find_results; // filefind results VArray path_bookmarks; /*######################################################################*/ VArray user_externals; VArray history; VArray see_filters; VArray panelizers; WArray mb; /* menu boxes */ VArray trim_tree; VArray view_profiles; VString view_profile; /*############################################ CONFIG SETTINGS #########*/ VString ext_colors[16]; VString shell_browser; VString shell_editor; VString shell_options; VString shell_diff; VString shell_prog; VString user_id_str; VString group_id_str; VString host_name_str; VString filename_opt; VString filename_conf; VString filename_history; VString filename_tree; VString filename_size_cache; VString filename_ffr; /* file find results */ /*######################################################################*/ int do_draw; int do_draw_status; /*######################################################################*/ /* messages printing */ void say( int line, int attr, const char* format, ... ) { char say_buf[4096]; ASSERT( line == 1 || line == 2 ); va_list vlist; va_start( vlist, format ); vsnprintf( say_buf, sizeof(say_buf), format, vlist ); va_end( vlist ); WString ws = say_buf; VString os = str_dot_reduce( ws, con_max_x()-1 ); vfu_con_out( 1, con_max_y() - ( (line == 1) ? 1 : 0 ), os, attr ); con_ce( attr ); } void say1(const char *a_str, int attr ) { say( 1, attr, "%s", a_str ); } void say2(const char *a_str, int attr ) { say( 2, attr, "%s", a_str ); } void say2errno() { VString str = "error: "; str += strerror(errno); say2( str ); } void saycenter( int line, int attr, const char *a_str ) { VString str = " "; int sl = str_len( a_str ); if ( sl < con_max_x() ) { sl = ( con_max_x() - sl ) / 2; str_mul( str, sl ); str = str + a_str; } say( line, attr, "%s", str.data() ); } void say1center(const char *a_str, int attr ) { saycenter( 1, attr, a_str ); } void say2center(const char *a_str, int attr ) { saycenter( 2, attr, a_str ); } void log_debug( const char* format, ... ) { char say_buf[4096]; va_list vlist; va_start( vlist, format ); vsnprintf( say_buf, sizeof( say_buf ), format, vlist ); va_end( vlist ); FILE* f = fopen( "/tmp/vfu.debug.txt", "at" ); if (!f) return; if ( fwrite( say_buf, 1, strlen( say_buf ), f ) != strlen( say_buf ) ) return; if ( fwrite( "\n", 1, 1, f ) != 1 ) return; fclose(f); } /*######################################################################*/ VString get_user_id_str( int uid, int padw = 0 ) { VString str; if( struct passwd* _pwd = getpwuid( uid ) ) str = _pwd->pw_name; else str.i( uid ); if( padw != 0 ) str_padw( str, padw ); return str; } VString get_group_id_str( int gid, int padw = 0 ) { VString str; if( struct group* _grp = getgrgid( gid ) ) str = _grp->gr_name; else str.i( gid ); if( padw != 0 ) str_padw( str, padw ); return str; } /*######################################################################*/ void TF::reset() /* reset -- NULL all fields */ { _name.undef(); _name_ext.undef(); _ext.undef();; memset( &_st, 0, sizeof(_st) ); _type_str[0] = 0; _is_link = 0; _is_dir = 0; strcpy( _mode_str, MODE_OFF ); _size = -1; /* unknown -- get from stat? */ _view.undef(); _color = cPLAIN; sel = 0; } /*-----------------------------------------------------------------------*/ TF::TF() { reset(); } /*-----------------------------------------------------------------------*/ TF::TF( const char* a_name, const struct stat* a_stat, int a_is_link ) { reset(); set_name( a_name ); update_stat( a_stat, a_is_link ); } /*-----------------------------------------------------------------------*/ TF::~TF() { reset(); } /*-----------------------------------------------------------------------*/ const char* TF::full_name( int fix ) { ASSERT( _name ); if ( _name[0] == '/' ) _full_name = _name; else { if ( work_mode == WM_ARCHIVE ) _full_name = archive_path; else _full_name = work_path; _full_name += _name; } if ( fix && _is_dir ) _full_name += "/"; /* i.e. str_fix_path() */ return _full_name.data(); } /*-----------------------------------------------------------------------*/ void TF::set_name( const char* a_new_name ) { _name = a_new_name; int last_slash = str_rfind( _name, '/' ); if ( last_slash == -1 ) _name_ext = _name; else str_copy( _name_ext, _name, last_slash + 1 ); int last_dot = str_rfind( _name, '.' ); if ( last_dot == -1 || last_dot == 0 ) _ext = ""; /* no dot or dot-file (hidden) */ else str_copy( _ext, _name, last_dot ); /* including leading dot */ drop_view(); } /*-----------------------------------------------------------------------*/ void TF::set_size( fsize_t a_new_size ) { _size = a_new_size; drop_view(); } /*-----------------------------------------------------------------------*/ int TF::color() { if ( _color < 0 ) _color = get_item_color( this ); return _color; } /*-----------------------------------------------------------------------*/ void TF::drop_view() { _view.undef(); } /*-----------------------------------------------------------------------*/ const char* TF::view() { if ( str_len( _view ) == 0 ) refresh_view(); return _view.data(); } /*-----------------------------------------------------------------------*/ void TF::refresh_view() { ASSERT( _name ); ASSERT( _name_ext ); ASSERT( _ext ); char stmode[16] = ""; // 10 + 1sep VString stowner; VString stgroup; char sttime[32] = ""; char stsize[16] = ""; char sttype[4] = ""; if ( !opt.long_name_view ) { if (opt.f_mode) { strcpy( stmode, _mode_str ); strcat( stmode, " " ); /* field separator */ } if (opt.f_owner) stowner = get_user_id_str( _st.st_uid, 8 ) + " "; if (opt.f_group) stgroup = get_group_id_str( _st.st_gid, 8 ) + " "; if (opt.f_time ) { time_t ftm = vfu_opt_time( _st ); time_str_compact( ftm, sttime ); long int tdiff = time( NULL ) - ftm; strcat( sttime, tdiff > 0 ? tdiff > 23*60*60 ? " " : "*" : "!" ); // TODO: use for something useful strcat( sttime, " " ); /* field separator */ } if (opt.f_size) { VString str; if ( _is_dir && _size == -1 ) str = "[DIR]"; else str = fsize_fmt( _size ); snprintf( stsize, sizeof(stsize), "%14s", (const char*)(str) ); strcat( stsize, " " ); /* field separator */ } } /* if ( !opt.LongNameView ) */ if (opt.f_type || opt.long_name_view ) { strcpy( sttype, _type_str ); /* there is no field separator here! */ } VString name_view = _name; #ifdef _TARGET_UNIX_ /* links are supported only under UNIX */ if ( _is_link ) { name_view += " -> "; name_view += vfu_readlink( _name ); } #endif /* the three space separator below is for the tag and selection marks `>>#' */ VString view; view = view + stmode + stowner + stgroup + sttime + stsize + sttype + " " + name_view; WString wview; wview = view; int x = con_max_x() - 1; // 1 char for scroller, FIXME: TODO: should be optional if ( str_len( wview ) > x ) { str_sleft( wview, x -1 ); wview += L">"; } else str_pad( wview, - x ); // +1 for the scroller _view = wview; } /*-----------------------------------------------------------------------*/ void TF::update_stat( const struct stat* a_new_stat, int a_is_link ) { if ( a_new_stat ) memcpy( &_st, a_new_stat, sizeof(_st) ); else stat( _name, &_st ); _is_link = (a_is_link == -1) ? file_is_link( _name ) : a_is_link; _is_dir = S_ISDIR(_st.st_mode ); strcpy( _type_str, file_type_str( _st.st_mode, _is_link ) ); file_get_mode_str( _st.st_mode, _mode_str ); if ( _is_dir ) _size = -1; /* FIXME: some auto thing here? */ else _size = _st.st_size; _color = -1; drop_view(); } /*######################################################################*/ void vfu_help() { say1center( HEADER ); say2center( CONTACT ); mb.undef(); mb.push( L"*keypad -- navigation keys" ); mb.push( L"ENTER -- enter into directory/View file ( `+' and `=' too )"); mb.push( L"BACKSPC -- (BS) chdir to parent directory ( `-' and ^H too )" ); mb.push( L"Alt + - -- exit current archive ( Alt + BACKSPACE too )" ); mb.push( L"TAB -- edit entry: filename, atrrib's/mode, owner, group"); mb.push( L"R.Arrow -- rename current file " ); mb.push( L"SPACE -- select/deselect current list item" ); mb.push( L"ESC -- exit menu"); mb.push( L"ESC+ESC -- exit menu"); mb.push( L"1 -- toggle `mode' field on/off " ); mb.push( L"2 -- toggle `owner' field on/off " ); mb.push( L"3 -- toggle `group' field on/off " ); mb.push( L"4 -- toggle `time' field on/off " ); mb.push( L"5 -- toggle `size' field on/off " ); mb.push( L"6 -- toggle `type' field on/off " ); mb.push( L"7 -- toggle `time type' field change/modify/access time " ); mb.push( L"8 -- turn on all fields" ); mb.push( L"0 -- toggle long name view ( show only type and file name )" ); mb.push( L"~ -- change current dir to HOME directory" ); mb.push( L"A -- arrange/Sort file list" ); mb.push( L"B -- browse(view) selected (if selection) or current file" ); mb.push( L"Alt+B -- browse(view) ONLY current file (regardless selection)" ); mb.push( L"C -- copy selected (if selection) or current file(s)" ); mb.push( L"Alt+C -- copy ONLY current file (regardless selection)" ); mb.push( L"D -- change directory" ); mb.push( L"Ctrl+D -- directory tree " ); mb.push( L"Alt+D -- chdir history " ); mb.push( L"E -- erase(remove) selected (if selection) or current file(s)!" ); mb.push( L"Alt+E -- erase(remove) ONLY current file (regardless selection)!" ); mb.push( L"F -- change file masks (space-delimited) "); mb.push( L"Ctrl+F -- reset file mask to `*'" ); mb.push( L"G -- global select/deselect" ); mb.push( L"H -- this help text" ); mb.push( L"I -- edit file" ); mb.push( L"Q -- exit here (to the current directory)"); mb.push( L"R -- reload directory/refresh file list" ); mb.push( L"Ctrl+R -- recursive reload... " ); mb.push( L"Alt+R -- reload/tree menu" ); mb.push( L"J -- jump to mountpoint" ); mb.push( L"L -- symlink selected/current file(s) into new directory" ); mb.push( L"Ctrl+L -- refresh/redraw entire screen" ); mb.push( L"M -- move selected (if selection) or current file(s)" ); mb.push( L"Alt+M -- move ONLY current file (regardless selection)" ); mb.push( L"N -- file find" ); mb.push( L"Alt+N -- file find menu" ); mb.push( L"O -- options(toggles) menu" ); mb.push( L"P -- file clipboard menu" ); mb.push( L"S -- incremental filename search" ); mb.push( L"Alt+S -- repeat last used incremental search entry (find next)" ); /* mb.push( L"Ctrl+C -- copy files to clipboard" ); mb.push( L"Ctrl+X -- cut files to clipboard" ); mb.push( L"Ctrl+V -- paste (copy) files from clipboard to current directory" ); */ mb.push( L"T -- tools menu (including rename and classify tools)" ); mb.push( L"U -- user menu (user external commands bound to menu) " ); mb.push( L"X -- exit to the old(startup) directory "); mb.push( L"Alt+X -- exit to the old(startup) directory "); mb.push( L"Z -- calculate directories sizes menu" ); mb.push( L"Ctrl+Z -- show size of the current (under the cursor >>) directory"); mb.push( L"Alt+Z -- show all directories sizes ( or Alt+Z )" ); mb.push( L"V -- edit vfu.conf file"); mb.push( L"! -- shell (also available with '?')" ); mb.push( L"/ -- command line" ); mb.push( L"` -- bookmarks menu" ); mb.push( L"Alt+# -- where # is 1..9, go to #'th bookmark" ); mb.push( L"vfu uses these (one of) these config files:"); mb.push( L" 1. $HOME/$RC_PREFIX/vfu/vfu.conf"); mb.push( L" 2. $HOME/.vfu/vfu.conf"); mb.push( L" 3. " FILENAME_CONF_GLOBAL0 ); mb.push( L" 4. " FILENAME_CONF_GLOBAL1 ); mb.push( L" 5. " FILENAME_CONF_GLOBAL2 ); mb.push( L"" ); vfu_menu_box( 1, 4, L"VFU Help ( PageUp/PageDown to scroll )" ); mb.undef(); do_draw = 1; } /*--------------------------------------------------------------------------*/ void vfu_init() { char t[MAX_PATH]; if( expand_path( "." ) == "" ) if( chdir( "/" ) ) { say1( "Cannot chdir to: /" ); say2errno(); } work_mode = WM_NORMAL; if( ! getcwd( t, sizeof(t) ) ) t[0] = 0; str_fix_path( t ); work_path = t; archive_name = ""; archive_path = ""; /* NOTE: archives' root directory is `' but not `/'! */ external_panelizer = ""; files_list_clear(); files_size = 0; sel_count = 0; sel_size = 0; fs_free = 0; fs_total = 0; fs_block_size = 0; file_list_index.wrap = 0; /* file_list_index.* are setup by vfu_read_files() */ user_id_str = get_user_id_str( getuid() ); group_id_str = get_group_id_str( getgid() ); gethostname( t, MAX_PATH-1 ); host_name_str = t; startup_path = work_path; tmp_path = ""; if ( tmp_path == "" ) tmp_path = getenv( "TEMP" ); if ( tmp_path == "" ) tmp_path = getenv( "TMP" ); if ( tmp_path == "" ) tmp_path = "/tmp/"; str_fix_path( tmp_path ); if ( getenv( "HOME" ) ) home_path = getenv( "HOME" ); else { home_path = tmp_path + user_id_str + "/"; make_path( home_path ); } shell_diff = "/usr/bin/diff"; /* FIXME: this should something relevant to the home_path from above if $HOME does not exist(?) well still can accept /tmp/ as it is default now */ rc_path = get_rc_directory( "vfu" ); /* setup config files locations */ filename_opt = rc_path + FILENAME_OPT; filename_conf = rc_path + FILENAME_CONF; filename_tree = rc_path + FILENAME_TREE; filename_size_cache = rc_path + FILENAME_SIZE_CACHE; filename_history = rc_path + FILENAME_HISTORY; filename_ffr = rc_path + FILENAME_FFR; VArray conf_data; if ( access( filename_conf, R_OK ) != 0 ) { /* cannot find local/user conf file, try copy one */ if ( access( FILENAME_CONF_GLOBAL0, R_OK ) == 0 ) conf_data.fload( FILENAME_CONF_GLOBAL0 ); else if ( access( FILENAME_CONF_GLOBAL1, R_OK ) == 0 ) conf_data.fload( FILENAME_CONF_GLOBAL1 ); else if ( access( FILENAME_CONF_GLOBAL2, R_OK ) == 0 ) conf_data.fload( FILENAME_CONF_GLOBAL2 ); conf_data.fsave( filename_conf ); } /* shell setup */ shell_prog = ""; if ( shell_prog == "" ) shell_prog = getenv( "VFU_SHELL" ); if ( shell_prog == "" ) shell_prog = getenv( "SHELL" ); /* this will load defaults first then load vfu.opt and at the end will load vfu.conf which will overwrite all if need to */ vfu_settings_load( conf_data.count() > 0 ? &conf_data : NULL ); size_cache_load(); file_list_index.wrap = 0; /* just to be safe :) */ files_mask = "*"; files_mask_array = str_split( " ", files_mask ); view_profiles.push( "123456" ); view_profile = "123456"; /* setup menu colors */ menu_box_info.ti = 95; /* title */ menu_box_info.cn = 23; /* normal */ menu_box_info.ch = 47; /* selected */ signal( SIGINT , vfu_signal ); signal( SIGHUP , vfu_signal ); signal( SIGTERM , vfu_signal ); signal( SIGQUIT , vfu_signal ); // signal( SIGWINCH, vfu_signal ); // already set in unicon/vslib srand( time( NULL ) ); do_draw = 1; vfu_read_files(); } /*--------------------------------------------------------------------------*/ void vfu_exit_path( const char *a_path ) { if( chdir( a_path ) ) { say1( VString( "Cannot chdir to: " ) + a_path ); say2errno(); } VString str; if ( getenv( "VFU_EXIT" ) ) str = getenv( "VFU_EXIT" ); else str = tmp_path + "vfu.exit." + user_id_str; unlink( str ); int fdx = open( str, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR ); if( fdx == -1 ) return; if( write( fdx, a_path, str_len( a_path ) ) ) // stupid, stupid -Wunused-result { } close( fdx ); } /*--------------------------------------------------------------------------*/ /* return 0 for exit-confirmed! */ int vfu_exit( const char* a_path ) { mb.undef(); mb.push( L"X Exit (to startup path)" ); mb.push( L"Q Quit (to work path) " ); if ( a_path == NULL ) { vfu_beep(); int z = vfu_menu_box( 50, 5, L" Exit VFU?" ); if ( z == -1 ) return 1; z ? vfu_exit_path( work_path ) : vfu_exit_path( startup_path ); return 0; } else { vfu_exit_path( a_path ); return 0; } } /*--------------------------------------------------------------------------*/ void vfu_run() { say1center( HEADER ); say2center( CONTACT ); /* int oldFLI = -1; // quick view */ while (4) { if (do_draw) { if ( do_draw > 2 ) vfu_reset_screen(); if ( do_draw > 1 ) do_draw_status = 1; vfu_redraw(); do_draw = 0; } if (do_draw_status) { vfu_redraw_status(); do_draw_status = 0; } /* TODO: quick view?... if ( work_mode == WM_NORMAL && files_list_count() > 0 && oldFLI != FLI ) { oldFLI = FLI; const char* fn = files_list_get(FLI)->full_name(); file_save( "/tmp/vfu-quick-view", (void*)fn, strlen( fn ) ); } */ show_pos( FLI+1, files_list_count() ); /* FIXME: should this be in vfu_redraw()? */ wchar_t wch = con_getwch(); if( wch == 0 || wch == UKEY_RESIZE ) wch = UKEY_CTRL_L; if ( wch >= 'A' && wch <= 'Z' ) wch = towlower( wch ); say1( "" ); if ( user_id_str == "root" ) say2center( "*** WARNING: YOU HAVE GOT ROOT PRIVILEGES! ***" ); else say2( "" ); // say( 1, cMESSAGE, "key/wch: %lX", wch ); if ( work_mode == WM_NORMAL || work_mode == WM_ARCHIVE ) switch (wch) { /* actually this is ANY work_mode (since there are two modes only) */ case L'1' : case L'2' : case L'3' : case L'4' : case L'5' : case L'6' : case L'7' : case L'8' : case L'0' : vfu_toggle_view_fields( wch ); break; case L'.' : vfu_toggle_view_fields( wch ); vfu_rescan_files( 0 ); break; case L's' : vfu_inc_search( 0 ); break; case UKEY_ALT_S : vfu_inc_search( 1 ); break; case UKEY_CTRL_L: do_draw = 3; break; case L'q' : if( vfu_exit( work_path ) == 0 ) return; break; case UKEY_ALT_X : case L'x' : if( vfu_exit( startup_path ) == 0 ) return; break; case 27 : if( vfu_exit( NULL ) == 0 ) return; break; case UKEY_UP : vfu_nav_up(); break; case UKEY_DOWN : vfu_nav_down(); break; case UKEY_PGUP : vfu_nav_ppage(); break; case UKEY_PGDN : vfu_nav_npage(); break; case UKEY_CTRL_A: case UKEY_HOME : vfu_nav_home(); break; case UKEY_CTRL_E: case UKEY_END : vfu_nav_end(); break; case L'h' : vfu_help(); break; case L'f' : vfu_change_file_mask( NULL ); break; case UKEY_CTRL_F : vfu_change_file_mask( "*" ); break; case UKEY_CTRL_D : tree_view(); break; case UKEY_ALT_R : vfu_read_files_menu(); break; /* this will be in alt+r menu case 'R' : con_cs(); vfu_refresh_all_views(); do_draw = 1; break; */ case UKEY_CTRL_R : vfu_rescan_files( 1 ); break; case L'r' : vfu_rescan_files( 0 ); break; case L' ' : vfu_nav_select(); break; case UKEY_BACKSPACE : case 8 : case L'-' : vfu_action_minus(); break; case UKEY_ALT_BACKSPACE : case UKEY_ALT_MINUS : vfu_action_minus( 2 ); break; case 13 : case L'+' : case L'=' : vfu_action_plus( wch ); break; case L'[' : vfu_action_minus(); vfu_nav_up(); vfu_action_plus( 13 ); break; case L']' : vfu_action_minus(); vfu_nav_down(); vfu_action_plus( 13 ); break; case UKEY_LEFT : if (opt.lynx_navigation) vfu_action_minus(); break; case UKEY_RIGHT : if (opt.lynx_navigation) vfu_action_plus( L'+' ); else if ( work_mode == WM_NORMAL ) vfu_rename_file_in_place(); break; case L'd' : vfu_chdir( NULL ); break; case UKEY_ALT_D : vfu_chdir_history(); break; case UKEY_ALT_EQ : case L'>' : opt.long_name_view = !opt.long_name_view; vfu_drop_all_views(); do_draw = 1; break; case L'a' : vfu_arrange_files(); break; case L'g' : vfu_global_select(); break; case L'o' : vfu_options(); break; case L'v' : vfu_edit_conf_file(); break; case L'!' : case L'?' : con_cs(); vfu_shell( shell_prog, 0 ); do_draw = 3; break; case L'u' : vfu_user_menu(); break; /* not documented unless here :) */ case UKEY_CTRL_T : { char s[128]; say1( "Timing screen draws (x1000)..." ); clock_t t = clock(); for(int z = 0; z < 1000; z++) vfu_redraw(); t = clock() - t; snprintf(s, sizeof(s), "Draw speed: %f dps.",(1000.0/((double)t/CLOCKS_PER_SEC))); say1(s); break; } case L'*' : FLGO( rand() % files_list_count() ); do_draw = 1; break; case L'z' : vfu_directory_sizes( 0 ); break; case UKEY_ALT_Z : vfu_directory_sizes( L'A' ); break; case UKEY_CTRL_Z : vfu_directory_sizes( L'Z' ); break; } if ( work_mode == WM_ARCHIVE ) switch (wch) { case L'c' : vfu_extract_files( 0 ); break; case UKEY_ALT_C : vfu_extract_files( 1 ); break; } if ( work_mode == WM_NORMAL ) switch (wch) { case L'b' : case UKEY_ALT_B : if ( wch == L'b' && sel_count > 0 ) vfu_browse_selected_files(); else { if ( files_list_count() > 0 ) vfu_browse( FLCUR->name(), wch == UKEY_ALT_B ); else say1( "No files" ); } break; case L'n' : vfu_file_find( 0 ); break; case UKEY_ALT_N : vfu_file_find( 1 ); break; case L'~' : vfu_chdir( home_path ); break; case L'/' : vfu_command(); break; case 'i' : if ( files_list_count() > 0 ) vfu_edit( files_list_get(FLI)->name() ); else say1( "No files"); break; case 'm' : vfu_copy_files(sel_count == 0, CM_MOVE); break; case UKEY_ALT_M : vfu_copy_files(1, CM_MOVE); break; case 'c' : vfu_copy_files(sel_count == 0, CM_COPY); break; case UKEY_ALT_C : vfu_copy_files(1, CM_COPY); break; case 'l' : vfu_copy_files(sel_count == 0, CM_LINK); break; case UKEY_ALT_L : vfu_copy_files(1, CM_LINK); break; case 'e' : vfu_erase_files(sel_count == 0); break; case UKEY_ALT_E : vfu_erase_files(1); break; case 'j' : vfu_jump_to_mountpoint( 0 ); break; case UKEY_ALT_J : vfu_jump_to_mountpoint( 1 ); break; case UKEY_ALT_1 : bookmark_goto( L'1' ); break; case UKEY_ALT_2 : bookmark_goto( L'2' ); break; case UKEY_ALT_3 : bookmark_goto( L'3' ); break; case UKEY_ALT_4 : bookmark_goto( L'4' ); break; case UKEY_ALT_5 : bookmark_goto( L'5' ); break; case UKEY_ALT_6 : bookmark_goto( L'6' ); break; case UKEY_ALT_7 : bookmark_goto( L'7' ); break; case UKEY_ALT_8 : bookmark_goto( L'8' ); break; case UKEY_ALT_9 : bookmark_goto( L'9' ); break; case '`' : bookmark_goto( 0 ); break; case 9 : vfu_edit_entry(); break; case 't' : vfu_tools(); break; case 'p' : clipboard_menu( 0 ); break; /* case KEY_CTRL_C : vfu_clipboard( 'C' ); break; // copy case KEY_CTRL_X : vfu_clipboard( 'X' ); break; // cut case KEY_CTRL_V : vfu_clipboard( 'V' ); break; // paste */ } if ( ( UKEY_F1 <= wch && wch <= UKEY_F10 ) || ( UKEY_SH_F1 <= wch && wch <= UKEY_SH_F10 ) || ( UKEY_ALT_F1 <= wch && wch <= UKEY_ALT_F10 ) || ( UKEY_CTRL_F1 <= wch && wch <= UKEY_CTRL_F10 ) || ( wch == UKEY_INS) ) vfu_user_external_exec( wch ); } } /*--------------------------------------------------------------------------*/ void vfu_help_cli() { printf( "%s", HEADER "\n" "Command line switches:\n" " none -- run in interactive mode (DEFAULT)\n" " -h -- this help screen\n" " -i -- go temporarily into interactive mode\n" " -d path -- change current path to `path'\n" " -r -- rebuild DirTree (under DOS -- tip: see -d)\n" " -t -- view DirTree\n" " -v -- version information\n" "tips:\n" " 1. command line switches are executed in order!\n" " 2. example: vfu -d c:/dos/ -r -i\n" " 3. example: vfu -d c:/ -r -d d:/ -r -d e:/ -r\n" "compile information:\n" " target description: " _TARGET_DESCRIPTION_ "\n" ); } /*--------------------------------------------------------------------------*/ void vfu_cli( int argc, char* argv[] ) { VString temp; GETOPT((char*)"hrd:ti") { switch(optc) { case 'h' : print_help_on_exit = 1; break; case 'i' : vfu_run(); break; case 'd' : temp = optarg; vfu_chdir( temp ); break; case 'r' : con_out(1,1,HEADER,cINFO); temp = "Rebuilding directory tree ( work_path is"; temp += work_path; temp += " )"; say2( temp ); tree_rebuild(); break; case 't' : vfu_con_out(1,1,HEADER,cINFO); tree_view(); vfu_exit( work_path ); break; default: vfu_help_cli(); break; } } } /*--------------------------------------------------------------------------*/ void vfu_done() { /* if dir_tree.count is 0 don't save -- there's nothing to be saved */ if ( dir_tree.count() && dir_tree_changed ) tree_save(); vfu_settings_save(); } /*--------------------------------------------------------------------------*/ void vfu_soft_reset_screen() { /* update scroll parameters */ file_list_index.set_min_max( 0, files_list_count() - 1 ); file_list_index.set_pagesize( con_max_y() - 7 ); FLGO( file_list_index.pos() ); vfu_drop_all_views(); vfu_redraw(); vfu_redraw_status(); say1( "" ); say2( "" ); } void vfu_reset_screen() { con_done(); con_init(); con_chide(); vfu_soft_reset_screen(); } void vfu_signal( int sig ) { vfu_done(); con_beep(); con_cs(); con_cshow(); con_done(); printf( "vfu: signal received: %d -- terminated\n", sig ); exit(200); } /*--------------------------------------------------------------------------*/ void vfu_toggle_view_fields( wchar_t wch ) { switch( wch ) { case L'1' : opt.f_mode = !opt.f_mode; break; case L'2' : opt.f_owner = !opt.f_owner; break; case L'3' : opt.f_group = !opt.f_group; break; case L'4' : opt.f_time = !opt.f_time; break; case L'5' : opt.f_size = !opt.f_size; break; case L'6' : opt.f_type = !opt.f_type; break; case L'7' : opt.f_time_type++; if (opt.f_time_type > 2) opt.f_time_type = 0; break; case L'8' : opt.f_mode = opt.f_owner = opt.f_group = opt.f_time = opt.f_size = opt.f_type = 1; break; case L'0' : opt.long_name_view = !opt.long_name_view; break; case L'.' : opt.show_hidden_files = !opt.show_hidden_files; break; default : return; /* cannot be reached really */ } vfu_drop_all_views(); } /*--------------------------------------------------------------------------*/ void vfu_shell( const char* a_command, const char* a_options ) { VString shell_line = a_command; VString o = a_options; VString status = ""; int res = vfu_update_shell_line( shell_line, o ); if (res) return; if ( str_find( o, '!' ) > -1 ) { // review shell_line say1( "Review shell line to be executed:" ); VString sl = shell_line; sl = str_dot_reduce( sl, con_max_x() - 1 ); say2( sl ); con_getwch(); } if ( str_find( o, 'n' ) == -1 ) /* [n]o console suspend */ { con_cs(); con_xy( 1, 1 ); con_cshow(); con_suspend(); } res = system( shell_line ); if ( res ) { sprintf( status, "*** execution failed, system() == %d ***", res ); } if ( str_find( o, 'w' ) != -1 ) /* [w]ait after shell */ { printf( "*** press enter ***" ); fflush( stdout ); fgetc( stdin ); } if ( str_find( o, 'n' ) == -1 ) /* [n]o console suspend */ { con_restore(); con_chide(); con_cs(); } if( chdir( work_path ) ) /* in case SHELL changed directory... (DOS only :)) */ { say1( VString( "Cannot chdir to: " ) + work_path ); say2errno(); } if ( str_find( o, 'r' ) != -1 ) vfu_rescan_files(); do_draw = 2; if ( str_find( o, 'n' ) != -1 ) do_draw = 0; if ( str_find( o, 'i' ) != -1 ) vfu_nav_down(); say1(""); say2( status ); } /*--------------------------------------------------------------------------*/ void update_status() { int z; fsize_t s; sel_count = 0; sel_size = 0; files_size = 0; /* files list statistics */ for( z = 0; z < files_list_count(); z++ ) { s = files_list_get(z)->size(); if ( files_list_get(z)->sel ) { sel_size += s; sel_count++; } files_size += s; } /* current fs statistics */ struct statfs stafs; statfs( ".", &stafs ); fs_free = (fsize_t)(stafs.f_bsize) * (opt.show_user_free?stafs.f_bavail:stafs.f_bfree); fs_total = (fsize_t)(stafs.f_bsize) * stafs.f_blocks; fs_block_size = (fsize_t)(stafs.f_bsize); do_draw_status = 1; } /*--------------------------------------------------------------------------*/ void vfu_edit( const char *fname ) { if ( files_list_count() == 0 ) { say1( "No files"); return; } if ( FLCUR->is_dir() ) { say1( "Cannot edit directory"); return; } con_cs(); if ( opt.internal_editor ) { opt.seo.cs = cINFO; SeeEditor editor( &opt.seo ); if( editor.open( fname ) == 0 ) { int r = 1; while ( r ) { editor.run(); r = editor.request_quit(); } } else say1( "Error loading file..." ); editor.close(); } else { VString str = shell_editor; if ( fname ) { str_replace( str, "%f", shell_escape( fname ) ); str_replace( str, "%F", shell_escape( fname ) ); } vfu_shell( str, "" ); } do_draw = 2; say1(""); say2(""); } /*--------------------------------------------------------------------------*/ void vfu_browse_selected_files() { /* FIXME: multiple files browsing should be back ASAP #define VFU_BROWSE_MAX_FILES 10 int i; // index int ic; // count for ( z = 0; z < files_list_count(); z++ ) if ( files_list_get(z)->sel ) if ( !files_list_get(z)->is_dir() ) SeeAddFile( files_list_get(z)->full_name() ); //------ int z; for ( z = 0; z < files_list_count(); z++ ) if ( files_list_get(z)->sel ) if ( !files_list_get(z)->is_dir() ) SeeAddFile( files_list_get(z)->full_name() ); SeeSLColor = cINFO; See(); do_draw = 2; say1(""); say2(""); */ } void vfu_browse( const char *fname, int no_filters ) { VString new_name = fname; VString tmp_name; char full_fname[MAX_PATH]; expand_path( fname, full_fname ); if ( ! no_filters && see_filters.count() > 0 ) { int z; for ( z = 0; z < see_filters.count(); z++ ) { VArray split; split = str_split( ",", see_filters[z] ); VString mask = split[0]; VString str = split[1]; if ( FNMATCH( mask, str_file_name_ext( fname ) ) ) continue; /* found */ tmp_name = vfu_temp(); str_replace( str, "%f", shell_escape( fname ) ); str_replace( str, "%F", shell_escape( full_fname ) ); str += " > " + tmp_name; vfu_shell( str, "" ); chmod( tmp_name, S_IRUSR | S_IWUSR ); break; } } if ( tmp_name != "" ) new_name = tmp_name; if ( opt.internal_browser ) { opt.svo.cs = cINFO; SeeViewer viewer( &opt.svo ); if( viewer.open( new_name ) == 0 ) viewer.run(); else say1( "Error loading file..." ); viewer.close(); } else { VString str = shell_browser; if ( fname ) { str_replace( str, "%f", shell_escape( fname ) ); str_replace( str, "%F", shell_escape( full_fname ) ); } vfu_shell( str, "" ); } do_draw = 2; say1(""); say2(""); if ( tmp_name != "" ) unlink( tmp_name ); } /*--------------------------------------------------------------------------*/ void vfu_action_plus( wchar_t wch ) { if ( files_list_count() == 0 ) return; TF *fi = FLCUR; if ( work_mode == WM_NORMAL ) { if ( fi->is_dir() ) { /* directory */ vfu_chdir( fi->name() ); } else { /* file */ int z; for ( z = 0; z < archive_extensions.count(); z++ ) if ( FNMATCH_OC( archive_extensions[z], fi->name_ext(), opt.lower_case_ext_config ) == 0 ) { FNMATCH_OC( archive_extensions[z], fi->name_ext(), opt.lower_case_ext_config ); z = -1; /* FIXME: this shouldn't be -1 for TRUE really :) */ break; } if ( z == -1 ) { /* yep this is archive */ work_mode = WM_ARCHIVE; archive_name = fi->name(); archive_path = ""; /* NOTE: archives' root dir is `' but not `/'! */ vfu_read_files(); say1( "ARCHIVE mode activated ( some keys/commands are disabled! )" ); } else if ( wch == UKEY_ENTER && vfu_user_external_find( UKEY_ENTER, fi->ext(), fi->type_str(), NULL ) != -1 ) vfu_user_external_exec( UKEY_ENTER ); else vfu_browse( fi->name() ); } } else { /* work_mode == WM_ARCHIVE */ if ( fi->is_dir() ) { /* directory */ VString p = fi->name(); str_fix_path( p ); archive_path += p; vfu_read_files(); } else if ( wch == UKEY_ENTER && vfu_user_external_find( UKEY_ENTER, fi->ext(), fi->type_str(), NULL ) != -1 ) vfu_user_external_exec( UKEY_ENTER ); else { /* file */ vfu_browse_archive_file(); } } } /*--------------------------------------------------------------------------*/ void vfu_action_minus( int mode ) { VString o = work_path; /* save old path i.e. current */ if ( work_mode == WM_NORMAL ) { if ( work_path[0] == '/' && work_path[1] == 0 ) return; vfu_chdir( ".." ); } else if ( work_mode == WM_ARCHIVE ) { if( mode == 2 ) archive_path = ""; if ( str_len( archive_path ) > 0 ) { str_cut_right( archive_path, "/" ); int z = str_rfind( archive_path, "/" ); o = archive_path; o += "/"; if ( z != -1 ) str_sleft(archive_path,z+1); else archive_path = ""; vfu_read_files(); } else { o += archive_name; o += "/"; /* FIXME: is this required ? */ work_mode = WM_NORMAL; archive_name = ""; archive_path = ""; /* NOTE: this shouldn't be `/'! */ vfu_chdir( "." ); } } int z = 0; for ( z = 0; z < files_list_count(); z++ ) { VString n; if ( work_mode == WM_ARCHIVE ) n = archive_path; else n = work_path; n += files_list_get(z)->name(); n += "/"; if ( pathcmp( o, n ) == 0 ) { FLGO(z); break; } } do_draw = 1; } /*--------------------------------------------------------------------------*/ // global select / different // returns 0 for ok int vfu_cmp_files_crc32( const char* src, const char* dst, const char* name ) { fname_t fn1; fname_t fn2; struct stat stat_src; struct stat stat_dst; strcpy( fn1, src ); strcat( fn1, name ); strcpy( fn2, dst ); strcat( fn2, name ); if (access( fn1, F_OK )) return 1; if (access( fn2, F_OK )) return 2; if (stat( fn1, &stat_src )) return 3; if (stat( fn2, &stat_dst )) return 4; if (S_ISDIR( stat_src.st_mode )) return 5; if (S_ISDIR( stat_dst.st_mode )) return 6; if ( stat_src.st_size != stat_dst.st_size ) return 7; if ( file_crc32( fn1 ) != file_crc32( fn2 ) ) return 8; return 0; } #define GSAME_NAME 1 #define GSAME_EXT 2 #define GSAME_SIZE 3 #define GSAME_DATETIME 4 #define GSAME_DATE 5 #define GSAME_TIME 6 #define GSAME_TIME1 7 #define GSAME_OWNER 8 #define GSAME_GROUP 9 #define GSAME_MODE 10 #define GSAME_TYPE 11 #define TIMECMP_DT 0 // compare date and time #define TIMECMP_D 1 // compare only date #define TIMECMP_T 2 // compare only time #define TIMECMP_T1 3 // compare only time (to 1 minute round) // return 0=don't match and 1=match int vfu_time_cmp( time_t t1, time_t t2, int type = TIMECMP_DT ) { char tmp1[32]; char tmp2[32]; strcpy( tmp1, ctime(&t1) ); strcpy( tmp2, ctime(&t2) ); if ( type == TIMECMP_T ) { strcpy( tmp1, tmp1+11 ); tmp1[8] = 0; strcpy( tmp2, tmp2+11 ); tmp2[8] = 0; } else if ( type == TIMECMP_T1 ) { strcpy( tmp1, tmp1+11 ); tmp1[5] = 0; strcpy( tmp2, tmp2+11 ); tmp2[5] = 0; } else if ( type == TIMECMP_D ) { strcpy( tmp1+10, tmp1+19 ); strcpy( tmp2+10, tmp2+19 ); } return (strcmp( tmp1, tmp2 ) == 0); } void vfu_global_select_same( int same_mode ) { VString same_str; long same_int = 0; fsize_t same_fsize = 0; TF* fi = FLCUR; switch( same_mode ) { case GSAME_NAME : same_str = fi->name(); break; case GSAME_EXT : same_str = fi->ext(); break; case GSAME_SIZE : same_fsize = fi->size(); break; case GSAME_DATETIME : case GSAME_DATE : case GSAME_TIME : case GSAME_TIME1 : same_int = vfu_opt_time( fi->st() ); break; case GSAME_OWNER : same_int = fi->st()->st_uid; break; case GSAME_GROUP : same_int = fi->st()->st_gid; break; case GSAME_MODE : same_int = fi->st()->st_mode; break; case GSAME_TYPE : same_str = fi->type_str(); break; default : return; } int z = 0; for (z = 0; z < files_list_count(); z++) { fi = files_list_get(z); int sel = 0; switch( same_mode ) { case GSAME_NAME : sel = (pathcmp(same_str, fi->name()) == 0); break; case GSAME_EXT : sel = (pathcmp(same_str, fi->ext()) == 0); break; case GSAME_SIZE : sel = (same_fsize == fi->size()); if ( fi->is_dir() ) sel = 0; break; case GSAME_DATETIME : sel = vfu_time_cmp(same_int, vfu_opt_time( fi->st())); break; case GSAME_DATE : sel = vfu_time_cmp(same_int, vfu_opt_time( fi->st() ), TIMECMP_D ); break; case GSAME_TIME : sel = vfu_time_cmp(same_int, vfu_opt_time( fi->st() ), TIMECMP_T ); break; case GSAME_TIME1 : sel = vfu_time_cmp(same_int, vfu_opt_time( fi->st() ), TIMECMP_T1 ); break; case GSAME_OWNER : sel = ((unsigned int)same_int == fi->st()->st_uid); break; case GSAME_GROUP : sel = ((unsigned int)same_int == fi->st()->st_gid); break; case GSAME_MODE : sel = ((unsigned int)same_int == fi->st()->st_mode); break; case GSAME_TYPE : sel = ( same_str == fi->type_str()); break; } fi->sel = sel; } } void vfu_global_select() { mb.undef(); mb.push( L"S All" ); mb.push( L"A All (+Dirs)" ); mb.push( L"R Reverse selection" ); mb.push( L"C Clear selection" ); mb.push( L"P Pack (list selected only)" ); mb.push( L"H Hide selected" ); mb.push( L"D Different" ); mb.push( L". Hide dirs" ); mb.push( L", Hide dotfiles" ); mb.push( L"= Select by mask (with directories)" ); mb.push( L"+ Select by mask (w/o directories)" ); mb.push( L"- Deselect by mask" ); mb.push( L"L Select Same..." ); mb.push( L"X Extended Select..." ); if ( vfu_menu_box( 30, 5, L"Select Files and Directories" ) == -1 ) return; wchar_t wch = menu_box_info.ec; if ( wch == L'X') { if ( work_mode != WM_NORMAL ) { say1( "Extended Select is not available in this mode." ); return; } mb.undef(); mb.push( L"A Select to the begin of the list" ); mb.push( L"E Select to the end of the list" ); mb.push( L"--searching--" ); mb.push( L"F Find string (no case)" ); mb.push( L"S Scan string (case sense)" ); mb.push( L"H Hex string" ); mb.push( L"/ Regular expression" ); mb.push( L"\\ Reg.exp (no case)" ); // mb.push( "--other--" ); // mb.push( "M Mode/Attributes" ); if ( vfu_menu_box( 32, 6, L"Extended Select" ) == -1 ) return; wch = menu_box_info.ec; if (wch == L'S') wch = L'B'; /* 'B' trans scan */ if (wch == L'H') wch = L'E'; /* 'E' trans hex */ if (wch == L'A') wch = L'<'; /* '<' trans to begin */ if (wch == L'E') wch = L'>'; /* '>' trans to end */ } switch(wch) { case L'S' : { for (int z = 0; z < files_list_count(); z++) if (!files_list_get(z)->is_dir()) files_list_get(z)->sel = 1; } break; case L'A' : { for (int z = 0; z < files_list_count(); z++) files_list_get(z)->sel = 1; } break; case L'R' : { int z; for (z = 0; z < files_list_count(); z++) if (!files_list_get(z)->is_dir()) files_list_get(z)->sel = !files_list_get(z)->sel; } break; case L'C' : { int z; for (z = 0; z < files_list_count(); z++) files_list_get(z)->sel = 0; } break; case L'P' : { int z; for (z = 0; z < files_list_count(); z++) { if (!files_list_get(z)->sel) files_list_del(z); } files_list_pack(); } break; case L'H' : { int z; for (z = 0; z < files_list_count(); z++) { if (files_list_get(z)->sel) files_list_del(z); } files_list_pack(); } break; case L'.' : { int z; for (z = 0; z < files_list_count(); z++) { if (files_list_get(z)->is_dir()) files_list_del(z); } files_list_pack(); } break; case L',' : { int z; for (z = 0; z < files_list_count(); z++) { if (files_list_get(z)->name()[0] == '.') files_list_del(z); } files_list_pack(); } break; case L'+' : case L'=' : case L'-' : { VString mask; int selaction = 0; if (wch != L'-') selaction = 1; if (wch == L'+') say1("Select by mask: (w/o directories)"); else if (wch == '=') say1("Select by mask: (with directories)"); else say1("Deselect by mask:"); if ( vfu_get_str( "", mask, HID_GS_MASK )) { VArray mask_array = str_split( " +", mask ); while( ( mask = mask_array.pop() ) != "" ) { if (opt.mask_auto_expand) vfu_expand_mask( mask ); int z = 0; for (z = 0; z < files_list_count(); z++) { if ( files_list_get(z)->is_dir() && wch == L'+') continue; if ( FNMATCH( mask, files_list_get(z)->name_ext() ) == 0) files_list_get(z)->sel = selaction; } } } say1( " " ); say2( " " ); } break; case L'D' : { if ( work_mode != WM_NORMAL ) { say1( "GlobalSelect/Different not available in this mode." ); break; } VString target; if ( vfu_get_dir_name( "Target directory:", target )) { str_fix_path( target ); int z = 0; for (z = 0; z < files_list_count(); z++) { if ( files_list_get(z)->is_dir() ) continue; say1( files_list_get(z)->name() ); files_list_get(z)->sel = (vfu_cmp_files_crc32( work_path, target, files_list_get(z)->name() ) != 0 ); } } say1( "Done." ); say2( " " ); } break; case L'/': case L'\\': case L'E': case L'F': case L'B': { say1(""); VString pat; if ( vfu_get_str( "Search string: ", pat, HID_GS_GREP ) ) { fsize_t size = 0; say1(""); say2(""); size = 0; for ( int z = 0; z < files_list_count(); z++ ) { size += files_list_get(z)->size(); if ( files_list_get(z)->is_dir() ) continue; int pos = -1; switch( wch ) { case L'F': pos = file_string_search( pat, files_list_get(z)->name(), "i" ); break; case L'B': pos = file_string_search( pat, files_list_get(z)->name(), "" ); break; case L'E': pos = file_string_search( pat, files_list_get(z)->name(), "h" ); break; case L'/': pos = file_string_search( pat, files_list_get(z)->name(), "r" ); break; case L'\\': pos = file_string_search( pat, files_list_get(z)->name(), "ri" ); break; } files_list_get(z)->sel = ( pos > -1 ); char s[128]; snprintf( s, sizeof(s), "Scanning %4.1f%% (%12.0f bytes in %s ) ", (100.0 * size) / (files_size+1.0), files_list_get(z)->size(), files_list_get(z)->name() ); say1( s ); } } say1(""); say2(""); break; } case L'L': { mb.undef(); mb.push( L"N Name" ); mb.push( L"E Extension" ); mb.push( L"S Size" ); mb.push( L"T Time" ); mb.push( L"I Time (1 min.round)" ); mb.push( L"D Date" ); mb.push( L"M Date+Time" ); mb.push( L"A Attr/Mode" ); mb.push( L"O Owner" ); mb.push( L"G Group" ); mb.push( L"Y Type (TP)" ); vfu_menu_box( 32, 6, L"Select Same..." ); wch = menu_box_info.ec; switch ( wch ) { case L'N' : vfu_global_select_same( GSAME_NAME ); break; case L'E' : vfu_global_select_same( GSAME_EXT ); break; case L'S' : vfu_global_select_same( GSAME_SIZE ); break; case L'M' : vfu_global_select_same( GSAME_DATETIME ); break; case L'T' : vfu_global_select_same( GSAME_TIME ); break; case L'I' : vfu_global_select_same( GSAME_TIME1 ); break; case L'D' : vfu_global_select_same( GSAME_DATE ); break; case L'O' : vfu_global_select_same( GSAME_OWNER ); break; case L'G' : vfu_global_select_same( GSAME_GROUP ); break; case L'A' : vfu_global_select_same( GSAME_MODE ); break; case L'Y' : vfu_global_select_same( GSAME_TYPE ); break; } } break; case L'M': { mode_str_t mode_str; strcpy( mode_str, MODE_STRING ); if(vfu_edit_attr( mode_str, 0 )) { for ( int z = 0; z < files_list_count(); z++ ) files_list_get(z)->sel = (strcmp( files_list_get(z)->mode_str()+1, mode_str+1 ) == 0); do_draw = 1; } } break; case L'<' : { if( files_list_count() > 0) for (int z = 0; z <= FLI; z++) if (!files_list_get(z)->is_dir()) files_list_get(z)->sel = 1; } break; case L'>' : { if( files_list_count() > 0) for (int z = FLI; z < files_list_count(); z++) if (!files_list_get(z)->is_dir()) files_list_get(z)->sel = 1; } break; } update_status(); do_draw = 1; } /*--------------------------------------------------------------------------*/ int vfu_user_external_find( wchar_t key, const char* ext, const char* type, VString *shell_line ) { VArray split; VString str; VString ext_str = ext; VString type_str = type; if ( ext_str == "" ) ext_str = "."; ext_str += "."; type_str = "." + type_str + "."; int z; for ( z = 0; z < user_externals.count(); z++ ) { split = str_split( ",", user_externals[z] ); if ( key_by_name( split[1] ) != key ) continue; /* if key not the same -- skip */ if ( split[2] != "*" ) /* if we should match and extension */ { if ( opt.lower_case_ext_config ) { str_low( split[2] ); str_low( ext_str ); str_low( type_str ); } if ( str_find( split[2], ext_str ) == -1 && str_find( split[2], type_str ) == -1 ) continue; /* not found -- next one */ } if ( shell_line ) /* if not NULL -- store shell line into it */ (*shell_line) = split[3]; return z; } return -1; } /*--------------------------------------------------------------------------*/ void vfu_user_external_exec( wchar_t key ) { if ( files_list_count() == 0 ) { say1( "Directory is empty: user externals are disabled!" ); return; } VString shell_line; TF *fi = FLCUR; if (vfu_user_external_find( key, fi->ext(), fi->type_str(), &shell_line ) != -1) { if ( work_mode == WM_NORMAL ) vfu_shell( shell_line, "" ); else if ( work_mode == WM_ARCHIVE ) { vfu_user_external_archive_exec( shell_line ); } } else { say( 1, cNORMAL, "No user external defined for this key and extension (%lx,%s)", key, fi->ext() ); } } /*--------------------------------------------------------------------------*/ VString tools_last_target; void vfu_tools() { mb.undef(); mb.push( L"R Real path" ); mb.push( L"D ChDir to symlink path" ); mb.push( L"G Go to symlink target" ); mb.push( L"B Go back to last target" ); mb.push( L"T Make directory" ); mb.push( L"P Path bookmarks" ); mb.push( L"A Rename tools..." ); mb.push( L"C Classify files" ); if ( vfu_menu_box( 30, 5, L"Tools" ) == -1 ) return; switch( menu_box_info.ec ) { case L'T' : { VString str; if (vfu_get_str( "Make directory(ies) (use space for separator)", str, HID_MKPATH )) { int err = 0; int z; VArray ms; ms = str_split( " +", str ); for ( z = 0; z < ms.count(); z++ ) if( make_path( ms[z] ) ) { say1( "Cannot create directory:" ); say2( ms[z] ); con_getwch(); err++; } if ( err == 0 ) say1( "MKDIR: ok." ); return; } } return; case L'P' : bookmark_goto( 0 ); return; } if( files_list_count() < 1 ) { say1( "No files..." ); return; } TF *fi = FLCUR; switch( menu_box_info.ec ) { case L'R' : { say1( expand_path( fi->name() ) ); return; } case L'D' : { if( ! fi->is_link() ) return; tools_last_target = fi->full_name(); if( ! fi->is_dir() ) return; vfu_chdir( expand_path( fi->name() ) ); return; } case L'G' : { if( ! fi->is_link() ) return; tools_last_target = fi->full_name(); VString target = vfu_readlink( fi->full_name() ); vfu_chdir( expand_path( str_file_path( target ) ) ); vfu_goto_filename( str_file_name_ext( target ) ); return; } case L'B' : { if( tools_last_target == "" ) return; VString target = tools_last_target; tools_last_target = fi->full_name(); vfu_chdir( expand_path( str_file_path( target ) ) ); vfu_goto_filename( str_file_name_ext( target ) ); return; } case L'A' : vfu_tool_rename(); return; case L'C' : vfu_tool_classify(); return; } } /*--------------------------------------------------------------------------*/ void bookmark_goto( wchar_t wch ) { VString t; WString wt; if ( wch == 0 ) { int z; mb.undef(); mb.push( L"A Bookmark current directory" ); mb.push( L"` Change working directory" ); mb.push( L"---" ); for( z = 0; z < 10; z++ ) { const char* ps = path_bookmarks.get( z ); if( !ps ) break; sprintf( t, "%d %s", ( z + 1 ) % 10, ps ); wt = t; mb.push( str_dot_reduce( wt, 60 ) ); } if ( vfu_menu_box( 5, 5, L"Path bookmarks") == -1 ) return; wch = menu_box_info.ec; } switch( wch ) { case L'`' : vfu_chdir( NULL ); return; case L'A' : bookmark_hookup(); return; } if ( wch >= L'1' && wch <= L'9' && str_len( path_bookmarks[ wch - L'1' ] ) > 0 ) { vfu_chdir( path_bookmarks[ wch - L'1' ] ); return; } } void bookmark_hookup() { int found = -1; for( int z = 0; z < path_bookmarks.count(); z++ ) { if( work_path == path_bookmarks[z] ) found = z; } if( found > -1 ) { say1( "Current directory is already hooked", chRED ); return; } path_bookmarks.push( work_path ); if ( path_bookmarks.count() > 10 ) path_bookmarks.shift(); } /*--------------------------------------------------------------------------*/ void vfu_command() { VString cmd; if ( vfu_get_str( "Command: ", cmd, HID_COMMANDS ) ) vfu_shell( cmd, "" ); } /*--------------------------------------------------------------------------*/ void vfu_rename_file_in_place() { if ( files_list_count() <= 0 ) { say1( "No files" ); return; } TF *fi = FLCUR; int y = ( FLI - FLP ) + 4; int x = tag_mark_pos + 3; WString www = fi->name(); TextInput( x, y, "", MAX_PATH, con_max_x() - tag_mark_pos - 3, www ); // FIXME: check return res VString str = www; if( str != fi->name() ) { if ( file_exist(str) ) say1( "Cannot rename: destination name exists!" ); else if(rename(fi->name(), str.data()) == 0) { fi->set_name( str ); say1("RENAME: ok."); } else { say1("RENAME: failed."); say2errno(); } } do_draw = 1; } /*--------------------------------------------------------------------------*/ void vfu_change_file_mask( const char* a_new_mask ) { VString tmp = files_mask; int new_mask = 0; if ( a_new_mask ) { tmp = a_new_mask; new_mask = 1; } else { new_mask = vfu_get_str( "", tmp, HID_FMASK, 6, 1 ); do_draw = 1; } if(new_mask) { str_cut_spc( tmp ); if ( str_len( tmp ) < 1 ) tmp = "*"; files_mask = tmp; files_mask_array = str_split( " +", files_mask ); if ( opt.mask_auto_expand ) { int z; for ( z = 0; z < files_mask_array.count(); z++ ) vfu_expand_mask( files_mask_array[z] ); } vfu_read_files(); } } /*--------------------------------------------------------------------------*/ /* n == 0..nnn for nnn only, -1 current only, -2 all */ int __vfu_dir_size_followsymlinks = 0; int __vfu_dir_size_samedevonly = 0; void vfu_directory_sizes( wchar_t wch ) { int z; int dir_size_mode = __vfu_dir_size_followsymlinks | __vfu_dir_size_samedevonly; wch = toupper( wch ); if ( wch == 0 ) { mb.undef(); mb.push( L"E Specify directory" ); mb.push( L"Z Directory under cursor" ); mb.push( L". Current directory `.'" ); mb.push( L"S Selected directories" ); mb.push( L"A All dir's in the list" ); mb.push( L"M Missing sizes dirs" ); mb.push( L"P Drop dir sizes cache (WARNING!)" ); mb.push( L"--directory size options--" ); mb.push( L"N Normal" ); mb.push( L"Y Follow symlinks (WARNING: may loop!)" ); mb.push( L"V Keep on the same device/filesystem only" ); if ( vfu_menu_box( 5, FLPS - 8, L"Directory size of:" ) == -1 ) return; wch = menu_box_info.ec; } DirSizeInfo size_info; say1( "Calculating files size. Press ESCAPE to cancel calculation." ); if ( wch == L'E' || wch == L'.' ) /* specific directory */ { VString target = work_path; if ( wch == L'.' ) target = work_path; else if ( !vfu_get_dir_name( "Calculate size of directory: ", target ) ) return; fsize_t dir_size = vfu_dir_size( target, 1, dir_size_mode, &size_info ); if ( dir_size == -1 ) return; say1( "Path: " + target ); say2( size_info.str() ); } else if ( wch == L'A' || wch == L'S' || wch == L'M' ) /* all, selected or missing sizes */ { size_cache_sort( 1 ); for( z = 0; z < files_list_count(); z++) { TF *fi = files_list_get(z); if ( ! fi->is_dir() ) continue; if ( wch == L'S' && ! fi->sel ) continue; /* if not sel'd and required -- skip */ if ( wch == L'M' && fi->size() > 0 ) continue; /* if not sel'd and required -- skip */ fsize_t dir_size = -1; if ( fi->is_link() ) dir_size = size_cache_get_pending( fi->name() ); if( dir_size == -1 ) dir_size = vfu_dir_size( fi->name(), 0, dir_size_mode, &size_info ); if ( dir_size == -1 ) break; // break requested fi->set_size( dir_size ); } size_cache_sort(); say1( wch == L'S' ? "Path: *** selected dirs in the list ***" : "Path: *** all dirs in the list ***" ); say2( size_info.str() ); } else if ( wch == L'Z' ) /* single one, under cursor */ { if ( FLCUR->is_dir() ) { FLCUR->set_size( vfu_dir_size( FLCUR->name(), 1, dir_size_mode, &size_info ) ); say1( "Path: " + work_path + VString( FLCUR->name() ) ); say2( size_info.str() ); } else say1("This is not directory..."); } else if( wch == L'N' ) /* normal traverse mode */ { __vfu_dir_size_followsymlinks = 0; __vfu_dir_size_samedevonly = 0; say1( "Directory size calculation mode set to NORMAL (all dev/fs, no symlinks)" ); } else if( wch == L'Y' ) /* follow symlinks */ { __vfu_dir_size_followsymlinks = __vfu_dir_size_followsymlinks ? 0 : DIR_SIZE_FOLLOWSYMLINKS; if( __vfu_dir_size_followsymlinks ) say1( "Directory size calculation will FOLLOW symlinks! LOOP WARNING!" ); else say1( "Directory size calculation will NOT follow symlinks" ); } else if( wch == L'V' ) /* traverse same device/filesystem only */ { __vfu_dir_size_samedevonly = __vfu_dir_size_samedevonly ? 0 : DIR_SIZE_SAMEDEVONLY; if( __vfu_dir_size_samedevonly ) say1( "Directory size calculation will KEEP THE SAME device/filesystem only!" ); else say1( "Directory size calculation will follow ALL devices/filesystems" ); } if( wch == L'P' ) { int scc = size_cache.count(); wchar_t wch = 0; if( scc > 0 ) { vfu_beep(); say2( VString() + "Entries to be removed: " + scc ); wch = towlower( vfu_ask( L"Directory sizes cache will be dropped? " L"( D=Yes, drop data!, ESC=cancel )", L"d" )); say1( "" ); say2( "" ); } else { say1( "Directory sizes cache is empty." ); } if( wch == L'd') { size_cache.undef(); say1( VString() + "Directory sizes cache dropped. Removed entries: " + scc ); } } do_draw = 1; update_status(); if ( opt.sort_order == 'S' && wch < 0 ) vfu_sort_files(); } /*--------------------------------------------------------------------------*/ void vfu_edit_entry( ) { char errstr[128]; int one = ( sel_count == 0 ); int z; WString str; mb.undef(); mb.push( L"M Mode" ); mb.push( L"A Octal Mode" ); mb.push( L"O Owner/Group" ); mb.push( L"N Name (TAB)" ); mb.push( L"T Time/Touch Mod+Acc Times" ); mb.push( L"I Modify Time" ); mb.push( L"E Access Time" ); mb.push( L"L Edit SymLink Reference" ); if ( sel_count ) { /* available only when selection exist */ mb.push( L"--"); mb.push( L"+ Target: Toggle" ); mb.push( L"C Target: Current File" ); mb.push( L"S Target: Selection" ); } while(1) { while(1) { str = L"Edit entry: "; str += one ? L"current file" : L"[ SELECTION ] "; menu_box_info.ac = 9; z = vfu_menu_box( 50, 5, str ); if ( z == -1 ) return; /* canceled */ if (menu_box_info.ec == 9 ) menu_box_info.ec = L'N'; if (menu_box_info.ec == L'+') { one = !one; continue; } if (menu_box_info.ec == L'S') { one = 0; continue; } if (menu_box_info.ec == L'C') { one = 1; continue; } break; } if ( menu_box_info.ec == L'N' ) /* name (rename) */ { vfu_rename_file_in_place(); break; } else if ( menu_box_info.ec == L'M' || menu_box_info.ec == L'A' ) /* attributes/mode */ { mode_str_t new_mode; int ok = 1; int err = 0; if ( menu_box_info.ec == L'M' ) { if (one) { strcpy( new_mode, FLCUR->mode_str() ); file_get_mode_str( FLCUR->name(), new_mode); } else strcpy(new_mode, MODE_MASK); ok = vfu_edit_attr(new_mode, !one ); } else { say1( "Enter octal mode (i.e. 755, 644, 1777, etc.)" ); VString str; int z = vfu_get_str( "", str, HID_OMODE ); str_cut_spc( str ); mode_t m; unsigned int im; sscanf( str, "%o", &im ); m = im; file_get_mode_str( m, new_mode ); ok = (z && str_len(str) > 0); } if( ok ) { for ( z = 0; z < files_list_count(); z++ ) if ( (one && FLI == z) || (!one && files_list_get(z)->sel) ) { TF *fi = files_list_get(z); if(file_set_mode_str(fi->name(), new_mode) == 0) { fi->update_stat(); do_draw = 1; } else err++; } } if (err) sprintf( errstr, "Change attr/mode errors: %d", err ); else strcpy( errstr, "Change attr/mode ok." ); say1( errstr ); if (err) say2errno(); break; } else if ( menu_box_info.ec == L'T' || menu_box_info.ec == L'I' || menu_box_info.ec == L'E' ) { char t[128]; strcpy( t, "Change times: " ); strcat( t, (menu_box_info.ec == 'T') ? "MODIFY,ACCESS" : ( (menu_box_info.ec == 'M') ? "MODIFY" : "ACCESS" ) ); strcat( t, one ? " for the current file:" : " for SELECTED FILES/DIRS:" ); strcat( t, " PLEASE KEEP THE FORMAT!" ); say1( t ); strcpy( t, time2str(time(NULL))); t[24] = 0; VString str = t; int z = vfu_get_str( "", str, HID_EE_TIME ); if( !(z && str_len(str) > 0) ) break; time_t new_time = str2time( str ); if ( new_time == 0 ) // well, this is 1.1.1970 but I consider it wrong { say1( "Wrong time string format." ); break; } int err = 0; struct utimbuf tb; for ( z = 0; z < files_list_count(); z++ ) if ( (one && FLI == z) || (!one && files_list_get(z)->sel) ) { TF *fi = files_list_get(z); tb.actime = fi->st()->st_atime; tb.modtime = fi->st()->st_mtime; if (menu_box_info.ec == L'M') tb.modtime = new_time; if (menu_box_info.ec == L'S') tb.actime = new_time; if (menu_box_info.ec == L'T') tb.modtime = new_time; if (menu_box_info.ec == L'T') tb.actime = new_time; if (utime( fi->name(), &tb ) == 0) { fi->update_stat(); do_draw = 1; } else err++; } if (err) sprintf( errstr, "Time touch errors: %d", err ); else strcpy( errstr, "Time touch ok." ); say1( errstr ); if (err) say2errno(); break; } else if ( menu_box_info.ec == L'O' ) { VString str; if (one) say1("Enter new `user.group | user | .group' for current file:"); else say1("Enter new `user.group | user | .group' for all SELECTED files:"); if( !(vfu_get_str( "", str, HID_EE_OWNER ) && str_len(str) > 0) ) break; VRegexp re( "^ *([^\\.]*)(\\.([^\\.]*))? *$" ); if( ! re.m( str ) ) { say1("Format is 'uid.gid', for example 'cade.users', 'cade.', '.users'"); break; } int uid = -1; int gid = -1; struct passwd *pwd = getpwnam(re[1]); if ( pwd ) uid = pwd->pw_uid; struct group *grp = getgrnam(re[3]); if ( grp ) gid = grp->gr_gid; int err = 0; for ( z = 0; z < files_list_count(); z++ ) if ( (one && FLI == z) || (!one && files_list_get(z)->sel) ) { TF *fi = files_list_get(z); int u = uid; int g = gid; if (u == -1) u = fi->st()->st_uid; if (g == -1) g = fi->st()->st_gid; if(chown(fi->name(), u, g) == 0) { fi->update_stat(); do_draw = 1; } else err++; } if (err) sprintf( errstr, "Change owner/group errors: %d", err ); else strcpy( errstr, "Change owner/group ok." ); say1( errstr ); if (err) say2errno(); break; } else if ( menu_box_info.ec == 'L' ) { if ( ! one ) { say1( "Cannot edit symlink reference for selection..." ); break; } TF* fi = FLCUR; if ( ! fi->is_link() ) { say1( "This is not a symlink..." ); break; } fname_t t = ""; t[ readlink( fi->name(), t, MAX_PATH - 1 ) ] = 0; VString str = t; //if ( vfu_get_str( "", str, 0 ) ) if ( vfu_get_dir_name( "SymLink Target:", str, 1, 'A' ) ) { fi->drop_view(); do_draw = 1; say2( "" ); if ( unlink( fi->name() ) || symlink( str, fi->name() ) ) { say1( "Edit SymLink reference error..." ); say2errno(); } else { say1( "Edit SymLink reference ok." ); } } break; } } return; } /*--------------------------------------------------------------------------*/ void vfu_jump_to_mountpoint( int all __attribute__((unused)) ) { VString str; char t[2048]; int z; VArray va; if ( va.fload( "/etc/mtab" ) ) return; if (va.count() < 1) return; mb.undef(); for(z = 0; z < va.count(); z++) { str = va[z]; str_cut( str, " \t"); str_word( str, " \t", t ); /* get device name */ str_cut( str, " \t"); str_word( str, " \t", t ); /* get mount point */ //va.set( z, t ); /* replace line with mount point only */ va[z] = t; /* replace line with mount point only */ struct statfs stafs; statfs( t, &stafs ); int hk = ('A'+ z); /* hot key */ fsize_t fs_free = stafs.f_bsize * ( opt.show_user_free ? stafs.f_bavail : stafs.f_bfree ); fsize_t fs_total = stafs.f_bsize * stafs.f_blocks; VString str_free = opt.use_gib_usage ? fsize_fmt( fs_free, 1 ) : size_str_compact( fs_free ); VString str_total = opt.use_gib_usage ? fsize_fmt( fs_total, 1 ) : size_str_compact( fs_total ); sprintf( str, "%c | %15s | %15s | %-30s ", hk, (const char*)str_free, (const char*)str_total, //stafs.f_bsize * ( opt.show_user_free ? stafs.f_bavail : stafs.f_bfree ) / (1024.0*1024.0), //stafs.f_bsize * stafs.f_blocks / (1024.0*1024.0), (const char*)(str_dot_reduce( t, 30 )) ); mb.push(WString(str)); } menu_box_info.ac = UKEY_CTRL_U; z = vfu_menu_box( 5, 5, L"Jump to mount-point (free/total) Ctrl+U=umount" ); if ( z == -1 ) return; if ( menu_box_info.ec == UKEY_CTRL_U ) { str = va[z]; str_fix_path( str ); if ( pathcmp( str, work_path ) == 0 ) { say1( "Warning: cannot unmount current directory" ); return; } str = "umount " + str + " 2> /dev/null"; snprintf( t, sizeof(t), "Unmounting, exec: %s", str.data() ); say1( t ); if (system( str ) == 0) say1( "umount ok" ); else say1( "umount failed" ); } else vfu_chdir( VString( va[z] ) ); } /*--------------------------------------------------------------------------*/ void vfu_user_menu() { VArray split; VArray lines; VString des; int z; mb.undef(); for ( z = 0; z < user_externals.count(); z++ ) { split = str_split( ",", user_externals[z] ); if ( strcasecmp( split[1], "menu" ) ) continue; /* not menu item -- skip */ /* FIXME: should we care about ext's or user will override this? */ /* split[2]; // extensions */ des = split[0]; if ( des != "---" ) /* not separator */ { /* fix menu hotkeys */ str_ins( des, 1, " " ); str_set_ch( des, 0, toupper(str_get_ch(des, 0)) ); } lines.push( split[3] ); mb.push( WString( des ) ); } if ( mb.count() == 0 ) { say1("No menu user externals defined..."); return; } z = vfu_menu_box( 5, 5, L"User menu (externals) " ); if ( z == -1 ) return; if ( work_mode == WM_NORMAL ) vfu_shell( lines[z], "" ); else if ( work_mode == WM_ARCHIVE ) { VString str = lines[z]; vfu_user_external_archive_exec( str ); } } /*--------------------------------------------------------------------------*/ void vfu_file_find_results() { do_draw = 2; if ( file_find_results.count() == 0 ) { say1("No file find results..."); return; } ConMenuInfo bi; bi.cn = cSTATUS; bi.ch = 31; bi.ti = cINFO; bi.ac = 'p'; say1center("------- ESC Exit ----- ENTER Chdir to target ----- P Panelize all results -----", cINFO ); say2(""); int z = con_full_box( 1, 1, L"VFU File find results", &file_find_results, &bi ); if ( bi.ec == 13 ) { VString fname; VString str = file_find_results[z]; str_trim_left( str, str_find( str, " | " ) + 3 ); z = str_rfind( str, '/' ); fname = str; str_sleft( str, z+1 ); str_trim_left( fname, z+1 ); vfu_chdir( str ); for( z = 0; z < files_list_count(); z++ ) if ( pathcmp( fname, files_list_get(z)->name_ext() ) == 0 ) { FLGO(z); vfu_nav_update_pos(); break; } } else if ( tolower(bi.ec) == 'p' ) { list_panelizer.undef(); for ( z = 0; z < file_find_results.count(); z++ ) { VString str = file_find_results[z]; str_trim_left( str, str_find( str, " | " ) + 3 ); list_panelizer.push( str ); } vfu_read_files( 0 ); } file_find_results.fsave( filename_ffr ); con_cs(); } /*--------------------------------------------------------------------------*/ VArray __ff_masks; VString __ff_path; VString __ff_pattern; VString __ff_opt; int __ff_rescount; int __ff_process( const char* origin __attribute__((unused)), /* origin path */ const char* fname, /* full file name */ const struct stat* st, /* stat struture or NULL */ int is_link __attribute__((unused)), /* 1 if link */ int flag ) { VString str; if ( flag == FTWALK_DX ) return 0; if ( vfu_break_op() ) return 1; if ( flag == FTWALK_D ) { str = fname; str = str_dot_reduce( str, con_max_x()-1 ); say2( str ); } const char *pc = strrchr( fname, '/' ); if (pc) pc++; else pc = fname; int add = 0; int z; for ( z = 0; z < __ff_masks.count(); z++ ) if ( opt.no_case_glob ? FNMATCH_NC( __ff_masks[z], pc ) == 0 : FNMATCH( __ff_masks[z], pc ) == 0 ) { add = 1; break; } if ( add && __ff_pattern != "" ) add = ( file_string_search( __ff_pattern, fname, __ff_opt ) > -1 ); if (!add) return 0; __ff_rescount++; char time_str[32]; VString size_str; time_str_compact( st->st_mtime, time_str ); if ( flag == FTWALK_D ) size_str = "[DIR]"; else size_str = size_str_compact( st->st_size ); str_pad( size_str, 7 ); str = ""; str = str + time_str + " " + size_str + " | " + fname; WString wstr; wstr.set_failsafe( str ); file_find_results.push( wstr ); wstr = str_dot_reduce( wstr, con_max_x()-1 ); con_puts( "\r" ); con_puts( VString( wstr ), cSTATUS ); con_puts( "\n" ); str = "Found items: "; str += __ff_rescount; say1( str ); return 0; } void vfu_file_find( int menu ) { VString str; wchar_t wch; if (menu) { if ( vfu_menu_box( L"File find", L"L Last find results,D Drop find results,N File find,F Find string (no case),S Scan string (case),B Scan string (case),E Hex string,/ Regular expresion,\\ Reg.exp (no case)", 5 ) == -1 ) return; wch = menu_box_info.ec; } else wch = L'N'; if ( wch == L'L' ) { if ( file_find_results.count() == 0 ) file_find_results.fload( filename_ffr ); vfu_file_find_results(); return; } if ( wch == L'D' ) { file_find_results.undef(); vfu_file_find_results(); /* FIXME: this will show `no results' warning */ return; } __ff_pattern = ""; if ( str_find( "FSB/\\", wch ) != -1 ) /* we want search for pattern */ { __ff_pattern = vfu_hist_get( HID_FFGREP, 0 ); if (!vfu_get_str( "Enter search pattern: ", __ff_pattern, HID_FFGREP )) return; if (wch == L'F' ) __ff_opt = "i "; else if (wch == L'S' ) __ff_opt = " "; else if (wch == L'B' ) __ff_opt = " "; else if (wch == L'E' ) __ff_opt = "h "; else if (wch == L'/' ) __ff_opt = "r "; else if (wch == L'\\') __ff_opt = "ri"; else {}; } str = vfu_hist_get( HID_FFMASK, 0 ); if ( str == "" ) str = "*"; if (!vfu_get_str( "Enter find masks (space separated): ", str, HID_FFMASK )) return; __ff_masks = str_split( " +", str ); str = work_path; if (!vfu_get_dir_name( "Enter start path: ", str )) return; __ff_path = str; /*--------------------------------------*/ if ( opt.mask_auto_expand ) { int z; for ( z = 0; z < __ff_masks.count(); z++ ) vfu_expand_mask( __ff_masks[z] ); } con_cs(); con_ta( cINFO ); vfu_con_out( 1, 1, HEADER ); sprintf( str, "Find mask: %s", vfu_hist_get( HID_FFMASK, 0 ) ); vfu_con_out( 1, 2, str ); sprintf( str, "Start path: %s", __ff_path.data() ); vfu_con_out( 1, 3, str ); if ( __ff_pattern != "" ) { sprintf( str, "Containing pattern: %s", __ff_pattern.data() ); vfu_con_out( 1, 4, str ); } file_find_results.undef(); __ff_rescount = 0; ftwalk( __ff_path, __ff_process ); vfu_file_find_results(); } /*--------------------------------------------------------------------------*/ void vfu_read_files_menu() { char t[1024]; VArray list; int z; VString str; mb.undef(); /* I don't format src like this but it gives clear idea what is all about */ mb.push( L"T Rescan DirTree" ); list.push(""); mb.push( L"F Rescan Files" ); list.push(""); mb.push( L"R Rescan Files Recursive" ); list.push(""); mb.push( L"L Refresh all views/screen (Ctrl+L)" ); list.push(""); if ( panelizers.count() > 0 ) { mb.push( L"--panelizers---" ); list.push(""); for ( z = 0; z < panelizers.count(); z++ ) { str = panelizers[z]; str_word( str, ",", t ); /* fix menu hotkeys */ str_ins( t, 1, " " ); str_set_ch( t, 0, toupper( str_get_ch( t, 0 ) ) ); mb.push( WString( t ) ); list.push(str); } } z = vfu_menu_box( 25, 5, L"Read/Rescan Files" ); if ( z == -1 ) { return; } if ( str_len( list[z] ) ) { /* so panelizer has been choosed */ external_panelizer = list[z]; str = ""; /* no shell options by default */ vfu_update_shell_line( external_panelizer, str ); vfu_read_files( 0 ); } else switch( menu_box_info.ec ) { case L'T' : tree_rebuild(); break; case L'F' : vfu_read_files( 0 ); break; case L'R' : vfu_read_files( 1 ); break; case L'L' : vfu_reset_screen(); break; } } /*--------------------------------------------------------------------------*/ void vfu_inc_search( int use_last_one ) { WString str; wchar_t wch; if( use_last_one && last_inc_search == "" ) use_last_one = 0; if( use_last_one && last_inc_search != "" ) str = last_inc_search; VString no_case_opt_str = opt.no_case_glob ? " no-case " : " "; if( use_last_one ) { say1( "Incremental" + no_case_opt_str + "search: ( ALT+S for next match )" ); wch = 9; } else { say1( "Incremental" + no_case_opt_str + "search: ( TAB for next or 'size:NNN' search )" ); wch = con_getwch(); } WRegexp size_re( L"^size:\\s*(\\d+)$" ); // TODO: allow "size:1024+" while( ( wch >= 32 && ( ! UKEY_IS_WIDE_CTRL( wch ) ) ) || wch == 8 || wch == UKEY_BACKSPACE || wch == 9 ) { if ( wch == 8 || wch == UKEY_BACKSPACE ) str_trim_right( str, 1 ); else if ( wch != 9 ) str_add_ch( str, wch ); say2( VString( str ) ); if ( files_list_count() == 0 ) { wch = con_getwch(); continue; } int z; if ( wch == 9 ) { z = FLI + 1; if ( z > FLMAX ) z = FLMIN; } else z = FLI; int direction = 1; int found = 0; int loops = 0; VString s_mask = str; int s_size = 0; if( size_re.m( str ) ) s_size = VString( size_re[1] ).i(); else vfu_expand_mask( s_mask ); while(1) { if ( z > FLMAX ) z = FLMIN; if ( z < FLMIN ) z = FLMAX; if( s_size ) found = files_list_get(z)->size() == s_size; else if( opt.no_case_glob ) found = ( FNMATCH_NC( s_mask, files_list_get(z)->name_ext() ) == 0 ); else found = ( FNMATCH( s_mask, files_list_get(z)->name_ext() ) == 0 ); if ( found ) break; z += direction; if ( loops++ > files_list_count() ) break; } if (found) { FLGO(z); vfu_redraw(); show_pos( FLI + 1, files_list_count() ); } if( use_last_one ) break; else wch = con_getwch(); } last_inc_search = str; if( use_last_one ) return; say1( "" ); say2( "" ); } /*--------------------------------------------------------------------------*/ void vfu_goto_filename( const char* fname ) { if ( files_list_count() == 0 ) return; for (int z = 0; z < files_list_count(); z++) { if( strcmp( fname, files_list_get(z)->name_ext() ) ) continue; FLGO(z); return; } } /*######################################################################*/ // #include /* memory allocation debug */ int main( int argc, char* argv[] ) { setlocale(LC_ALL,""); #ifndef NDEBUG // mtrace(); /* memory allocation debug */ #endif print_help_on_exit = 0; con_init(); con_cs(); con_fg( cNORMAL ); con_bg( cBLACK ); con_chide(); vfu_init(); argc > 1 ? vfu_cli( argc, argv ) : vfu_run(); /* ... :) */ vfu_done(); con_cs(); con_cshow(); con_done(); if( print_help_on_exit ) vfu_help_cli(); /* printf("%s\n [http://cade.noxrun.com/]\nThank You for using VFU!\n\n", HEADER ); */ return 0; } /*######################################################################*/ vfu-5.09/vfu/vfusetup.h0000644000175000017500000000634214444676602013475 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _SETUP_H_ #define _SETUP_H_ /* * * This file is used to setup some global parameters as * files locations and other similar things... * (it is separated from vfu.h just to keep it clear) * */ #define VFU_VERSION "5.09" #define HEADER "VF/U v" VFU_VERSION " Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski 'Cade'" #define CONTACT " [http://cade.noxrun.com]" #define FILENAME_OPT "vfu.options" #define FILENAME_CONF "vfu.conf" #define FILENAME_TREE "vfu.tree" #define FILENAME_SIZE_CACHE "vfu.size" #define FILENAME_HISTORY "vfu.history" #define FILENAME_FFR "vfu.ffr" // The SYSCONFDIR macro can be used here, which is defined when // ../configure --sysconfdir=nnnn is used. It no option is given, it // defaults to /usr/local/etc on most systems. // The macro has been added to Makefile.am // -andy5995 2018-12-29 #ifndef FILENAME_CONF_GLOBAL0 #define FILENAME_CONF_GLOBAL0 "/etc/" FILENAME_CONF #endif #ifndef FILENAME_CONF_GLOBAL1 #define FILENAME_CONF_GLOBAL1 "/usr/local/etc/" FILENAME_CONF #endif #ifndef FILENAME_CONF_GLOBAL2 #define FILENAME_CONF_GLOBAL2 "/usr/libexec/vfu/" FILENAME_CONF #endif #define RX_TEMP_LIST "RX_TEMP_LIST" /* colors */ #define cPLAIN (cNORMAL) // normal white #define cHEADER (chRED) // files list headers #define cINFO (chYELLOW) // general info messages #define cINFO2 (chYELLOW) // bottom information panel #define cINPUT (CONCOLOR(chWHITE,cBLUE)) // normal input lines #define cINPUT2 (CONCOLOR(cBLACK,cWHITE)) // selected input lines #define cMESSAGE (cWHITE) // all messages #define cSTATUS (cCYAN) // status messages (progress info) #define cSTATUS2 (chCYAN) // alt status messages (copy progress info) #define cWARNING (CONCOLOR(chWHITE,cRED)) // warning messages #define cBAR (CONCOLOR(chWHITE,cBLUE)) // inverted select bar (dir tree) #define cTAG (CONCOLOR(cRED,cWHITE)) // currently selected file #define cMENU_CN (CONCOLOR(chWHITE,cBLUE)) // menu normal #define cMENU_CH (CONCOLOR(chWHITE,cGREEN)) // menu highlite #define cMENU_TI (CONCOLOR(chWHITE,cMAGENTA)) // menu title /* mono config -- never tested! */ /* #define cPLAIN (cWHITE) #define cHEADER (CONCOLOR(cBLACK,cWHITE)) #define cINFO (chWHITE) #define cINFO2 (CONCOLOR(cBLACK,cWHITE)) #define cINPUT (CONCOLOR(cWHITE,cBLACK)) #define cINPUT2 (CONCOLOR(cBLACK,cWHITE)) #define cMESSAGE (chWHITE) #define cSTATUS (cWHITE) #define cSTATUS2 (chWHITE) #define cWARNING (CONCOLOR(cBLACK,cWHITE)) #define cBAR (CONCOLOR(cBLACK,cWHITE)) #define cTAG cBAR #define cMENU_CN (CONCOLOR(cWHITE,cBLACK)) // menu normal #define cMENU_CH (CONCOLOR(cBLACK,cWHITE)) // menu highlite #define cMENU_TI (CONCOLOR(cBLACK,cWHITE)) // menu title */ /* colors setup end */ #endif //_SETUP_H_ vfu-5.09/vfu/vfumenu.h0000644000175000017500000000152214365574242013273 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUMENU_H_ #define _VFUMENU_H_ #include #include "vfuuti.h" #define menu_box_info con_default_menu_info int vfu_toggle_box( int x, int y, const wchar_t *title, ToggleEntry* toggles ); int vfu_menu_box( int x, int y, const wchar_t *title, WArray *wa = &mb ); int vfu_menu_box( const wchar_t* title, const wchar_t* menustr, int row = -1 ); #endif /* _VFUMENU_H_ */ /* eof vfumenu.h */ vfu-5.09/vfu/vfufiles.h0000644000175000017500000000346014365574242013434 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUFILES_H_ #define _VFUFILES_H_ #include "vfu.h" int files_list_count(); int files_list_is_empty( int pos ); TF* files_list_get( int pos ); void files_list_set( int pos, TF* fp ); void files_list_add( TF* fp ); void files_list_trim(); // remove last TF* item void files_list_del( int pos ); void files_list_pack(); void files_list_clear(); /*###########################################################################*/ const char* file_type_str( mode_t mode, int is_link ); /*###########################################################################*/ void vfu_rescan_files( int a_recursive = 0 ); void vfu_read_files( int a_recursive = 0 ); int vfu_add_file( const char* fname, const struct stat *st, int is_link ); void vfu_read_archive_files( int a_recursive ); void vfu_read_local_files( int a_recursive ); void vfu_read_external_files(); void vfu_read_pszlist_files(); int vfu_fmask_match( const char* fname ); /*###########################################################################*/ void vfu_file_entry_move(); /*###########################################################################*/ int namenumcmp( const char* s1, const char* s2 ); int ficmp(int fn1, TF *f2); void __vfu_sort(int l, int r); void vfu_sort_files(); void vfu_arrange_files(); /*###########################################################################*/ #endif //_VFUFILES_H_ vfu-5.09/vfu/vfuview.h0000644000175000017500000000207314365574242013303 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUVIEW_H_ #define _VFUVIEW_H_ #include "vfuopt.h" extern int tag_mark_pos; extern int sel_mark_pos; int get_item_color( TF* fi ); VString fsize_fmt( fsize_t fs, int use_gib = 0 ); /* return commified number */ void show_pos( int curr, int all ); void vfu_drop_all_views(); void vfu_draw(int n); void vfu_redraw(); /* redraw file list and header */ void vfu_redraw_status(); /* redraw bottom status, total,free,selected... */ void vfu_nav_up(); void vfu_nav_down(); void vfu_nav_ppage(); void vfu_nav_npage(); void vfu_nav_home(); void vfu_nav_end(); void vfu_nav_select(); void vfu_nav_update_pos(); #endif //_VFUVIEW_H_ vfu-5.09/vfu/vfuopt.h0000644000175000017500000000766214367022262013134 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUOPT_H_ #define _VFUOPT_H_ #include "see.h" #include "vfuuti.h" #define OPT_SCROLL_PAGESTEP(s) ( (s) == 1 ? 0.3 : (s) == 2 ? 0.5 : 1 ) extern const wchar_t *NOYES[]; extern const wchar_t *FTIMETYPE[]; extern const wchar_t *TAGMARKS[]; extern const wchar_t *COMMA_TYPES[]; struct Options { wchar_t sort_order; wchar_t sort_direction; int sort_top_dirs; fname_t last_copy_path[3]; fname_t path_bookmarks[10]; int f_size; int f_time; int f_mode; int f_group; int f_owner; int f_type; int f_time_type; int long_name_view; int tree_compact; int tree_cd; int show_hidden_files; /* `dot' files in UNIX, `HS' files in dos */ int allow_beep; int use_colors; int use_dir_colors; /* /etc/DIR_COLORS */ int lower_case_ext_config; int copy_free_space_check; int copy_calc_totals; int copy_keep_mode; /* preserve mode, owner, group on copy ? */ int tag_mark_type; int internal_browser; int internal_editor; int mask_auto_expand; int shell_cls; int zap_ro; /* zap/erase read-only files */ int no_case_glob; int show_user_free; /* ...space instead of real fs free */ int menu_borders; int lynx_navigation; /* should <- == - and -> == + */ int default_copy_to_cwd; /* default copy dir always points to CWD */ int auto_mount; int keep_selection; /* on rescan files */ int bytes_freed; /* calc/show bytes freed on erase */ int smart_home_end; /* toggle between first/last entry and first/last file/directory in the list */ int use_si_sizes; int use_gib_usage; int comma_type; int scroll_pagestep; SeeViewerOptions svo; SeeEditorOptions seo; void reset(void) { sort_order = 0; sort_direction = 0; sort_top_dirs = 0; memset(last_copy_path, 0, sizeof last_copy_path); memset(path_bookmarks, 0, sizeof path_bookmarks); f_size = 0; f_time = 0; f_mode = 0; f_group = 0; f_owner = 0; f_type = 0; f_time_type = 0; long_name_view = 0; tree_compact = 0; tree_cd = 0; show_hidden_files = 0; allow_beep = 0; use_colors = 0; use_dir_colors = 0; lower_case_ext_config = 0; copy_free_space_check = 0; copy_calc_totals = 0; copy_keep_mode = 0; tag_mark_type = 0; internal_browser = 0; internal_editor = 0; mask_auto_expand = 0; shell_cls = 0; zap_ro = 0; no_case_glob = 0; show_user_free = 0; menu_borders = 0; lynx_navigation = 0; default_copy_to_cwd = 0; auto_mount = 0; keep_selection = 0; bytes_freed = 0; smart_home_end = 0; use_si_sizes = 0; use_gib_usage = 0; comma_type = 0; scroll_pagestep = 0; svo.reset(); seo.reset(); } }; extern Options opt; int key_by_name( const char* key_name ); time_t vfu_opt_time( const struct stat st ); time_t vfu_opt_time( const struct stat* st ); time_t vfu_opt_time( time_t ctime, time_t mtime, time_t atime ); int set_set( const char *line, const char *keyword, char *target ); int set_set( const char *line, const char *keyword, VString &target ); int set_set( const char *line, const char *keyword, int &target ); int set_set( const char *line, const char *keyword, VArray &splitter ); void vfu_settings_load( VArray* data ); void vfu_settings_save(); void vfu_edit_conf_file(); void vfu_options(); #endif /* _VFUOPT_H_ */ vfu-5.09/vfu/vfufiles.cpp0000644000175000017500000003724414365574242013776 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include #include "vfufiles.h" #include "vfuopt.h" #include "vfuview.h" #include "vfumenu.h" #include "vfudir.h" /*###########################################################################*/ #define FILES_LIST_BUCKET_SIZE (32*1024) TF** files_list = NULL; int files_list_cnt = 0; int files_list_size = 0; /* index in the files list */ ScrollPos file_list_index; void __files_list_resize( int new_size ) { while( new_size < files_list_cnt ) { files_list_cnt--; delete files_list[ files_list_cnt ]; files_list[ files_list_cnt ] = NULL; } int new_files_list_size = ( int( new_size / FILES_LIST_BUCKET_SIZE ) + 1 ) * FILES_LIST_BUCKET_SIZE; if( new_files_list_size == files_list_size ) return; TF** new_files_list = new TF*[ new_files_list_size ]; memset( new_files_list, 0, sizeof(TF*) * ( new_files_list_size ) ); memcpy( new_files_list, files_list, sizeof(TF*) * ( files_list_cnt ) ); delete [] files_list; files_list = new_files_list; files_list_size = new_files_list_size; file_list_index.set_min_max( 0, files_list_cnt - 1 ); } int files_list_count() { return files_list_cnt; } int files_list_is_empty( int pos ) { ASSERT( pos >= 0 && pos < files_list_cnt ); return files_list[pos] == NULL ? 1 : 0; } TF* files_list_get( int pos ) { ASSERT( pos >= 0 && pos < files_list_cnt ); ASSERT( files_list[pos] ); // cannot get empty TF* return files_list[pos]; } void files_list_set( int pos, TF* fp ) { ASSERT( pos >= 0 && pos < files_list_cnt ); files_list[pos] = fp; } void files_list_add( TF* fp ) { __files_list_resize( files_list_cnt + 1 ); files_list_cnt++; files_list_set( files_list_cnt - 1, fp ); } void files_list_trim() { if( files_list_cnt <= 0 ) return; __files_list_resize( files_list_cnt - 1 ); } void files_list_del( int pos ) { ASSERT( pos >= 0 && pos < files_list_cnt ); ASSERT( files_list[pos] ); delete files_list[pos]; files_list[pos] = NULL; } void files_list_clear() { __files_list_resize( 0 ); } void files_list_pack() { int pos = 0; int next = 0; while( pos < files_list_cnt ) { if ( files_list[pos] == NULL ) { next = pos + 1; while ( next < files_list_cnt && files_list[next] == NULL ) next++; if ( next < files_list_cnt && files_list[next] != NULL ) { files_list[pos] = files_list[next]; files_list[next] = NULL; } else break; } else pos++; } files_list_cnt = 0; while ( files_list_cnt < files_list_size && files_list[files_list_cnt] ) files_list_cnt++; /* update scroll parameters */ file_list_index.set_min_max( 0, files_list_cnt - 1 ); file_list_index.set_pagesize( con_max_y() - 7 ); update_status(); vfu_nav_update_pos(); do_draw = 2; } /*###########################################################################*/ static char __file_stat_type_buf[3]; const char* file_type_str( mode_t mode, int is_link ) { strcpy(__file_stat_type_buf, "--"); if (S_ISDIR(mode) && is_link) strcpy(__file_stat_type_buf, "<>"); else // box, but not exact if (S_ISBLK(mode) ) strcpy(__file_stat_type_buf, "=="); else // block, stacked if (S_ISCHR(mode) ) strcpy(__file_stat_type_buf, "++"); else // like dots, separates if (S_ISFIFO(mode)) strcpy(__file_stat_type_buf, "()"); else // () pipe mimic if (S_ISSOCK(mode)) strcpy(__file_stat_type_buf, "@@"); else // internet if (is_link ) strcpy(__file_stat_type_buf, "->"); else // points, link if (S_ISDIR (mode)) strcpy(__file_stat_type_buf, "[]"); else // box if ((mode & S_IXOTH)||(mode & S_IXGRP)||(mode & S_IXUSR)) strcpy(__file_stat_type_buf, "**"); else // * marks executables {}; return __file_stat_type_buf; } /*###########################################################################*/ /* actually this function is called only when 'R' key is pressed it calls vfu_read_files() and keeps selection and tag mark position update: now it is called and from vfu_shell when %r */ void vfu_rescan_files( int a_recursive ) { int z; int old_fli = FLI; int old_flp = FLP; VString keep = "1"; /* save selection, remember which files are selected */ VTrie savea; int savea_count = 0; if ( opt.keep_selection && sel_count > 0 ) { for ( z = 0; z < files_list_cnt ; z++) if ( files_list_get(z)->sel ) { savea[ files_list_get(z)->name() ] = keep; savea_count++; } } vfu_read_files( a_recursive ); /* restore selection */ if ( opt.keep_selection && savea_count > 0 ) { for ( z = 0; z < files_list_count() ; z++ ) if ( savea.exists( files_list_get(z)->name() ) ) files_list_get(z)->sel = 1; update_status(); } file_list_index.set_page( old_flp ); file_list_index.set_pos( old_fli ); vfu_nav_update_pos(); } /*---------------------------------------------------------------------------*/ void vfu_read_files( int a_recursive ) { say1( "Rescanning files... press ESC to interrupt" ); /* clear files list -- delete all found entries */ files_list_clear(); if ( archive_name != "" ) { ASSERT( work_mode == WM_ARCHIVE ); vfu_read_archive_files( a_recursive ); } else if ( external_panelizer != "" ) { ASSERT( work_mode == WM_NORMAL ); vfu_read_external_files(); opt.sort_order = L'U'; } else if ( list_panelizer.count() ) { ASSERT( work_mode == WM_NORMAL ); vfu_read_pszlist_files(); opt.sort_order = L'U'; } else { ASSERT( work_mode == WM_NORMAL ); vfu_read_local_files( a_recursive ); } /* update scroll parameters */ file_list_index.set_min_max( 0, files_list_cnt - 1 ); file_list_index.set_pagesize( con_max_y() - 7 ); file_list_index.set_pagestep( OPT_SCROLL_PAGESTEP(opt.scroll_pagestep) ); update_status(); vfu_nav_update_pos(); vfu_sort_files(); vfu_drop_all_views(); FLGO(0); /* this ignores the sort keep list position */ say1( "" ); say2( "" ); do_draw = 2; } /*---------------------------------------------------------------------------*/ int vfu_add_file( const char* fname, const struct stat *st, int is_link ) { VString ne = str_file_name_ext( fname ); if ( ne == "." || ne == ".." ) return 0; /* now try to hide `system/special' files */ if ( ! opt.show_hidden_files && ne[0] == '.' ) return 0; int is_dir = S_ISDIR( st->st_mode ); if ( ! is_dir ) /* mask is not allowed for dirs */ if ( vfu_fmask_match( ne ) ) return 0; /* doesn't match the mask */ TF *fi = new TF( fname, st, is_link ); files_list_add( fi ); /* get dir sizes for directories */ if ( work_mode == WM_NORMAL && is_dir ) { if ( is_link ) { /* symlinks */ fname_t t; expand_path( fi->full_name( 1 ), t ); str_fix_path( t ); fi->set_size( size_cache_get( t ) ); /* fi->set_size( size_cache_get( str_fix_path( expand_path( fi->full_name( 1 ) ) ) ) ); */ } else { /* not symlinks */ fi->set_size( size_cache_get( fi->full_name( 1 ) ) ); } } /* show progress ... */ if ( files_list_cnt % ( files_list_cnt > 1024 ? 373 : 73 ) == 0 ) { VString files_list_cnt_str = files_list_cnt; str_comma( files_list_cnt_str ); sprintf( ne, "Rescanning files... [%s] press ESC to interrupt", files_list_cnt_str.data() ); say1( ne ); } return 0; } /*---------------------------------------------------------------------------*/ int __vfu_ftw_add( const char* origin, const char* fname, const struct stat *st, int is_link, int flag ) { if ( vfu_break_op() ) return 1; if ( flag == FTWALK_DX ) return 0; /* exit directory */ VString str = fname; str_trim_left( str, str_len( origin ) ); return vfu_add_file( str, st, is_link ); } void vfu_read_local_files( int a_recursive ) { ftwalk( ".", __vfu_ftw_add, a_recursive ? -1 : 1 ); } /*---------------------------------------------------------------------------*/ void vfu_read_external_files() { fname_t fn_line; if ( external_panelizer == "" ) return; say1( "Rescanning files...(external panelizer)" ); FILE *f = popen( external_panelizer, "r" ); while( fgets( fn_line, MAX_PATH - 1, f ) ) { str_cut( fn_line, " \t\n\r" ); if ( access( fn_line, F_OK ) ) continue; struct stat st; stat( fn_line, &st ); say2( fn_line ); vfu_add_file( fn_line, &st, file_is_link( fn_line ) ); } pclose( f ); external_panelizer = ""; /* reset -- there's no reload on this */ } /*---------------------------------------------------------------------------*/ void vfu_read_pszlist_files() { int z; for ( z = 0; z < list_panelizer.count(); z++ ) { const char* pc = list_panelizer[z]; struct stat st; stat( pc, &st ); vfu_add_file( pc, &st, file_is_link( pc ) ); } list_panelizer.undef(); /* reset -- there's no reload on this */ } /*---------------------------------------------------------------------------*/ int vfu_fmask_match( const char* fname ) { int z; for(z = 0; z < files_mask_array.count(); z++) if ( FNMATCH(files_mask_array[z],fname) == 0) return 0; return 1; } /*###########################################################################*/ /* this compares Name20 and Name3 and returns second as smaller :) (or so) */ int namenumcmp( const char* s1, const char* s2 ) { VRegexp re1( "^(.*?)([0123456789]+)(\\.(.*))?$" ); VRegexp re2( "^(.*?)([0123456789]+)(\\.(.*))?$" ); if ( re1.m(s1) && re2.m(s2) ) { VString ss1; VString ss2; sprintf( ss1, "%020d", atoi(re1[2]) ); sprintf( ss2, "%020d", atoi(re2[2]) ); ss1 = re1[1] + ss1 + re1[3]; ss2 = re2[1] + ss2 + re2[3]; return pathcmp( ss1, ss2 ); } else { return pathcmp( s1, s2 ); } } /*---------------------------------------------------------------------------*/ int ficmp(int nf1, TF *f2) { TF *f1 = files_list[nf1]; int z = 0; /* keep dirs on top */ if( opt.sort_top_dirs ) { if ( f1->is_dir() && !f2->is_dir()) return -1; if (!f1->is_dir() && f2->is_dir()) return 1; } if (opt.sort_order == L'U') return 0; switch (opt.sort_order) { case L'N' : z = pathcmp(f1->name(), f2->name()); break; case L'M' : z = namenumcmp(f1->name(), f2->name()); break; case L'E' : z = pathcmp(f1->ext(), f2->ext()); break; case L'S' : z = (f2->size() < f1->size()) - (f2->size() > f1->size()); break; case L'T' : z = f1->st()->st_mtime - f2->st()->st_mtime; break; case L'H' : z = f1->st()->st_ctime - f2->st()->st_ctime; break; case L'C' : z = f1->st()->st_atime - f2->st()->st_atime; break; case L'A' : z = strcmp( f1->mode_str(), f2->mode_str() ); break; case L'O' : z = (f2->st()->st_uid > f1->st()->st_uid) - (f2->st()->st_uid < f1->st()->st_uid); if ( z == 0 ) z = (f2->st()->st_gid > f1->st()->st_gid) - (f2->st()->st_gid < f1->st()->st_gid); break; case L'G' : z = (f2->st()->st_gid > f1->st()->st_gid) - (f2->st()->st_gid < f1->st()->st_gid); if ( z == 0 ) z = (f2->st()->st_uid > f1->st()->st_uid) - (f2->st()->st_uid < f1->st()->st_uid); break; case L'Y' : z = strcmp( f1->type_str(), f2->type_str() ); break; default : ASSERT( ! "Non valid sort order (opt.sort_order)" ); break; } if ( z == 0 ) z = pathcmp( f1->name(), f2->name() ); ASSERT( opt.sort_direction == L'A' || opt.sort_direction == L'D' ); if (z) { z = (z > 0) - (z < 0); if (opt.sort_direction == L'D') return -z; } return z; } /*---------------------------------------------------------------------------*/ void __vfu_sort( int l, int r ) { int i; int j; int mid; TF *fi; TF *midf; i = l; j = r; mid = ((l+r) / 2); midf = files_list[mid]; do { while (ficmp(i,midf) == -1) i++; while (ficmp(j,midf) == 1) j--; if (i <= j) { fi = files_list[i]; files_list[i] = files_list[j]; files_list[j] = fi; i++; j--; } } while (i <= j); if (l < j) __vfu_sort(l, j); if (i < r) __vfu_sort(i, r); } /*---------------------------------------------------------------------------*/ void vfu_sort_files() { if ( ! files_list_cnt ) return; if ( opt.sort_order == L'U' ) return; VString str = files_list[FLI]->name(); VString ss = "Sorting... ["; str_add_ch( ss, opt.sort_order ); str_add_ch( ss, opt.sort_direction ); ss += "]"; say1( ss ); __vfu_sort( 0, files_list_cnt - 1 ); do_draw = 1; if ( str != "" ) { int z = 0; for (z = 0; z < files_list_cnt; z++) if ( str == files_list[z]->name() ) { FLGO(z); break; } } } /*---------------------------------------------------------------------------*/ void vfu_arrange_files() { int _ord; int _rev; mb.undef(); mb.push( L"N Name" ); mb.push( L"M Name### (RTFM)" ); mb.push( L"E Extension" ); mb.push( L"S Size" ); mb.push( L"T Modify Time" ); mb.push( L"H Change Time" ); mb.push( L"C Access Time" ); mb.push( L"D Attr/mode" ); mb.push( L"O Owner" ); mb.push( L"G Group" ); mb.push( L"Y Type (TP)" ); mb.push( L"U Unsorted" ); mb.push( L"---" ); mb.push( L"R Randomize" ); mb.push( L"V Move Entry" ); mb.push( L"---" ); mb.push( L"A Quick swap NAME/CHANGE" ); if ( vfu_menu_box( 50, 5, L"Arrange" ) == -1 ) return; _ord = menu_box_info.ec; if ( _ord == L'A' ) { opt.sort_direction = L'A'; opt.sort_order = opt.sort_order == L'N' ? L'H' : L'N'; vfu_sort_files(); say1( VString( "File list is now arranged by " ) + ( opt.sort_order == 'N' ? "NAME (ASCENDING)" : "CHANGE TIME (ASCENDING)" ) ); return; } if (_ord == L'V' ) { vfu_file_entry_move(); return; } if (_ord == L'R') { /* Fisher-Yates shuffle */ int i = files_list_count() - 1; while( i >= 0 ) { int j = rand() % ( i + 1 ); TF *tmp = files_list_get( i ); files_list_set( i, files_list_get(j) ); files_list_set( j, tmp ); i--; } do_draw = 2; return; } opt.sort_order = _ord; if( opt.sort_order == L'U' ) { say1( "Next directory rescan will be unsorted." ); return; } mb.undef(); mb.push( L"A Ascending"); mb.push( L"D Descending" ); if ( vfu_menu_box( 50, 5, L"Order" ) == -1 ) return; _rev = menu_box_info.ec; opt.sort_direction = _rev; vfu_sort_files(); say1(""); } /*###########################################################################*/ void vfu_file_entry_move() { VString t; t = t + "MOVE/REORDER File entry: " + files_list[FLI]->name(); say1( t ); say2( "Use Up/Down Arrows to reorder, ESC,ENTER when done." ); int key = 0; while( key != 13 && key != 27 ) // enter or esc { int old = FLI; switch(key) { case UKEY_UP : vfu_nav_up(); break; case UKEY_DOWN : vfu_nav_down(); break; } if ( old != FLI ) { TF* fi = files_list[old]; files_list[old] = files_list[FLI]; files_list[FLI] = fi; vfu_redraw(); } key = con_getch(); } say1( " " ); say2( " " ); } /*###########################################################################*/ /* eof vfufiles.cpp */ vfu-5.09/vfu/COPYING0000644000175000017500000004307014145574042012466 0ustar cadecade GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vfu-5.09/vfu/Makefile0000644000175000017500000000621714371545165013102 0ustar cadecade# use this to disable flto optimizations: # make NO_FLTO=1 # and this to enable verbose mode: # make V=1 STRIP?=strip PKG_CONFIG?=pkg-config PCRE08_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-8) PCRE08_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-8) PCRE32_CC?=$(shell $(PKG_CONFIG) --cflags libpcre2-32) PCRE32_LD?=$(shell $(PKG_CONFIG) --libs libpcre2-32) YASCREEN_CC?=$(shell $(PKG_CONFIG) --cflags yascreen) YASCREEN_LD?=$(shell $(PKG_CONFIG) --libs yascreen) NCURSES_CC?=$(shell $(PKG_CONFIG) --cflags ncursesw) NCURSES_LD?=$(shell $(PKG_CONFIG) --libs ncursesw) BINS:=vfu vfu.yas ifeq ($(YASCREEN_LD),) BINS:=$(filter-out vfu.yas,$(BINS)) endif ifeq ($(NCURSES_LD),) BINS:=$(filter-out vfu,$(BINS)) endif all: $(BINS) SRCS:=\ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp OBJS:=$(SRCS:.cpp=.o) DEPS:=$(OBJS:.o=.d) $(OBJS:.o=.y.d) ifndef NO_FLTO CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign CXXFLAGS+=-flto=auto else CXXFLAGS?=-O3 -fno-stack-protector -mno-stackrealign endif CXXFLAGS+=$(CCDEF) # some architectures do not have -mno-stackrealign HAVESREA:=$(shell if $(CXX) -mno-stackrealign -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) # old comiplers do not have -Wdate-time HAVEWDTI:=$(shell if $(CXX) -Wdate-time -xc -c /dev/null -o /dev/null >/dev/null 2>/dev/null;then echo yes;else echo no;fi) MYCXXFLAGS:=$(CPPFLAGS) $(CXXFLAGS) $(PCRE08_CC) $(PCRE32_CC) $(YASCREEN_CC) $(NCURSES_CC) -Wall -Wextra -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIE -I. -I../vstring -I../vslib ifeq ("$(HAVESREA)","no") MYCXXFLAGS:=$(filter-out -mno-stackrealign,$(MYCXXFLAGS)) endif ifeq ("$(HAVEWDTI)","no") MYCXXFLAGS:=$(filter-out -Wdate-time,$(MYCXXFLAGS)) endif MYLDFLAGS:=$(MYCXXFLAGS) $(LDFLAGS) -fPIE -pie MYLIBS:=$(LIBS) $(PCRE08_LD) $(PCRE32_LD) MYLDFLAGS+=$(LDDEF) ifeq ("$(V)","1") Q:= E:=@true else Q:=@ E:=@echo endif %.o: %.cpp $(E) DE $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_CURSES_ -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $< $(E) CXX $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_CURSES_ -c -o $@ $< %.y.o: %.cpp $(E) DE $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_YASCREEN_ -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $< $(E) CXX $@ $(Q)$(CXX) $(MYCXXFLAGS) -D_UNICON_USE_YASCREEN_ -c -o $@ $< VFUOBJ:=\ see.o \ vfu.o \ vfuarc.o \ vfucopy.o \ vfudir.o \ vfufiles.o \ vfumenu.o \ vfuopt.o \ vfusys.o \ vfutools.o \ vfuuti.o \ vfuview.o VFUYOBJ:=$(VFUOBJ:.o=.y.o) vfu: $(VFUOBJ) ../vstring/libvstring.a ../vslib/libvslib.a ../vslib/libvscon.a $(E) LD $@ $(Q)$(CXX) -o $@ $(MYLDFLAGS) $(VFUOBJ) $(MYLIBS) $(NCURSES_LD) -L../vstring -lvstring -L../vslib -lvslib -L../vslib -lvscon vfu.yas: $(VFUYOBJ) ../vstring/libvstring.a ../vslib/libvslib.a ../vslib/libvscony.a $(E) LD $@ $(Q)$(CXX) -o $@ $(MYLDFLAGS) $(VFUYOBJ) $(MYLIBS) $(YASCREEN_LD) -L../vstring -lvstring -L../vslib -lvslib -L../vslib -lvscony clean: $(E) CLEAN $(Q) rm -f *.a *.o *.d vfu vfu.yas re: $(Q)$(MAKE) --no-print-directory clean $(Q)$(MAKE) --no-print-directory -j -include $(DEPS) .PHONY: all clean re vfu-5.09/vfu/see.cpp0000644000175000017500000013666314372253241012723 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include #include #include #include #include "see.h" #ifndef ASSERT #define ASSERT assert #endif #define CHKPOS ASSERT( fpos >= 0 ); ASSERT( fpos <= fsize ) char HEXCHARS[] = "0123456789ABCDEF"; static char bg_xlat_table[2][64] = { "?", "abwgdevzijklmnoprstufhc`[]yxuqABWGDEVZIJKLMNOPRSTUFHC`[]YXUQ" }; static char bgw_xlat_table[2][64] = { "", "abwgdevzijklmnoprstufhc`[]yxuqABWGDEVZIJKLMNOPRSTUFHC`[]YXUQ" }; /*--------------------------------------------------------------------*/ SeeViewer::SeeViewer( SeeViewerOptions *a_opt ) { opt = a_opt; memset( &escape_keys, 0, sizeof( escape_keys ) ); f = NULL; line = last_line = 1; col = 0; end_reached = 0; fpos = fsize = 0; fname = ""; freezed = 0; do_draw = 0; if( opt->auto_size ) { opt->xmin = 1; opt->ymin = 1; opt->xmax = con_max_x(); opt->ymax = con_max_y(); } rows = opt->ymax - opt->ymin - (opt->status != 0) + 1; cols = opt->xmax - opt->xmin + 1; //FIXME: fix bsize! +32? buff = new char[ opt->bsize * 2 ]; /* extra for tab expansion */ help_str = "+-----------------------------------------------------------------------------+\n" "| SeeViewer v" SEE_VERSION " (c) Vladi Belperchinov-Shabanski |\n" "| |\n" "| Key TextMode HexMode Compatibility |\n" "| --------+--------------------+--------------------+------------------------ |\n" "| UpArrow | one line back | 16 bytes back | P = Home |\n" "| DnArrow | one line forward | 16 bytes forward | B = PgUp |\n" "| LtArrow | col -8 ( `.' `>' ) | 1 byte back | SPC = PgDn |\n" "| RtArrow | col +8 ( `,' `<' ) | 1 byte forward | ENTER = DnArrow |\n" "| Home | go to line 1 | go to byte 0 | |\n" "| End | go to last line | go to last byte | |\n" "| Ctrl+E | -'- (no line info) | go to last byte | l -- BG DOS xlate (slow)|\n" "| PgUp/Dn | one page back/forw | one page back/forw | L -- BG WIN xlate (slow)|\n" "| --------+--------------------+--------------------+------------------------ |\n" "| TAB -- switch between Text and Hex mode | ESC -- exit |\n" "| 1..0 -- switch to slot 1 .. slot 10 | Alt+X -- exit |\n" "| W w -- text wrap (TEXT) or wide screen (HEX) | - -- exit |\n" "| + -- goto line/pos (+line/pos, -line/pos) | d -- show dec.pos (HEX) |\n" "| I -- binary edit (HEX) | o -- show EOL's (TEXT) |\n" "| F S -- find string (F=no case, S=case sense) | r -- show ruler (TEXT) |\n" "| \\ / -- regexp search (\\=no case, /=case sense) | a -- filter backspaces |\n" "| E -- hex pattern search | t -- tab expansion |\n" "| N F3 -- find next, M -- find next backwards | g G -- grid (HEX) |\n" "+-----------------------------------------------------------------------------+"; } /*--------------------------------------------------------------------*/ SeeViewer::~SeeViewer() { close(); if ( buff ) delete [] buff; buff = NULL; } /*--------------------------------------------------------------------*/ /* add escape key which will cause run() exit */ void SeeViewer::escape_on( int key ) { int z = 0; while( z < MAX_ESCAPE_KEYS-1 ) { if (!escape_keys[z]) { escape_keys[z] = key; return; } z++; } } /*--------------------------------------------------------------------*/ int SeeViewer::open( const char* a_fname ) { if (!buff) return 1; if (f) fclose( f ); xlat = 0; f = NULL; line = 0; col = 0; last_line = 0; end_reached = 0; fpos = fsize = 0; fname = a_fname; freezed = 0; do_draw = 0; f = fopen( fname, "r" ); if (!f) return 2; fsize = file_size( f ); return 0; } /*--------------------------------------------------------------------*/ void SeeViewer::close() { if ( f ) fclose( f ); f = NULL; } /*--------------------------------------------------------------------*/ void SeeViewer::status( const char* format, ... ) { char buf[1024]; va_list vlist; va_start( vlist, format ); vsnprintf( buf, sizeof(buf), format, vlist ); va_end( vlist ); VString str; str = "| "; str += buf; if (str_len(str) >= cols) str_sleft( str, (cols-2) ); else str_pad( str, -(cols-2) ); str += "|"; vfu_con_out( opt->xmin, opt->ymax, str, opt->cs ); } /*--------------------------------------------------------------------*/ //FIXME: 2 versions not needed void SeeViewer::filter( char *s, int size ) { int z; for ( z = 0; z < size; z++ ) if ( (unsigned char)s[z] < 32 ) s[z] = '.'; if (xlat == 1) str_tr( s, bg_xlat_table[0], bg_xlat_table[1] ); else if (xlat == 2) str_tr( s, bgw_xlat_table[0], bgw_xlat_table[1] ); } void SeeViewer::filter( VString &s, int size ) { int z; for ( z = 0; z < size; z++ ) if ( (unsigned char)s[z] < 32 ) s[z] = '.'; if (xlat == 1) str_tr( s, bg_xlat_table[0], bg_xlat_table[1] ); else if (xlat == 2) str_tr( s, bgw_xlat_table[0], bgw_xlat_table[1] ); } /*--------------------------------------------------------------------*/ void SeeViewer::draw_hex() { CHKPOS; int rowsz; // row size int needw; // needed screen width con_max_x(); while(4) { rowsz = opt->hex_cols * 8; // row size needw = 10 + 2 + // offset rowsz * 3 + rowsz / 8 + 2 + // hexdump rowsz; // ascii if ( opt->hex_cols == 1 ) break; if ( needw > con_max_x() ) opt->hex_cols = 1; else break; } if ( needw > con_max_x() ) { status( "HEX mode not available for this screen width" ); return; } fseeko( f, fpos, SEEK_SET ); int rs = fread( buff, 1, rowsz * rows, f ); int x; int y; VString offset; VString hexdump; VString ascii; char t[256]; for( y = 0; y < rows; y++ ) { sprintf( t, opt->dec_pos ? "%10" PRId64 : "%010" PRIX64, (int64_t)(fpos + rowsz * y) ); offset = t; ascii = ""; hexdump = ""; for( x = 0; x < rowsz; x++ ) { if ( y * rowsz + x >= rs ) { break; } else { int c = (unsigned char)buff[y * rowsz + x]; sprintf( t, "%02X ", c ); hexdump += t; str_add_ch( ascii, c < 32 ? '.' : c ); if ( (x + 1) % 8 == 0 && x > 0 && x < rowsz - 1 ) hexdump += "- "; } } if ( hexdump == "" ) hexdump = "~"; filter( ascii, str_len(ascii) ); str_pad( hexdump, -( rowsz * 3 + (opt->hex_cols - 1) * 2 ) ); str_pad( ascii, -rowsz ); VString line = offset + "| " + hexdump + "|" + ascii; if ( hexdump[0] == '~' ) line = "~"; str_pad( line, -cols ); vfu_con_out( 1, y+1, line, (opt->grid && y % 2 == 0) ? opt->ch : opt->cn ); } status( "%3.0f%% | Pos. %4" FMT_OFF_T "d of %4" FMT_OFF_T "d | Alt+H Help | %s", fpos_percent(), fpos, fsize, fname.data() ); } /*--------------------------------------------------------------------*/ void SeeViewer::draw_txt() { CHKPOS; if ( line == -1 ) last_line = -1; off_t cpos = fpos; int z = 0; int y = 0; VString str; if( ftello(f) != cpos ) fseeko( f, cpos, SEEK_SET ); // FIXME: ? for( y = 0; y < rows; y++ ) { if ( cpos >= fsize ) { str = "~"; str_pad( str, -cols ); vfu_con_out( 1, y+1, str, (opt->grid && y%2==0) ? opt->ch : opt->cn ); continue; } z = read_text( cpos ); while ( z > 0 && ( buff[z-1] == '\r' || buff[z-1] == '\n' ) ) z--; buff[z] = 0; filter( buff, z ); WString www = buff; int show_lmark = 0; int show_rmark = 0; int show_eol = -1; if ( col > 0 ) { if (col >= z) { // buff[0] = 0; www = L""; show_lmark = 1; z = 0; } else { // str_trim_left( buff, col ); str_trim_left( www, col ); z -= col; } } if ( z > cols ) { // buff[cols] = 0; str_sleft( www, cols ); show_rmark = 1; } else { if ( opt->show_eol && !show_lmark ) show_eol = z+1; } //str_pad( buff, -cols ); str_pad( www, -cols ); vfu_con_out( 1, opt->ymin+y, VString( www ), (opt->grid && y%2==0) ? opt->ch : opt->cn); if ( re.ok() && re.m( buff ) ) vfu_con_out( re.sub_sp(0)+1, opt->ymin+y, re.sub(0), CONCOLOR( cBLACK, cWHITE ) ); if (show_lmark) vfu_con_out(1,opt->ymin+y,"<",chRED); if (show_rmark) vfu_con_out( opt->xmax, opt->ymin+y, ">", chRED ); if (show_eol != -1) vfu_con_out( show_eol, opt->ymin+y, "$", chGREEN ); } status( "%3.0f%% | Pos. %4" FMT_OFF_T "d | Line %4" FMT_OFF_T "d of %4" FMT_OFF_T "d%c|%4d+ | Alt+H Help | %s", fpos_percent(), fpos, line, last_line, end_reached?' ':'?', col+1, fname.data() ); } /*--------------------------------------------------------------------*/ void SeeViewer::draw() { (opt->hex_mode) ? draw_hex() : draw_txt(); if ( xlat == 1 ) vfu_con_out( opt->xmax - 7, opt->ymin, "BG XLAT", chRED ); if ( xlat == 2 ) vfu_con_out( opt->xmax - 10, opt->ymin, "BGWIN XLAT", chRED ); } /*--------------------------------------------------------------------*/ void SeeViewer::up_hex() { CHKPOS; fpos -= opt->hex_cols * 8; if ( fpos < 0 ) fpos = 0; line = -1; // hex moving invalidates text line position } void SeeViewer::up_txt() { CHKPOS; off_t cpos = fpos; if ( cpos == 0 ) return; int i = opt->wrap; if ( cpos - i < 0 ) i = cpos; cpos -= i; fseeko( f, cpos, SEEK_SET ); int res = fread( buff, 1, i, f ); ASSERT( res == i ); if ( buff[i-1] == '\n' ) i--; while( i > 0 && buff[i-1] != '\n' ) i--; if ( i > 0 ) { memmove( buff, buff + i, res - i ); // make buffer contain only last line buff[res - i] = 0; } fpos -= res - i; if ( fpos < 0 ) fpos = 0; if ( fpos == 0 ) line = 1; if ( line > 1 ) line--; } /*--------------------------------------------------------------------*/ void SeeViewer::down_hex() { CHKPOS; fpos += opt->hex_cols * 8; if ( fpos > fsize ) fpos = fsize; line = -1; // hex moving invalidates text line position } void SeeViewer::down_txt() { CHKPOS; int z = 0; if ( fpos == fsize ) return; if ( fseeko( f, fpos, SEEK_SET ) ) return; int res = fread( buff, 1, opt->wrap, f ); z = 0; while( z < res && buff[z] != '\n' ) z++; if (buff[z] == '\n') z++; buff[z] = 0; fpos += z; if ( line >= 0 ) line++; if ( line > last_line ) last_line = line; if ( fpos > fsize ) fpos = fsize; if ( fpos == fsize && last_line != -1 ) end_reached = 1; } /*--------------------------------------------------------------------*/ void SeeViewer::home() { fpos = 0; line = 1; } /*--------------------------------------------------------------------*/ void SeeViewer::end_hex() { fpos = fsize; end2(); } void SeeViewer::end_txt() { if (end_reached) { end2(); return; } while ( fpos < fsize ) { if ( con_kbhit() && con_getch() == 27 ) return; down(); if (line % 768 == 0) status( " Going down.... line: %6" FMT_OFF_T "d (%3.0f%%) press ESCAPE to cancel ", line, fpos_percent() ); } end2(); } /*--------------------------------------------------------------------*/ void SeeViewer::end2() { int z = 0; if (!end_reached) line = -1; else line = last_line; fpos = fsize; for ( z = 0; z < 2 * rows / 3; z++ ) up(); } /*--------------------------------------------------------------------*/ void SeeViewer::go_to() { VString sss; if(opt->hex_mode) { sprintf( sss, "x%" FMT_OFF_T "X", fpos ); status( " Goto pos: " ); WString www = sss; if ( ! TextInput( 15, opt->ymax, "", 20, 20, www ) ) { draw(); return; } sss = www; off_t new_pos = fpos; str_cut_spc( sss ); str_up( sss ); if ( sss[0] == '-' ) new_pos = (sss[1] == 'X') ? hex2long( (const char*)sss+2 ) : atol( (const char*)sss+1 ); else if ( sss[0] == '+' ) new_pos += (sss[1] == 'X') ? hex2long( (const char*)sss+2 ) : atol( (const char*)sss+1 ); else new_pos = (sss[0] == 'X') ? hex2long( (const char*)sss+1 ) : atol( (const char*)sss ); if ( new_pos >= 0 && new_pos < fsize ) fpos = new_pos; draw(); } else { if ( last_line == -1 ) { status( "Cannot determine line number..." ); return; } sprintf( sss, "%" FMT_OFF_T "d", line); status( " Goto line: " ); WString www = sss; if (!TextInput( 15, opt->ymax, "", 20, 20, www )) { draw(); return; } sss = www; off_t new_line = line; str_cut_spc( sss ); str_up( sss ); if ( sss[0] == '-' ) new_line -= atol( (const char*)sss+1 ); else if ( sss[0] == '+' ) new_line += atol( (const char*)sss+1 ); else new_line = atol( (const char*)sss ); if (new_line < 0) new_line = 0; if (last_line != -1 && end_reached && new_line > last_line) new_line = last_line; if (new_line == line) { draw(); return; } if (new_line > line) while( new_line != line && fpos < fsize ) { if ( con_kbhit() && con_getch() == 27 ) return; down(); if ( line % 768 == 0) status( " Going down.... line: %6" FMT_OFF_T "d -- %3.0f%% (press ESCAPE to cancel) ", line, fpos_percent() ); } else while( new_line != line && fpos > 0 ) { if ( con_kbhit() && con_getch() == 27 ) return; up(); if ( line % 768 == 0) status( " Going up.... line: %6" FMT_OFF_T "d -- %3.0f%% (press ESCAPE to cancel) ", line, fpos_percent() ); } draw(); } } /*--------------------------------------------------------------------*/ int SeeViewer::find_next_hex( int rev __attribute__((unused)) ) { //FIXME: implement!!! return 0; } int SeeViewer::find_next_txt( int rev ) { if ( ! re.ok() ) { re.comp( opt->last_search, opt->last_opt ); } if ( ! re.ok() ) { status( "No search pattern..." ); return 1; } off_t opos = fpos; VString msg; if ( ! rev ) down(); while(4) { rev ? up() : down(); if ( line % 768 == 0) status( "Searching.... line: %6" FMT_OFF_T "d -- %3.0f%% (press ESCAPE to cancel) ", line, fpos_percent() ); if ( re.m( buff ) ) { off_t spos = re.sub_sp( 0 ); if ( ! rev ) up(); draw(); spos += fpos; status( "Pattern `%s' found at pos: %" FMT_OFF_T "d (0x%" FMT_OFF_T "X)", opt->last_search, spos, spos ); break; } if ( (! rev && fpos == fsize) || ( rev && fpos == 0 ) ) { fpos = opos; fseeko( f, opos, SEEK_SET ); status( "Pattern `%s' not found...", opt->last_search ); break; } if ( con_kbhit() && con_getch() == 27 ) { fpos = opos; fseeko( f, opos, SEEK_SET ); status( "Search canceled..." ); break; } } return 0; } /*--------------------------------------------------------------------*/ int SeeViewer::find( const char* opts ) { VString sss; status( "Find %s: ", opts ); int ii = str_len(sss)+2; WString www = opt->last_search; if(!TextInput( opt->xmin+ii, opt->ymax, "", opt->xmax-ii-4, opt->xmax-ii-4, www )) { draw(); return 1; } sss = www; str_sleft( sss, MAX_SEARCH_LEN ); strcpy( opt->last_search, sss ); strcpy( opt->last_opt, opts ); re.comp( opt->last_search, opt->last_opt ); return find_next(); } /*--------------------------------------------------------------------*/ void SeeViewer::hex_edit() { if (!opt->hex_mode) { status( "HexEdit is available only in HEX mode :)" ); return; } int rowsz = opt->hex_cols * 8; // row size int in_text = 0; // if text is edited int editbs = rows * rowsz; unsigned char *editb = new unsigned char[editbs]; fseeko( f, fpos, SEEK_SET ); editbs = fread( editb, 1, editbs, f ); if ( editbs == 0 ) { delete [] editb; status( "Nothing to edit or read error..." ); return; } int epos = 0; int bytepos = 0; /* first or second byte part? :) */ status( "WARNING: HEX EDITING MODE! ENTER = SAVE, ESC = CANCEL, TAB = TOGGLE EDIT MODE !" ); con_cshow(); int key = 0; while(4) { if (in_text) con_xy( 13 + rowsz * 3 + (opt->hex_cols - 1) * 2 + 1 + epos % rowsz, 1 + epos / rowsz ); else con_xy( 13 + (epos % rowsz) * 3 + 2 * ( epos % rowsz / 8 ) + bytepos, 1 + epos / rowsz ); if ( key == 0 ) key = con_getch(); if ( key == 27 ) break; if ( key == 13 ) { /* will commit changes -- file should be reopened for RW */ fclose( f ); f = fopen( fname, "r+b" ); fseeko( f, fpos, SEEK_SET ); int r = fwrite( editb, 1, editbs, f ); fclose( f ); if ( r != editbs ) { status( "Write error (press a key)" ); con_beep(); con_getch(); } f = fopen( fname, "rb" ); break; } switch( key ) { case 9 : in_text = !in_text; break; case UKEY_RIGHT : if (bytepos == 0 && !in_text) bytepos = 1; else if (epos < editbs - 1) { epos++; if (!in_text) bytepos = 0; } break; case UKEY_LEFT : if (bytepos == 1 && !in_text ) bytepos = 0; else if (epos > 0) { epos--; if (!in_text) bytepos = 1; } break; case UKEY_DOWN : if ( epos + rowsz < editbs ) epos += rowsz; break; case UKEY_UP : if ( epos - rowsz >= 0 ) epos -= rowsz; break; case UKEY_PGUP : epos = epos % rowsz; break; case UKEY_PGDN : epos = editbs - editbs % rowsz + epos % rowsz; if (epos >= editbs) epos = editbs - 1; break; case UKEY_HOME : epos = epos - epos % rowsz; bytepos = 0; break; case UKEY_END : epos = epos + (rowsz - epos%rowsz - 1); if (epos >= editbs) epos = editbs - 1; break; } if ( !in_text && key > 0 && key < 255 && strchr( HEXCHARS, toupper(key) ) ) { int n = str_find( HEXCHARS, toupper(key) ); char tmp[2]; tmp[0] = HEXCHARS[n]; tmp[1] = 0; con_puts( tmp, chRED ); if (bytepos == 0) { editb[epos] = (n << 4) + ( editb[epos] & 0x0F ); } else { editb[epos] = (n ) + ( editb[epos] & 0xF0 ); } tmp[0] = editb[epos]; filter( tmp, 1 ); con_xy( 13 + rowsz * 3 + (opt->hex_cols - 1) * 2 + 1 + epos % rowsz, 1 + epos / rowsz); con_puts( tmp, chRED ); key = UKEY_RIGHT; } else if ( in_text && key >= 32 && key < 255 ) { char tmp[3]; tmp[0] = key; tmp[1] = 0; con_puts( tmp, chRED ); con_xy( 13 + (epos % rowsz) * 3 + 2 * ( epos % rowsz / 8 ), 1 + epos / rowsz ); sprintf( tmp, "%02X", key ); tmp[2] = 0; con_puts( tmp, chRED ); editb[epos] = key; key = UKEY_RIGHT; } else key = 0; } con_chide(); delete [] editb; draw(); } /*--------------------------------------------------------------------*/ void SeeViewer::help() { con_out( 1, 1, help_str ); do_draw = 1; con_getch(); } /*--------------------------------------------------------------------*/ int SeeViewer::run() { CHKPOS; if (!f) return 27; int ch = 0; draw(); while(ch != 27) { if ( do_draw ) { draw(); do_draw = 0; } ch = con_getch(); if( ch == 0 ) ch = UKEY_CTRL_L; if ( ch == 27 || ch == '-' || ch == 'q' || ch == UKEY_ALT_X || ch == UKEY_BACKSPACE ) return ch; int z = 0; while( escape_keys[z] ) if ( escape_keys[z++] == ch ) return ch; switch(ch) { case UKEY_F1 : case UKEY_ALT_H : case '?' : case 'h' : case 'H' : help(); break; case UKEY_UP : up(); draw(); break; case 13 : case UKEY_DOWN : down(); draw(); break; case 'b' : case 'B' : case UKEY_PGUP : for ( z = 0; z < rows; z++ ) up(); draw(); break; case ' ' : case UKEY_PGDN : for ( z = 0; z < rows; z++ ) down(); draw(); break; case 'p' : case 'P' : case UKEY_HOME : if (fpos == 0) col = 0; else home(); draw(); break; case UKEY_END : end(); draw(); break; case UKEY_CTRL_E : end2(); draw(); break; case UKEY_CTRL_L : if ( opt->auto_size ) { opt->xmin = 1; opt->ymin = 1; opt->xmax = con_max_x(); opt->ymax = con_max_y(); } rows = opt->ymax - opt->ymin - (opt->status != 0) + 1; cols = opt->xmax - opt->xmin + 1; con_cs(); draw(); break; case '>' : case '.' : case UKEY_RIGHT : if (opt->hex_mode) { if (fpos < fsize) fpos++; draw(); } else { if (col < opt->wrap-10) { col += (ch == '>') ? 1 : 8; draw(); } } break; case '<' : case ',' : case UKEY_LEFT : if (opt->hex_mode) { if (fpos > 0) fpos--; draw(); } else { if (col > 0) { col -= (ch=='<')?1:8; if (col < 0) col = 0; draw(); } } break; case 9 : opt->hex_mode = !opt->hex_mode; if (!opt->hex_mode) { fpos++; if ( fpos > fsize ) fpos = fsize; up(); } draw(); break; case 'g' : case 'G' : opt->grid = !opt->grid; draw(); break; case 'W' : case 'w' : if ( opt->hex_mode ) { opt->hex_cols++; draw(); } else { opt->wrap = (opt->wrap < opt->bsize)? opt->bsize : cols; draw(); status( (opt->wrap == cols)? " Wrap ON" : " Wrap OFF" ); } break; case 'l' : xlat = (xlat == 1) ? 0 : 1; draw(); break; case 'L' : xlat = (xlat == 2) ? 0 : 2; draw(); break; case 'f' : case 'F' : find( "i" ); break; case 's' : case 'S' : find( "f" ); break; case 'e' : case 'E' : find( "fh" ); break; case '/' : find( "r" ); break; //FIXME: remove 'r', use 'f' case '\\' : find( "ri" ); break; case UKEY_F3 : case 'n' : case 'N' : find_next( 0 ); break; case 'm' : case 'M' : find_next( 1 ); break; case '+' : go_to(); break; case 'd' : case 'D' : if (opt->hex_mode) { opt->dec_pos = !opt->dec_pos; draw(); } break; case 'o' : case 'O' : if (!opt->hex_mode) { opt->show_eol = !opt->show_eol; draw(); } break; case 'a' : case 'A' : if (!opt->hex_mode) { opt->handle_bs = !opt->handle_bs; draw(); status( opt->handle_bs? " BackSpace handling ON" : " BackSpace handling OFF" ); } break; case 't' : case 'T' : if (!opt->hex_mode) { opt->handle_tab = !opt->handle_tab; draw(); status( opt->handle_tab? " TAB expansion ON" : " TAB expansion OFF" ); } break; case 'r' : case 'R' : if (!opt->hex_mode) { int z = 0; VString ruler; while ( str_len(ruler) < opt->xmax ) { ruler += "|0-------"; z++; ruler += z % 10; } str_sleft( ruler, opt->xmax ); vfu_con_out( 1, 1, ruler, opt->ch ); } break; case 'i' : case 'I' : hex_edit(); break; } } return ch; /* 27 */ } /*--------------------------------------------------------------------*/ /* read ahead with tab and backspace expansion */ /* result goes into `buff', the margin is `wrap' */ int SeeViewer::read_text( off_t &cpos ) { buff[0] = 0; int z = 0; unsigned char ch; while( z < opt->wrap ) { if ( cpos >= fsize ) break; ch = fgetc( f ); cpos++; if( opt->handle_bs && ch == 8 ) { if( z > 0 ) z--; continue; } if( opt->handle_tab && ch == 9 ) { ASSERT( opt->tabsize > 0 ); int i = ( ( z / opt->tabsize ) + 1 ) * opt->tabsize - z; while( z < opt->wrap && i > 0 ) { buff[z] = ' '; z++; if( z == SEE_MAX_LINE_LENGTH ) break; i--; } continue; } buff[z] = ch; z++; if( z == SEE_MAX_LINE_LENGTH ) break; if ( ch == '\n' ) break; } buff[z] = 0; return z; } /**********************************************************************/ /**********************************************************************/ #define SEEDCOL (col - colpage + 1) /* screen column */ #define SEEDROW (sv.pos() - sv.page() + 1) /* screen row */ SeeEditor::SeeEditor( SeeEditorOptions *a_opt ) { opt = a_opt; memset( &escape_keys, 0, sizeof(escape_keys)); fname = ""; col = 0; colpage = 0; mod = 0; freezed = 0; if ( opt->auto_size ) { opt->xmin = 1; opt->ymin = 1; opt->xmax = con_max_x(); opt->ymax = con_max_y(); } rows = opt->ymax - opt->ymin - (opt->status != 0) + 1; cols = opt->xmax - opt->xmin + 1; sv.set_min_max( 0, va.count() - 1 ); sv.set_pagesize( rows ); sv.go( 0 ); help_str = "+-----------------------------------------------------------------------------+\n" "| SeeEditor v" SEE_VERSION " (c) Vladi Belperchinov-Shabanski |\n" "| ^ is Ctrl+key, @ is Alt+key, # is Shift+key |\n" "| |\n" "| Up_Arrow or ^P -- one line up ESC -- request exit |\n" "| Down_Arrow or ^N -- one line down (will prompt for save) |\n" "| Left_Arrow or ^B -- one char left ^W -- pipe cmd input as text |\n" "| Right_Arrow or ^F -- one char right @F -- find string (no case) |\n" "| Page_Up or ^U -- one page up @S -- find next (with case) |\n" "| Page_Down or ^V -- one page down @G -- find next |\n" "| Home or ^A -- goto beg. of line F3 -- find next |\n" "| End or ^E -- goto end of line ^L -- redraw screen |\n" "| Del or ^D -- del. char under cursor ~pattern is regexp search |\n" "| Backspace or ^H -- del. char to the left \\pattern is normal search |\n" "| ^K^U -- goto beg. of file pattern is same as \\pattern |\n" "| ^K^V -- goto end of file |\n" "| ^Y -- delete current line ^T -- toggle auto indent |\n" "| F1 or @H -- this help screen ^C -- quit without save NOW! |\n" "| F2 or ^K^D or ^S -- save file ^X -- Save All and Quit Now |\n" "| |\n" "| No UNDO! If you make a mistake -- quit the file without saving it! |\n" "| --------------------------------------------------------------------------- |\n" "| You can replace this editor with external one -- see VFU docs for details! |\n" "+-----------------------------------------------------------------------------+"; } /*--------------------------------------------------------------------*/ SeeEditor::~SeeEditor() { } /*--------------------------------------------------------------------*/ /* add escape key which will cause run() exit */ void SeeEditor::escape_on( int key ) { int z = 0; while( z < MAX_ESCAPE_KEYS-1 ) { if (!escape_keys[z]) { escape_keys[z] = key; return; } z++; } } /*--------------------------------------------------------------------*/ int SeeEditor::open( const char* a_fname ) { if ( va.count() || str_len( fname ) ) close(); fname = a_fname; remove_all(); insert_file( fname ); if (access( fname, F_OK )) { mod = 1; va.push( L"" ); /* hack if new file */ } sv.set_min_max( 0, va.count() - 1 ); sv.go( 0 ); col = colpage = 0; mod = 0; return 0; } /*--------------------------------------------------------------------*/ void SeeEditor::close() { if ( mod ) /* if modified */ if ( request_quit() ) return; /* request denied */ fname = ""; col = 0; colpage = 0; sv.go( 0 ); va.undef(); mod = 0; con_chide(); } /*--------------------------------------------------------------------*/ void SeeEditor::status( const char* format, ... ) { char buf[1024]; va_list vlist; va_start( vlist, format ); vsnprintf( buf, 1024, format, vlist ); va_end( vlist ); VString str; str = "| "; str += buf; if (str_len(str) >= cols) str_sleft( str, (cols-2) ); else str_pad( str, -(cols-2) ); str += "|"; vfu_con_out( opt->xmin, opt->ymax, str, opt->cs ); set_cursor(); } /*--------------------------------------------------------------------*/ int SeeEditor::expand_tabs( VString &str, VString &map ) { int res = 0; int i = 0; map = ""; str_pad( map, str_len( str ) ); while( ( i = str_find( str, '\t' ) ) > -1 ) { int j; ASSERT( opt->tabsize > 0 ); j = ( i / opt->tabsize + 1 ) * opt->tabsize; j = j - i; res += (j - 1); str_del( str, i, 1 ); while( j-- ) { str_ins_ch( str, i, ' ' ); str_ins_ch( map, i, '+' ); } str_del( map, i, 1 ); str_ins_ch( map, i, '*' ); } return res; } /*--------------------------------------------------------------------*/ int SeeEditor::real_col( int row ) { int c = col; if (row == -1) row = sv.pos(); VString str = va[row]; VString map; if ( expand_tabs( str, map ) ) { str_sleft( map, col ); c -= str_count( map, "+" ); } return c; } /*--------------------------------------------------------------------*/ void SeeEditor::set_cursor() { con_xy( SEEDCOL, SEEDROW ); } /*--------------------------------------------------------------------*/ void SeeEditor::draw_line( int n ) { if ( freezed ) return; ASSERT( sv.max() == va.count() - 1 ); if ( n > sv.max() ) { VString sss = "~"; str_pad( sss, - cols ); vfu_con_out( 1, ( n - sv.page() ) + 1, sss, opt->cn ); } else { VString map; VString str = va[n]; expand_tabs( str, map ); str_trim_left( str, colpage ); str_sleft( str, cols ); str_pad( str, - cols ); vfu_con_out( 1, ( n - sv.page() ) + 1, str, opt->cn ); } set_cursor(); } /*--------------------------------------------------------------------*/ void SeeEditor::draw( int from ) { if ( freezed ) return; int z; con_chide(); if ( from > -1 ) /* from == -1 to update status line only */ for( z = from; z < rows; z++ ) draw_line( sv.page() + z ); con_cshow(); status( "%s | %3.0f%% | Line:%5d of%5d |%4d+ %s | Alt+H Help | %s", mod?"MOD!":"----", (100.0*sv.pos())/(sv.max()?sv.max():1), sv.pos()+1, sv.max()+1, col+1, opt->insert?"INS":"ovr", fname.data() ); set_cursor(); } /*--------------------------------------------------------------------*/ int SeeEditor::save() { remove_trails(); if (va.fsave( fname )) { status( "Cannot save file: %s! ", fname.data() ); return 0; } else { status( "File saved ok" ); mod = 0; return 1; } } /*--------------------------------------------------------------------*/ int SeeEditor::request_quit() { if ( mod == 0 ) return 0; /* ok -- not modified */ while(4) { con_beep(); status( "File is modified! Press: Save, Quit, Cancel" ); con_chide(); int k = con_getch(); con_cshow(); if ( k == 'S' || k == 's' ) { if(!save()) { status( "Cannot save file! Press: Save, Quit, Cancel" ); continue; /* error saving file */ } else return 0; /* okay */ } if ( k == 'Q' || k == 'q' ) { mod = 0; /* considered unmodified at that point */ return 0; /* okay */ } if ( k == 27 ) { return 1; /* denied */ } } } /*--------------------------------------------------------------------*/ void SeeEditor::left() { if (col <= 0) return; VString str = va[sv.pos()]; VString map; if ( expand_tabs( str, map ) ) { col--; while (col > 0 && map[col] == '+') col--; } else col--; if (SEEDCOL < 1) colpage--; } /*--------------------------------------------------------------------*/ void SeeEditor::right() { VString str = va[sv.pos()]; VString map; if ( expand_tabs( str, map ) ) { col++; while (map[col] == '+') col++; } else col++; if (SEEDCOL > cols) colpage++; } /*--------------------------------------------------------------------*/ void SeeEditor::home() { col = colpage = 0; } /*--------------------------------------------------------------------*/ void SeeEditor::end() { remove_trails( sv.pos() ); VString str = va[sv.pos()]; VString map; expand_tabs( str, map ); col = str_len( str ); if (SEEDCOL > cols) { colpage = col - cols/2; if (colpage < 0) colpage = 0; draw(); } } /*--------------------------------------------------------------------*/ void SeeEditor::go_to() { } /*--------------------------------------------------------------------*/ void SeeEditor::kdel() { VString str = va[sv.pos()]; int c = real_col(); if (c >= str_len( str )) { if ( sv.pos() == sv.max() ) return; mod = 1; VString nstr = va[sv.pos()+1]; /* next string (below) */ if ( c > str_len( str ) ) str_pad( str, -c, ' ' ); /* the line is short -- pad with spaces */ str += nstr; va[ sv.pos() ] = str; va.del( sv.pos()+1 ); sv.set_min_max( 0, va.count() - 1 ); draw(); /* FIXME: from ROW to the end of the page */ } else { mod = 1; str_del( va[ sv.pos() ], c, 1 ); draw_line( sv.pos() ); } } /*--------------------------------------------------------------------*/ void SeeEditor::kbs() { VString str = va[sv.pos()]; int c = real_col(); if ( c > str_len( str ) ) { left(); return; } else if (c == 0) { if (sv.pos() == 0) return; up(); end(); kdel(); } else { left(); kdel(); } } /*--------------------------------------------------------------------*/ void SeeEditor::kenter() { mod = 1; if ( va.count() == 0 ) va.push( L"" ); int c = real_col(); WString str = va[sv.pos()]; WString nstr = str; str_sleft( str, c ); str_trim_left( nstr, c ); va.set( sv.pos(), str ); va.ins( sv.pos()+1, nstr ); sv.set_min_max( 0, va.count()-1 ); sv.down(); col = 0; /* !!! here should go the auto indenting... */ if ( opt->auto_indent && sv.pos() > 1) { str = va[sv.pos()-1]; int z = 0; int nc = 0; while( z < str_len(str) && (str[z] == L' ' || str[z] == L'\t') ) { if ( str[z] == L'\t' ) nc += opt->tabsize; else nc++; z++; } str = va[sv.pos()]; col = nc; while( nc-- ) str_ins_ch( str, 0, L' ' ); va.set( sv.pos(), str ); } if ( SEEDCOL > opt->xmax || SEEDCOL < 1 ) { colpage = col - cols/2; if (colpage < 0) colpage = 0; draw(); } else draw(); /* FIXME: from ROW to the end of the page */ } /*--------------------------------------------------------------------*/ void SeeEditor::kinsert( wchar_t wch ) { if ( UKEY_IS_WIDE_CTRL( wch ) ) return; if ( wch == 13 || wch == 10 ) { kenter(); return; } mod = 1; if ( va.count() == 0 ) va.push( L"" ); WString str = va[sv.pos()]; int c = real_col(); if (!opt->insert) str_del( str, c, 1 ); if ( str_len(str) < c ) str_pad( str, -c, L' ' ); str_ins_ch( str, c, wch ); va.set( sv.pos(), str ); right(); if ( SEEDCOL > opt->xmax || SEEDCOL < 1 ) { colpage = col - cols/2; if (colpage < 0) colpage = 0; draw(); } else draw(); /* FIXME: from ROW to the end of the page */ } /*--------------------------------------------------------------------*/ void SeeEditor::insert_file( const char* fn __attribute__((unused)) ) { mod = va.count(); /* FIXME: this should insert file in current position! */ va.fload( fname ); remove_trails(); sv.set_min_max( 0, va.count() - 1 ); } /*--------------------------------------------------------------------*/ void SeeEditor::remove_line( int n ) { if ( n == -1 ) n = sv.pos(); ASSERT( sv.max() == va.count() - 1 ); if ( n < 0 || n > sv.max() ) return; mod = 1; if ( n == sv.max() ) { if ( str_len( va[n] ) == 0 ) return; va.set( n, L"" ); } else { va.del( n ); sv.set_min_max( 0, va.count() - 1 ); sv.go( sv.pos() ); } draw(); } /*--------------------------------------------------------------------*/ void SeeEditor::remove_all() { while( va.count() ) { remove_line( 0 ); mod = 1; } } /*--------------------------------------------------------------------*/ void SeeEditor::remove_trails( int n ) /* remove trailing spaces/tabs */ { if ( n != -1 ) { ASSERT( sv.max() == va.count() - 1 ); if ( n < 0 || n > sv.max() ) return; WString str = va[n]; str_cut_right( str, L" \t\n\r" ); va.set( n, str ); } else for ( int z = 0; z < va.count(); z++ ) { WString str = va[z]; str_cut_right( str, L" \t\n\r" ); va.set( z, str ); } } /*--------------------------------------------------------------------*/ void SeeEditor::insert_pipe_cmd() { VString sss = "Command to pipe in: "; int ii = str_len( sss )+2; status( sss ); WString www = opt->last_pipe_cmd; if( ! TextInput( opt->xmin+ii, opt->ymax, "", opt->xmax-ii-4, opt->xmax-ii-4, www ) ) { draw(); return; } sss = www; str_sleft( sss, MAX_SEARCH_LEN ); strcpy( opt->last_pipe_cmd, sss ); FILE* f = popen( opt->last_pipe_cmd, "r" ); if ( !f ) { status( "Command execution failed..." ); status( opt->last_pipe_cmd ); return; } char ch; freezed = 1; while( (ch = fgetc( f ) ) != EOF ) kinsert( ch ); freezed = 0; pclose( f ); draw(); } /*--------------------------------------------------------------------*/ int SeeEditor::find_next() { if ( opt->last_search[0] == 0 ) { status( "No pattern" ); return 0; } int z; int pos = -1; for ( z = sv.pos() + 1; z <= sv.max(); z++ ) { WString str = va[z]; if ( opt->no_case ) str_up( str ); if ( opt->last_search[0] == '~' ) pos = str_find_regexp( str, opt->last_search + 1 ); else if ( str[0] == L'\\' ) pos = str_find( str, opt->last_search + 1 ); else pos = str_find( str, opt->last_search ); if ( pos != -1 ) break; } if ( pos != -1 ) { sv.go( z ); col = pos; if (SEEDCOL > cols) { colpage = col - cols/2; if (colpage < 0) colpage = 0; } do_draw = 1; return 1; } else { status( "Pattern not found" ); return 0; } } /*--------------------------------------------------------------------*/ int SeeEditor::find( int no_case ) { status( "Find %s: ", no_case?"(no case)":"(case sense)" ); WString www = opt->last_search; if( ! TextInput( opt->xmin, opt->ymax, "", opt->xmax-1, opt->xmax-1, www ) ) { draw(); return 1; } str_sleft( www, MAX_SEARCH_LEN ); VS_FN_STRCPY( opt->last_search, www ); opt->no_case = no_case; if ( opt->no_case ) str_up( opt->last_search ); return find_next(); } /*--------------------------------------------------------------------*/ void SeeEditor::help() { vfu_con_out( 1, 1, help_str ); do_draw = 1; con_getwch(); } /*--------------------------------------------------------------------*/ int SeeEditor::run() { con_cshow(); draw(); set_cursor(); wchar_t wch; int pend = 0; /* used for double key-strokes as ^K^x command */ while(4) { int ox = SEEDCOL; int oy = SEEDROW; int orp = sv.page(); int ocp = colpage; int oi = opt->insert; pend = 0; wch = con_getwch(); if( wch == 0 ) wch = UKEY_CTRL_L; if ( wch == UKEY_CTRL_C ) { mod = 0; /* it is `quit' i.e. no save so this should be ok */ return wch; } if ( wch == UKEY_CTRL_X ) { save(); return wch; } else if ( wch == 27 || wch == UKEY_ALT_X ) { if ( request_quit() == 0 ) return wch; else continue; } if ( wch == UKEY_CTRL_K ) { pend = wch; vfu_con_out( SEEDCOL, SEEDROW, "^K", opt->cs ); set_cursor(); wch = con_getwch(); draw_line( sv.pos() ); } switch( wch ) { case UKEY_CTRL_N : case UKEY_DOWN : down(); break; case UKEY_CTRL_P : case UKEY_UP : up(); break; case UKEY_CTRL_B : case UKEY_LEFT : left(); break; case UKEY_CTRL_F : case UKEY_RIGHT : right(); break; case UKEY_CTRL_U : if ( pend == UKEY_CTRL_K ) sv.home(); else sv.ppage(); break; case UKEY_PGUP : sv.ppage(); break; case UKEY_CTRL_V : if ( pend == UKEY_CTRL_K ) sv.end(); else sv.npage(); break; case UKEY_PGDN : sv.npage(); break; case UKEY_CTRL_A : case UKEY_HOME : home(); break; case UKEY_CTRL_E : case UKEY_END : end(); break; case UKEY_INS : opt->insert = !opt->insert; break; case UKEY_CTRL_Y : remove_line(); break; /* SeedKxxx functions are for KEYxxx handles */ case UKEY_ALT_H : case UKEY_F1 : help(); break; case UKEY_CTRL_S : case UKEY_F2 : save(); break; case UKEY_CTRL_D : if ( pend == UKEY_CTRL_K ) save(); else kdel(); break; case UKEY_ALT_F : find( 1 ); break; case UKEY_ALT_S : find( 0 ); break; case UKEY_ALT_G : case UKEY_F3 : find_next(); break; case UKEY_DEL : kdel(); break; case UKEY_CTRL_H : kbs(); break; case 10 : case 13 : kenter(); break; case UKEY_CTRL_L : if ( opt->auto_size ) { opt->xmin = 1; opt->ymin = 1; opt->xmax = con_max_x(); opt->ymax = con_max_y(); } rows = opt->ymax - opt->ymin - (opt->status != 0) + 1; cols = opt->xmax - opt->xmin + 1; sv.set_pagesize( rows ); con_cs(); draw(); break; case UKEY_CTRL_W : insert_pipe_cmd(); break; case UKEY_CTRL_T : opt->auto_indent = !opt->auto_indent; status( (opt->auto_indent) ? "AutoIndent ON" : "AutoIndent OFF" ); break; case UKEY_ALT_0 : case UKEY_ALT_1 : case UKEY_ALT_2 : case UKEY_ALT_3 : case UKEY_ALT_4 : case UKEY_ALT_5 : case UKEY_ALT_6 : case UKEY_ALT_7 : case UKEY_ALT_8 : case UKEY_ALT_9 : if ( wch == UKEY_ALT_0 ) wch = UKEY_ALT_9+1; return wch; case 27 : return wch; default : kinsert( wch ); break; } if ( do_draw || orp != sv.page() || ocp != colpage || oi != opt->insert ) { draw(); set_cursor(); do_draw = 0; } else if ( ox != SEEDCOL || oy != SEEDROW ) { draw( -1 ); /* just update status line */ set_cursor(); } } } /***eof****************************************************************/ vfu-5.09/vfu/vfuarc.h0000644000175000017500000000130714365574242013075 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUARC_H_ #define _VFUARC_H_ #include "vfu.h" void vfu_read_archive_files(); void vfu_browse_archive_file(); void vfu_extract_files( int one ); void vfu_user_external_archive_exec( VString &shell_line ); #endif /* _VFUARC_H_ */ /* eof vfuarc.h */ vfu-5.09/vfu/vfusys.h0000644000175000017500000000274314371545165013152 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUSYS_H_ #define _VFUSYS_H_ #include #include #include /* VFU specific defines */ #define MODE_OFF "----------" #define MODE_STRING "drwxrwxrwx" #define MODE_MASK "-?????????" #define MODE_WRITE_ON "??w???????" /* allow WRITE mask */ typedef char mode_str_t[12]; /* these functions set/get file's attributes/mode from/to string with this format: for Linux/UNIX: drwxrwxrwx for DOS: DV----RHSA it is supposed that all attribs count is 10 */ /* FIXME: these functions cannot handle symlink flag yet */ void file_get_mode_str( const mode_t tm, mode_str_t &mod_str ); int file_get_mode_str( const char *filename, mode_str_t &mod_str ); int file_set_mode_str( const char *filename, const mode_str_t mod_str ); int vfu_edit_attr( mode_str_t mod_str, int allow_masking = 1 ); /* FIXME: dir_exist should check if directory really */ #define dir_exist( d ) ( access( d, F_OK ) == 0 ) #define file_exist( d ) ( access( d, F_OK ) == 0 ) #endif /* _VFUSYS_H_ */ /* eof vfusys.h */ vfu-5.09/vfu/vfucopy.h0000644000175000017500000001125014444676602013301 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUCOPY_H_ #define _VFUCOPY_H_ #ifndef COPY_BUFFER_SIZE #define COPY_BUFFER_SIZE 1024*1024 /* 1M */ #endif /* copy modes **************************************************************/ #define CM_COPY 0 // copy #define CM_MOVE 1 // move #define CM_LINK 2 // symlink /* overwrite modes *********************************************************/ #define OM_ASK 0 /* ask before overwrite */ #define OM_ALWAYS 1 /* always overwrite */ #define OM_NEVER 2 /* never overwrite */ #define OM_IF_MTIME 3 /* if newer modify time*/ #define OM_ALWAYS_IF_MTIME 4 /* always if newer modify time*/ /* copy results ************************************************************/ #define CR_OK 0 #define CR_SKIP 200 #define CR_ABORT 255 /* run-time copy info structure ********************************************/ struct CopyInfo { CopyInfo() { reset(); }; void reset() { no_info = files_count = current_count = ok_count = skipped_count = no_free_check = over_mode = abort = 0; files_size = current_size = 0; elapsed_time = 0; }; int no_info; fsize_t files_size; long files_count; /* not used */ fsize_t current_size; long current_count; /* not used */ long elapsed_time; long ok_count; /* files copied ok */ long skipped_count; /* files skipped */ int no_free_check; /* if 1 -- don't check for destination free space */ int over_mode; /* what to do if dest exist? see OM_XXX defines */ int abort; /* if != 0 -- abort and return */ VString description; }; /*************************************************************************** ** ** utilities ** ****************************************************************************/ fsize_t device_free_space( const char *target ); /* user free space, NOT real! */ int file_is_same( const char *src, const char *dst ); int device_is_same( const char *src, const char *dst ); int fast_stat( const char* s, struct stat *st ); int over_if_exist( const char* src, const char *dst, CopyInfo* copy_info ); void show_copy_pos( fsize_t a_fc, /* file under copy current pos */ fsize_t a_fa, /* file under copy all size */ long a_et, /* elapsed time for current file copy */ CopyInfo *copy_info ); /* totals info */ int vfu_copy_mode( const char* src, const char* dst ); /*************************************************************************** ** ** COPY/MOVE/SYMLINK ** ****************************************************************************/ /* copy/move ***************************************************************/ int __vfu_file_copy( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_file_move( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_dir_copy( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_dir_move( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_link_copy( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_link_move( const char* src, const char* dst, CopyInfo* copy_info ); /* erase *******************************************************************/ int __vfu_dir_erase( const char* target, fsize_t* bytes_freed = NULL ); int __vfu_file_erase( const char* target, fsize_t* bytes_freed = NULL ); int __vfu_link_erase( const char* target, fsize_t* bytes_freed = NULL ); /* shells, call __*_*_*() above ********************************************/ int __vfu_copy( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_move( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_symlink( const char* src, const char* dst, CopyInfo* copy_info ); int __vfu_erase( const char* target, fsize_t* bytes_freed = NULL ); /* high-level interface functions ******************************************/ void vfu_copy_files( int a_one, int a_mode ); void vfu_erase_files( int a_one ); /*************************************************************************** ** ** CLIPBOARD ** ****************************************************************************/ void clipboard_add_del( int del = 0 ); void clipboard_paste( int mode ); void clipboard_clear(); void clipboard_view(); void clipboard_menu( wchar_t act ); #endif //_VFUCOPY_H_ vfu-5.09/vfu/vfutools.cpp0000644000175000017500000002426214365574242014030 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfumenu.h" #include "vfucopy.h" #include "vfuview.h" #include "vfufiles.h" #include "vfutools.h" /*------------------------------------------------------------------------*/ void __get_classify_str( const char *fname, wchar_t wch, char *tmp ) { tmp[0] = 0; strcpy( tmp, str_file_path( fname ) ); if ( wch == L'N') { strcat( tmp, str_file_name( fname ) ); if (strlen(fname) == strlen(tmp)) strcat( tmp, ".---" ); } else if (wch == L'E') { strcat( tmp, str_file_ext( fname ) ); if (strlen(tmp) == 0) strcat( tmp, "---" ); } else { strcat( tmp, str_file_name( fname ) ); str_sleft( tmp, wch - L'0' ); if (strlen(fname) == strlen(tmp)) strcat( tmp, ".---" ); } } void vfu_tool_classify() { fname_t tmp; if ( sel_count == 0 ) { say1( "Classify function works only on already selected files..." ); return; } mb.undef(); mb.push( L"N Name "); mb.push( L"E Ext" ); mb.push( L"1 First 1 letter " ); mb.push( L"2 First 2 letters" ); mb.push( L"3 First 3 letters" ); mb.push( L"4 First 4 letters" ); mb.push( L"5 First 5 letters" ); mb.push( L"6 First 6 letters" ); mb.push( L"7 First 7 letters" ); mb.push( L"8 First 8 letters" ); mb.push( L"9 First 9 letter" ); if ( vfu_menu_box( 32, 6, L"Classify files by") == -1 ) return; wchar_t wch = menu_box_info.ec; VArray fa; // files array int z; int i; for ( z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( fi->is_dir() ) continue; if ( ! fi->sel ) continue; __get_classify_str( fi->name(), wch, tmp ); int found = 0; for ( i = 0; i < fa.count(); i++ ) if ( pathcmp( tmp, fa[i] ) == 0 ) { found = 1; break; } if ( ! found ) fa.push( tmp ); } for ( i = 0; i < fa.count(); i++ ) { if( dir_exist( fa[i] ) ) continue; int res = make_path( fa[i] ); if ( res ) { fname_t t; sprintf( t, "Cannot create directory: %s, (press a key for cancel)", fa.get(i) ); say1( t ); say2errno(); con_getwch(); return; } } CopyInfo copy_info; copy_info.files_size = sel_size; copy_info.files_count = sel_count; for ( z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( fi->is_dir() ) continue; if ( fi->is_link() ) continue; if ( !fi->sel ) continue; __get_classify_str( fi->name(), wch, tmp ); strcat( tmp, "/" ); ASSERT( dir_exist( tmp ) ); strcat( tmp, fi->name_ext()); if ( __vfu_file_move( fi->name(), tmp, ©_info ) == 255 ) break; } vfu_read_files( 0 ); } /*------------------------------------------------------------------------*/ void vfu_tool_rename() { int z; int err; VString path; VString new_name; VString t; if ( files_list_count() < 1 ) { say1( "No files to rename... (Empty directory)" ); return; }; if ( sel_count < 1 ) { say1( "No files to rename... (You have to select required files)" ); return; }; mb.undef(); mb.push( L"1 README.TXT => readme.txt" ); mb.push( L"2 README.TXT => readme.TXT" ); mb.push( L"3 README.TXT => README.txt" ); mb.push( L"4 readme.txt => README.TXT" ); mb.push( L"5 readme.txt => README.txt" ); mb.push( L"6 readme.txt => readme.TXT" ); mb.push( L"_ Replace spaces with _" ); mb.push( L"Y Simplify name (RTFM)" ); mb.push( L"S Sequential rename" ); mb.push( L"T Prefix with current date+time" ); mb.push( L"D Prefix with current date" ); mb.push( L"W Swap SymLink with Original" ); mb.push( L"R Replace SymLink with Original" ); if (vfu_menu_box( 32, 6, L"Rename Tools" ) == -1) return; switch( menu_box_info.ec ) { case L'S' : vfu_tool_seq_rename(); break; case L'R' : vfu_tool_replace_sym_org(0); break; case L'W' : vfu_tool_replace_sym_org(1); break; case L'1' : case L'2' : case L'3' : case L'4' : case L'5' : case L'6' : case L'_' : case L'Y' : err = 0; for ( z = 0; z < files_list_count(); z++ ) { TF* fi = files_list_get(z); // if ( fi->is_dir() ) continue; // why not? ;) if ( !fi->sel ) continue; path = str_file_path( fi->name() ); new_name = ""; t = str_file_name( fi->name() ); if (menu_box_info.ec == L'1' || menu_box_info.ec == L'2') str_low( t ); if (menu_box_info.ec == L'4' || menu_box_info.ec == L'5') str_up( t ); new_name += t; t = str_file_ext( fi->name() ); if (menu_box_info.ec == L'1' || menu_box_info.ec == L'3') str_low( t ); if (menu_box_info.ec == L'4' || menu_box_info.ec == L'6') str_up( t ); if (strlen(t) > 0) { new_name += "."; new_name += t; } if (menu_box_info.ec == L'_') str_tr( new_name, " ", "_" ); if (menu_box_info.ec == L'Y') { str_replace( new_name, "%20", "_" ); str_tr( new_name, " `'&\"\\/,()!", "___________" ); str_tr( new_name, "", "aaeeeeaiiiuuoo" ); str_squeeze( new_name, "_" ); str_replace( new_name, "_-_", "-" ); } new_name = path + new_name; if ( !file_exist( new_name) ) { if (rename( fi->name(), new_name ) == 0) { fi->set_name( new_name ); do_draw = 2; } else err++; } else err++; } sprintf( t, "Rename complete (errors: %d)", err ); say1( t ); break; case L'T' : case L'D' : char time_prefix[32]; time_t timenow = time( NULL ); tm tmnow; localtime_r( &timenow, &tmnow ); if( strftime( time_prefix, sizeof(time_prefix) - 1, menu_box_info.ec == L'T' ? "%Y%m%d_%H%M%S_" : "%Y%m%d_", &tmnow ) <= 0 ) { t = "Rename error! Cannot constrict date/time prefix: "; t += strerror( errno ); say1( t ); break; } err = 0; for ( z = 0; z < files_list_count(); z++ ) { TF* fi = files_list_get(z); if ( !fi->sel ) continue; path = str_file_path( fi->name() ); new_name = path + time_prefix + str_file_name_ext( fi->name() ); if ( ! file_exist( new_name) ) { if ( rename( fi->name(), new_name ) == 0) { fi->set_name( new_name ); do_draw = 2; } else err++; } else err++; } sprintf( t, "Rename complete (errors: %d)", err ); say1( t ); break; } } /*------------------------------------------------------------------------*/ void vfu_tool_seq_rename() { VString prefix; VString suffix; VString s_digpos; VString s_start; if(!vfu_get_str( "Enter filename prefix: ", prefix, HID_SEQ_PREFIX )) return; if(!vfu_get_str( "Enter filename suffix: ", suffix, HID_SEQ_SUFFIX )) return; if(!vfu_get_str( "Enter digit places: (digits only) ", s_digpos, HID_SEQ_DIGPOS )) return; if(!vfu_get_str( "Enter start number: (digits only) ", s_start, HID_SEQ_START )) return; int digpos = atoi( s_digpos ); int start = atoi( s_start ); if (digpos < 1 || digpos > 20) digpos = 1; if (start < 0) start = 0; VString new_name; VString t; VString fmt; sprintf( fmt, "%%s%%0%dd%%s", digpos ); int err = 0; int z; for ( z = 0; z < files_list_count(); z++ ) { TF* fi = files_list_get(z); if ( fi->is_dir() ) continue; if ( !fi->sel ) continue; sprintf( new_name, fmt, prefix.data(), start, suffix.data() ); t = str_file_path( fi->name() ); /* FIXME: full name? */ new_name = t + new_name; if (access( new_name, F_OK ) == 0) { err++; continue; } if (rename( fi->name(), new_name )) { err++; continue; } fi->set_name( new_name ); do_draw = 2; start++; } sprintf( t, "Rename complete (errors: %d)", err ); say1( t ); } /*------------------------------------------------------------------------*/ // replaces symlink entry with original one: // rm symlink // mv original symlink void vfu_tool_replace_sym_org( int swap ) { int err = 0; int z; for ( z = 0; z < files_list_count(); z++ ) { TF* fi = files_list_get(z); if ( fi->is_dir() ) continue; if ( !fi->sel ) continue; if ( !fi->is_link() ) continue; VString sym = fi->full_name(); VString org = vfu_readlink( sym ); if (access( org, F_OK )) { err++; continue; } if (unlink( sym )) { err++; continue; } // FIXME: TODO: correct? if (rename( org, sym )) { err++; continue; } if (swap) { if(symlink( sym, org )) { say1( "error: cannot symlink " + sym + " -> " + org ); say2errno(); return; } } fi->update_stat(); do_draw = 2; } char t[256]; sprintf( t, "Replace complete (errors: %d)", err ); say1( t ); } /*------------------------------------------------------------------------*/ /* eof vfutools.cpp */ vfu-5.09/vfu/vfuopt.cpp0000644000175000017500000003533414371545165013473 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfu.h" #include "vfuopt.h" #include "vfuuti.h" #include "vfudir.h" #include "vfuview.h" #include "vfumenu.h" Options opt; const wchar_t *NOYES[] = { L" - ", L"YES", NULL }; const wchar_t *NOYESPRECOPY[] = { L" - ", L"YES", L"PRELIM", NULL }; const wchar_t *FTIMETYPE[] = { L"CHANGE", L"MODIFY", L"ACCESS", NULL }; const wchar_t *TAGMARKS[] = { L">>", L"=>", L"->", L"*>", NULL }; const wchar_t *SIIEC[] = { L"IEC", L"SI ", NULL }; const wchar_t *COMMA_TYPES[] = { L"'", L"`", L",", L" ", L"_", NULL }; const wchar_t *PAGE_STEPS[] = { L"1 LINE", L"30% PG", L"50% PG", NULL }; ToggleEntry Toggles[] = { // { "[a] 1234567890123456", &(opt.some) }, { L' ', L"--screen--", NULL, NULL }, { L'1', L"Show Mode field", &(opt.f_mode), NOYES }, { L'2', L"Show Owner field", &(opt.f_owner), NOYES }, { L'3', L"Show Group field", &(opt.f_group), NOYES }, { L'4', L"Show Time field", &(opt.f_time), NOYES }, { L'5', L"Show Size field", &(opt.f_size), NOYES }, { L'6', L"Show Type field", &(opt.f_type), NOYES }, { L'7', L" Time Type ", &(opt.f_time_type), FTIMETYPE }, { L'8', L"Long name view ", &(opt.long_name_view), NOYES }, { L' ', L"TagMark type ", &(opt.tag_mark_type), TAGMARKS }, { L' ', L"Use colors ", &(opt.use_colors), NOYES }, { L' ', L"Use /etc/DIR_COLORS", &(opt.use_dir_colors), NOYES }, { L' ', L"Lowercase extensions for configs", &(opt.lower_case_ext_config), NOYES }, { L'.', L"Show hidden files", &(opt.show_hidden_files), NOYES }, { L' ', L"--navigation--", NULL, NULL }, { L'i', L"Use internal viewer", &(opt.internal_browser), NOYES }, { L'I', L"Use internal editor", &(opt.internal_editor), NOYES }, { L' ', L"Use menu borders", &(opt.menu_borders), NOYES }, { L' ', L"--trees/dirs-- " , NULL, NULL }, { L' ', L"Compact DirTree", &(opt.tree_compact), NOYES }, { L' ', L"CDTree (cdpath) ", &(opt.tree_cd), NOYES }, { L' ', L"--troubleshooting--", NULL, NULL }, { L' ', L"Clear screen on shell", &(opt.shell_cls), NOYES }, { L' ', L"--compatibility--", NULL, NULL }, { L' ', L"Lynx style navigation", &(opt.lynx_navigation), NOYES }, { L' ', L"Mask auto expand", &(opt.mask_auto_expand), NOYES }, { L' ', L"Use CWD as target for COPY/MOVE", &(opt.default_copy_to_cwd), NOYES }, { L' ', L"--other--", NULL, NULL }, /* { ' 'L, "Can Zap/Erase READ-ONLY Files?!", &(opt.zap_ro), NOYES }, ? */ { L' ', L"Keep directories on top of the list", &(opt.sort_top_dirs), NOYES }, { L' ', L"Smart HOME/END keys (only Top Dirs mode)", &(opt.smart_home_end), NOYES }, { L' ', L"Case insensitive file/dir names matching", &(opt.no_case_glob), NOYES }, { L'b', L"Allow beep!", &(opt.allow_beep), NOYES }, { L's', L"Free space check on copy", &(opt.copy_free_space_check), NOYES }, { L' ', L"Auto mount on change dir", &(opt.auto_mount), NOYES}, { L' ', L"Preserve selection (after rescan)", &(opt.keep_selection), NOYES}, { L' ', L"Preserve ownership/mode on copy?", &(opt.copy_keep_mode), NOYES }, { L' ', L"Show user's free space", &(opt.show_user_free), NOYES }, { L' ', L"Calc/Show bytes on copy", &(opt.copy_calc_totals), NOYESPRECOPY }, { L' ', L"Calc/Show bytes freed on erase", &(opt.bytes_freed), NOYES }, { L' ', L"Prefer GiB in disk usage status", &(opt.use_gib_usage), NOYES }, { L' ', L"Show file/dir sizes in units", &(opt.use_si_sizes), SIIEC }, { L' ', L"1000s separator type", &(opt.comma_type), COMMA_TYPES }, { L' ', L"Scrolling page step", &(opt.scroll_pagestep), PAGE_STEPS }, { 0, L"---", NULL, NULL } }; /*---------------------------------------------------------------------------*/ time_t vfu_opt_time( const struct stat st ) { if (opt.f_time_type == 0) return st.st_ctime; else if (opt.f_time_type == 1) return st.st_mtime; else if (opt.f_time_type == 2) return st.st_atime; else return 0; } time_t vfu_opt_time( const struct stat* st ) { return vfu_opt_time( *st ); } time_t vfu_opt_time( time_t ctime, time_t mtime, time_t atime ) { if (opt.f_time_type == 0) return ctime; else if (opt.f_time_type == 1) return mtime; else if (opt.f_time_type == 2) return atime; else return 0; } /*---------------------------------------------------------------------------*/ void vfu_load_dir_colors() { #ifdef _TARGET_UNIX_ VArray va; va.fload( "/etc/DIR_COLORS" ); if (va.count() == 0) return; while( va.count() ) { VString str = va[0]; va.del( 0 ); int comment = str_find( str, '#' ); if ( comment != -1 ) str_sleft( str, comment ); str_cut( str, " \t" ); if ( str_len( str ) == 0 ) continue; if ( strncmp( str, "TERM " , 5 ) == 0 ) continue; if ( strncmp( str, "COLOR " , 6 ) == 0 ) continue; if ( strncmp( str, "OPTIONS ", 8 ) == 0 ) continue; int pos = -1; if ( str_find( str, "31" ) != -1 ) pos = cRED; else if ( str_find( str, "32" ) != -1 ) pos = cGREEN; else if ( str_find( str, "33" ) != -1 ) pos = cYELLOW; else if ( str_find( str, "34" ) != -1 ) pos = cBLUE; else if ( str_find( str, "35" ) != -1 ) pos = cMAGENTA; else if ( str_find( str, "36" ) != -1 ) pos = cCYAN; else if ( str_find( str, "37" ) != -1 ) pos = cWHITE; else {}; int spc = str_find( str, ' ' ); if ( spc == -1 || pos == -1 ) continue; str_sleft( str, spc ); str_replace( str, "DIR", ".[].<>" ); str_replace( str, "LINK", ".->" ); str_replace( str, "FIFO", ".()" ); str_replace( str, "SOCK", ".##" ); str_replace( str, "BLK", ".==" ); str_replace( str, "CHR", ".++" ); str_replace( str, "EXEC", ".**" ); str_ins( ext_colors[pos], 0, str ); }; for ( int z = 0; z < 16; z++ ) if( str_len( ext_colors[z] ) > 0 ) { ext_colors[z] += "."; if ( opt.lower_case_ext_config ) str_low( ext_colors[z] ); } #endif /* _TARGET_UNIX_ */ } /*---------------------------------------------------------------------------*/ int set_arr( const char *line, const char *keyword, VArray &target ) { VRegexp re("^[ \011]*([a-zA-Z0-9]+)[ \011]*=[ \011]*(.+)"); if ( ! re.m( line ) ) return 0; if ( str_low( re[1] ) != keyword ) return 0; target.push( re[2] ); return 1; } /*---------------------------------------------------------------------------*/ int set_str( const char *line, const char *keyword, VString &target ) { VRegexp re("^[ \011]*([a-zA-Z0-9]+)[ \011]*=[ \011]*(.+)"); if ( ! re.m( line ) ) return 0; if ( str_low( re[1] ) != keyword ) return 0; target = re[2]; return 1; } /*---------------------------------------------------------------------------*/ int set_int( const char *line, const char *keyword, int &target ) { VRegexp re("^[ \011]*([a-zA-Z0-9]+)[ \011]*=[ \011]*([0123456789]+)"); if ( ! re.m( line ) ) return 0; if ( str_low( re[1] ) != keyword ) return 0; target = atoi( re[2] ); return 1; } /*---------------------------------------------------------------------------*/ int set_splitter( const char *line, const char *keyword, VArray &splitter ) { VRegexp re("^[ \011]*([a-zA-Z0-9]+)[ \011]*=[ \011]*(.+)"); if ( ! re.m( line ) ) return 0; if ( str_low( re[1] ) != keyword ) return 0; splitter = str_split( PATH_DELIMITER, re[2] ); return 1; } /*---------------------------------------------------------------------------*/ int key_by_name( const char* key_name ) { if ( strcmp( key_name, "IC" ) == 0 ) return UKEY_INS; if ( strcmp( key_name, "INS" ) == 0 ) return UKEY_INS; if ( strcmp( key_name, "INSERT" ) == 0 ) return UKEY_INS; if ( strcmp( key_name, "ENTER" ) == 0 ) return UKEY_ENTER; if ( strcmp( key_name, "RETURN" ) == 0 ) return UKEY_ENTER; /* if (strcmp (key_name, "MENU" ) == 0) ux.key = - menucount; */ VRegexp reFKEYS( "[\\@\\^\\#]?[fF][01234567890]+" ); if ( reFKEYS.m( key_name ) ) { if ( toupper(key_name[0]) == 'F' ) return UKEY_F1 + atoi( key_name + 1 ) - 1; else if ( toupper(key_name[0]) == '@' ) return UKEY_ALT_F1 + atoi( key_name + 2 ) - 1; else if ( toupper(key_name[0]) == '^' ) return UKEY_CTRL_F1 + atoi( key_name + 2 ) - 1; else if ( toupper(key_name[0]) == '#' ) return UKEY_SH_F1 + atoi( key_name + 2 ) - 1; } return 0; } /*---------------------------------------------------------------------------*/ void vfu_settings_load( VArray* data = NULL ) { VString str; user_externals.undef(); history.undef(); see_filters.undef(); panelizers.undef(); archive_extensions.undef(); path_bookmarks.undef(); /***** LOAD DEFAULTS *******/ opt.reset(); opt.seo.handle_tab = 1; opt.sort_order = 'N'; opt.sort_direction = 'A'; opt.sort_top_dirs = 1; opt.f_size = 1; opt.f_time = 1; opt.f_mode = 1; opt.f_group = 1; opt.f_owner = 1; opt.f_type = 1; opt.f_time_type = 1; opt.long_name_view = 0; opt.tree_compact = 0; opt.tree_cd = 1; opt.show_hidden_files = 1; opt.allow_beep = 1; opt.use_colors = 1; opt.use_dir_colors = 1; opt.lower_case_ext_config = 1; opt.copy_free_space_check = 1; opt.copy_calc_totals = 1; opt.copy_keep_mode = 1; opt.tag_mark_type = 0; opt.internal_browser = 1; opt.internal_editor = 1; opt.mask_auto_expand = 1; opt.shell_cls = 1; opt.zap_ro = 0; opt.show_user_free = 1; opt.menu_borders = 0; opt.lynx_navigation = 0; opt.auto_mount = 1; opt.keep_selection = 1; opt.bytes_freed = 1; opt.use_si_sizes = 0; opt.smart_home_end = 1; opt.scroll_pagestep = 1; /***** LOAD DEFAULTS END ***/ Options tmp_opt; tmp_opt.reset(); if ( file_load_crc32( filename_opt, &tmp_opt, sizeof( tmp_opt ) ) == 0 ) memcpy( &opt, &tmp_opt, sizeof(Options) ); else say1( "warning: bad vfu.options file, loading defaults..." ); history.fload( filename_history ); file_list_index.set_pagestep( OPT_SCROLL_PAGESTEP(opt.scroll_pagestep) ); if ( getenv("EDITOR")) { shell_editor = getenv("EDITOR"); shell_editor += " %f"; } if ( getenv("PAGER") ) { shell_browser = getenv("PAGER"); shell_browser += " %f"; } else if ( getenv("BROWSER") ) { shell_browser = getenv("BROWSER"); shell_browser += " %f"; } else if ( getenv("VIEWER") ) { shell_browser = getenv("VIEWER"); shell_browser += " %f"; } VRegexp re_ux("^\\s*u?x\\s*=\\s*([^,]*)[ \011]*,\\s*([^, \011]*)\\s*,\\s*([^, \011]*)\\s*,(.*)$", "i"); VRegexp re_see( "^\\s*see\\s*=\\s*([^, \011]*)\\s*,(.*)$", "i" ); VRegexp re_pan( "^\\s*panelize\\s*=\\s*([^,]*)\\s*,(.*)$", "i" ); VArray conf_data; if ( data ) conf_data = *data; else conf_data.fload( filename_conf ); for( int c = 0; c < conf_data.count(); c++ ) { const char* line = conf_data.get( c ); if ( line[0] == '#' ) continue; if ( line[0] == ';' ) continue; if ( strlen( line ) == 0 ) continue; if(set_str( line, "browser", shell_browser))continue; if(set_str( line, "pager", shell_browser))continue; if(set_str( line, "viewer", shell_browser))continue; if(set_arr( line, "archive", archive_extensions))continue; if(set_str( line, "editor", shell_editor))continue; if(set_str( line, "diff", shell_diff))continue; if(set_arr( line, "bookmark", path_bookmarks))continue; /* if(set_str( line, "cblack" , ext_colors[0]); */ if(set_str( line, "cgreen" , ext_colors[cGREEN]))continue; if(set_str( line, "cred" , ext_colors[cRED]))continue; if(set_str( line, "ccyan" , ext_colors[cCYAN]))continue; if(set_str( line, "cwhite" , ext_colors[cWHITE]))continue; if(set_str( line, "cmagenta" , ext_colors[cMAGENTA]))continue; if(set_str( line, "cblue" , ext_colors[cBLUE]))continue; if(set_str( line, "cyellow" , ext_colors[cYELLOW]))continue; if(set_str( line, "chblack" , ext_colors[chBLACK]))continue; if(set_str( line, "chgreen" , ext_colors[chGREEN]))continue; if(set_str( line, "chred" , ext_colors[chRED]))continue; if(set_str( line, "chcyan" , ext_colors[chCYAN]))continue; if(set_str( line, "chwhite" , ext_colors[chWHITE]))continue; if(set_str( line, "chmagenta", ext_colors[chMAGENTA]))continue; if(set_str( line, "chblue" , ext_colors[chBLUE]))continue; if(set_str( line, "chyellow" , ext_colors[chYELLOW]))continue; if(set_splitter( line, "trimtree", trim_tree ))continue; /* following code is used to clean input data */ if( re_ux.m( line ) ) { str = ""; str = str + re_ux[1] + ","; /* get description */ str = str + re_ux[2] + ","; /* get key name */ VString t = re_ux[3]; /* get extensions */ if ( t != "*" && t[-1] != '.' ) t += "."; str = str + t + ","; str += re_ux[4]; /* get shell line */ user_externals.push( str ); continue; } else if( re_see.m( line ) ) { str = ""; see_filters.push( str + re_see[1] + "," + re_see[2] ); continue; } else if( re_pan.m( line ) ) { str = ""; panelizers.push( str + re_pan[1] + "," + re_pan[2] ); continue; } } if (opt.use_dir_colors) vfu_load_dir_colors(); } /*---------------------------------------------------------------------------*/ void vfu_settings_save() { file_save_crc32( filename_opt, &opt, sizeof(opt)); // file_save_crc32( filename_size_cache, &size_cache, sizeof(size_cache)); history.fsave( filename_history ); size_cache_save(); } /*---------------------------------------------------------------------------*/ void vfu_edit_conf_file() { if (opt.internal_editor) { opt.seo.cs = cINFO; SeeEditor editor( &opt.seo ); if( editor.open( filename_conf ) == 0 ) { int r = 1; while ( r ) { editor.run(); r = editor.request_quit(); } } else say1( "Error loading file..." ); editor.close(); } else { VString line = shell_editor; str_replace( line, "%f", shell_escape( filename_conf ) ); str_replace( line, "%F", shell_escape( filename_conf ) ); vfu_shell( line.data(), 0 ); } vfu_settings_save(); vfu_settings_load(); do_draw = 2; say1(""); say2(""); } /*---------------------------------------------------------------------------*/ void vfu_options() { say1("press SPACE to toggle, ENTER or ESC to exit"); say2(""); vfu_toggle_box( 30, 5, L"Options/Toggles (scroll down, SPACE selects)", Toggles ); vfu_settings_save(); vfu_settings_load(); vfu_drop_all_views(); vfu_redraw(); vfu_redraw_status(); say1(""); say2(""); } vfu-5.09/vfu/vfusys.cpp0000644000175000017500000001514714365574242013510 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfu.h" #include "vfusys.h" #include "vfuuti.h" #include "vfumenu.h" /*###########################################################################*/ void file_get_mode_str( const mode_t tm, mode_str_t &mod_str ) { strcpy( mod_str, MODE_OFF ); if (S_ISDIR(tm) ) mod_str[0] = 'd';else if (S_ISLNK(tm) ) mod_str[0] = 'l';else if (S_ISBLK(tm) ) mod_str[0] = 'b';else if (S_ISCHR(tm) ) mod_str[0] = 'c';else if (S_ISFIFO(tm)) mod_str[0] = 'f';else if (S_ISSOCK(tm)) mod_str[0] = 's'; if ((tm & S_IRUSR) != 0) mod_str[1] = 'r'; if ((tm & S_IWUSR) != 0) mod_str[2] = 'w'; if ((tm & S_IXUSR) != 0) mod_str[3] = 'x'; if ((tm & S_IRGRP) != 0) mod_str[4] = 'r'; if ((tm & S_IWGRP) != 0) mod_str[5] = 'w'; if ((tm & S_IXGRP) != 0) mod_str[6] = 'x'; if ((tm & S_IROTH) != 0) mod_str[7] = 'r'; if ((tm & S_IWOTH) != 0) mod_str[8] = 'w'; if ((tm & S_IXOTH) != 0) mod_str[9] = 'x'; if ((tm & S_ISUID) != 0) mod_str[3] = ((tm & S_IXUSR) != 0) ? 's' : 'S'; if ((tm & S_ISGID) != 0) mod_str[6] = ((tm & S_IXGRP) != 0) ? 's' : 'S'; if ((tm & S_ISVTX) != 0) mod_str[9] = ((tm & S_IXOTH) != 0) ? 't' : 'T'; } /*---------------------------------------------------------------------------*/ int file_get_mode_str( const char *filename, mode_str_t &mod_str ) { strcpy( mod_str, MODE_OFF ); struct stat st; if ( stat(filename, &st) ) return 1; file_get_mode_str(st.st_mode, mod_str); return 0; } /*---------------------------------------------------------------------------*/ int file_set_mode_str( const char *filename, const mode_str_t mod_str ) { mode_str_t old_mod_str; mode_str_t new_mod_str; mode_t new_mode = 0; strcpy( new_mod_str, mod_str ); if (strchr( new_mod_str, '?' )) { if (file_get_mode_str(filename, old_mod_str)) return 1; if (new_mod_str[1] == '?') new_mod_str[1] = old_mod_str[1]; if (new_mod_str[2] == '?') new_mod_str[2] = old_mod_str[2]; if (new_mod_str[3] == '?') new_mod_str[3] = old_mod_str[3]; if (new_mod_str[4] == '?') new_mod_str[4] = old_mod_str[4]; if (new_mod_str[5] == '?') new_mod_str[5] = old_mod_str[5]; if (new_mod_str[6] == '?') new_mod_str[6] = old_mod_str[6]; if (new_mod_str[7] == '?') new_mod_str[7] = old_mod_str[7]; if (new_mod_str[8] == '?') new_mod_str[8] = old_mod_str[8]; if (new_mod_str[9] == '?') new_mod_str[9] = old_mod_str[9]; } if (new_mod_str[1] == 'r') new_mode |= S_IRUSR; if (new_mod_str[2] == 'w') new_mode |= S_IWUSR; if (new_mod_str[3] == 'x') new_mode |= S_IXUSR; if (new_mod_str[4] == 'r') new_mode |= S_IRGRP; if (new_mod_str[5] == 'w') new_mode |= S_IWGRP; if (new_mod_str[6] == 'x') new_mode |= S_IXGRP; if (new_mod_str[7] == 'r') new_mode |= S_IROTH; if (new_mod_str[8] == 'w') new_mode |= S_IWOTH; if (new_mod_str[9] == 'x') new_mode |= S_IXOTH; if (new_mod_str[3] == 's') { new_mode |= S_ISUID; new_mode |= S_IXUSR; } if (new_mod_str[3] == 'S') new_mode |= S_ISUID; if (new_mod_str[6] == 's') { new_mode |= S_ISGID; new_mode |= S_IXGRP; } if (new_mod_str[6] == 'S') new_mode |= S_ISGID; if (new_mod_str[9] == 't') { new_mode |= S_ISVTX; new_mode |= S_IXOTH; } if (new_mod_str[9] == 'T') new_mode |= S_ISVTX; return ( chmod( filename, new_mode ) != 0 ); } /*---------------------------------------------------------------------------*/ int vfu_edit_attr( mode_str_t mod_str, int allow_masking ) { int mode_i[16]; if (allow_masking == 0) { /* "-rwxrwxrwx" */ for ( int z = 0; z < 16; z++ ) mode_i[z] = 1; mode_i[ 1] = (mod_str[1] != 'r'); mode_i[ 2] = (mod_str[2] != 'w'); mode_i[ 3] = (mod_str[3] != 'x'); mode_i[ 4] = (mod_str[4] != 'r'); mode_i[ 5] = (mod_str[5] != 'w'); mode_i[ 6] = (mod_str[6] != 'x'); mode_i[ 7] = (mod_str[7] != 'r'); mode_i[ 8] = (mod_str[8] != 'w'); mode_i[ 9] = (mod_str[9] != 'x'); mode_i[10] = !((mod_str[3] == 's') || (mod_str[3] == 'S')); mode_i[11] = !((mod_str[6] == 's') || (mod_str[6] == 'S')); mode_i[12] = !((mod_str[9] == 't') || (mod_str[9] == 'T')); if (mode_i[3]) mode_i[ 3] = !(mod_str[3] == 's'); if (mode_i[6]) mode_i[ 6] = !(mod_str[6] == 's'); if (mode_i[9]) mode_i[ 9] = !(mod_str[9] == 't'); } else { for ( int z = 0; z < 16; z++ ) mode_i[z] = 2; } const wchar_t* AONOFF1[] = { L"YES", L" - ", L" ? ", NULL }; const wchar_t* AONOFF2[] = { L"YES", L" - ", NULL }; #define AONOFF ( allow_masking ? AONOFF1 : AONOFF2 ) ToggleEntry mode_toggles[] = { { L' ', L"Read Owner", &mode_i[ 1], AONOFF }, { L' ', L"Write Owner", &mode_i[ 2], AONOFF }, { L' ', L"Exec/Srch Owner", &mode_i[ 3], AONOFF }, { L' ', L"Read Group", &mode_i[ 4], AONOFF }, { L' ', L"Write Group", &mode_i[ 5], AONOFF }, { L' ', L"Exec/Srch Group", &mode_i[ 6], AONOFF }, { L' ', L"Read Other", &mode_i[ 7], AONOFF }, { L' ', L"Write Other", &mode_i[ 8], AONOFF }, { L' ', L"Exec/Srch Other", &mode_i[ 9], AONOFF }, { L' ', L"Set user id", &mode_i[10], AONOFF }, { L' ', L"Set group id", &mode_i[11], AONOFF }, { L' ', L"Sticky Bit", &mode_i[12], AONOFF }, { 0, L"---", NULL, NULL }, }; if ( !vfu_toggle_box( 50, 5, L"Change file Mode", mode_toggles ) ) return 0; if (mode_i[ 1] < 2) mod_str[1] = mode_i[ 1] == 0 ? 'r' : '-'; if (mode_i[ 2] < 2) mod_str[2] = mode_i[ 2] == 0 ? 'w' : '-'; if (mode_i[ 3] < 2) mod_str[3] = mode_i[ 3] == 0 ? 'x' : '-'; if (mode_i[ 4] < 2) mod_str[4] = mode_i[ 4] == 0 ? 'r' : '-'; if (mode_i[ 5] < 2) mod_str[5] = mode_i[ 5] == 0 ? 'w' : '-'; if (mode_i[ 6] < 2) mod_str[6] = mode_i[ 6] == 0 ? 'x' : '-'; if (mode_i[ 7] < 2) mod_str[7] = mode_i[ 7] == 0 ? 'r' : '-'; if (mode_i[ 8] < 2) mod_str[8] = mode_i[ 8] == 0 ? 'w' : '-'; if (mode_i[ 9] < 2) mod_str[9] = mode_i[ 9] == 0 ? 'x' : '-'; if (mode_i[10] < 2) if (mode_i[10] == 0) mod_str[3] = (mode_i[ 3] == 0) ? 's' : 'S'; if (mode_i[11] < 2) if (mode_i[11] == 0) mod_str[6] = (mode_i[ 6] == 0) ? 's' : 'S'; if (mode_i[12] < 2) if (mode_i[12] == 0) mod_str[9] = (mode_i[ 9] == 0) ? 't' : 'T'; return 1; } /*---------------------------------------------------------------------------*/ /*###########################################################################*/ /* eof vfusys.cpp */ vfu-5.09/vfu/see.h0000644000175000017500000001352114365574242012364 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _SEE_H_ #define _SEE_H_ #include #include #include #include "vfuuti.h" #define SEE_VERSION "4.10" #define MAX_SEARCH_LEN 128 #define MAX_PIPE_LEN 128 #define MAX_ESCAPE_KEYS 64 #define SEE_MAX_LINE_LENGTH (32*1024) struct SeeViewerOptions { SeeViewerOptions() { reset(); } void reset() { auto_size = 1; xmin = xmax = ymin = ymax = -1; /* auto */ cn = CONCOLOR( cWHITE, cBLACK ); ch = CONCOLOR( cWHITE, cBLUE ); cs = CONCOLOR( cBLACK, cWHITE ); status = 1; bsize = SEE_MAX_LINE_LENGTH; tabsize = 8; wrap = bsize; handle_bs = 1; handle_tab = 1; hex_mode = 0; dec_pos = 0; grid = 0; show_eol = 0; last_search[0] = 0; last_opt[0] = 0; hex_cols = 2; } int auto_size; int xmin, xmax, ymin, ymax; int cn; /* color normal */ int ch; /* color hilite */ int cs; /* color status line */ int status; /* show status line */ int bsize; /* block size (i.e. the longest line ) */ int tabsize; /* tab size usually 8 */ int wrap; /* word wrap */ int handle_bs; /* backspaces */ int handle_tab; /* tabs */ int hex_mode; /* in hex mode */ int dec_pos; /* show decimal positions in hex mode */ int grid; /* show hilite grid */ int show_eol; /* show end of lines with $ */ int hex_cols; /* 8-bytes columns to show in hex mode */ char last_search[MAX_SEARCH_LEN+1]; char last_opt[32]; }; #define FMT_OFF_T "l" #ifdef _FILE_OFFSET_BITS # if _FILE_OFFSET_BITS == 64 # undef FMT_OFF_T # define FMT_OFF_T "ll" # elif _FILE_OFFSET_BITS > 64 # error "cannot represent _FILE_OFFSET_BITS >64" # endif #endif class SeeViewer { SeeViewerOptions* opt; int escape_keys[MAX_ESCAPE_KEYS]; VString help_str; VRegexp re; FILE* f; VString fname; off_t fpos; off_t fsize; off_t line; off_t last_line; int end_reached; int col; int rows; int cols; int xlat; int freezed; int do_draw; char* buff; public: SeeViewer( SeeViewerOptions *a_opt ); ~SeeViewer(); /* add escape key which will cause run() exit */ void escape_on( int key ); /* set help message */ void set_help_string( const char* s ) { if ( s ) help_str = s; }; int open( const char* a_fname ); void close(); void status( const char* format, ... ); void filter( char *s, int size ); void filter( VString &s, int size ); void draw_hex(); void draw_txt(); void draw(); void up_txt(); void up_hex(); void down_txt(); void down_hex(); void up() { opt->hex_mode ? up_hex() : up_txt(); } void down() { opt->hex_mode ? down_hex() : down_txt(); } void home(); void end_hex(); void end_txt(); void end() { opt->hex_mode ? end_hex() : end_txt(); } void end2(); void go_to(); int find_next_txt( int rev = 0 ); int find_next_hex( int rev = 0 ); int find_next( int rev = 0 ) { opt->hex_mode ? find_next_hex(rev) : find_next_txt(rev); return 0; } int find( const char* opts ); void hex_edit(); void help(); int run(); double fpos_percent() const { return 100 * (double(fpos) / (fsize?fsize:1)); } protected: int read_text( off_t &cpos ); }; /**********************************************************************/ struct SeeEditorOptions { SeeEditorOptions() { reset(); } void reset() { auto_size = 1; xmin = xmax = ymin = ymax = -1; /* auto */ cn = CONCOLOR( cWHITE, cBLACK ); ch = CONCOLOR( cWHITE, cBLUE ); cs = CONCOLOR( cBLACK, cWHITE ); status = 1; tabsize = 8; max_line = 4096; handle_tab = 1; auto_indent = 0; insert = 1; last_search[0] = 0; no_case = 1; last_pipe_cmd[0] = 0; } int auto_size; int xmin, xmax, ymin, ymax; int cn; /* color normal */ int ch; /* color hilite */ int cs; /* color status line */ int status; /* show status line */ int tabsize; /* tab size usually 8 */ int max_line; /* word wrap */ int handle_tab; /* tabs */ int auto_indent; int insert; /* if editor is in `insert' mode */ wchar_t last_search[MAX_SEARCH_LEN+1]; int no_case; char last_pipe_cmd[MAX_PIPE_LEN]; }; class SeeEditor { SeeEditorOptions* opt; int escape_keys[MAX_ESCAPE_KEYS]; VString help_str; VString fname; VRegexp re; int col; int colpage; ScrollPos sv; /* vertical scroller */ WArray va; /* string/text cluster */ int mod; /* modify flag */ int rows; int cols; int freezed; int do_draw; public: SeeEditor( SeeEditorOptions *a_opt ); ~SeeEditor(); /* add escape key which will cause run() exit */ void escape_on( int key ); /* set help message */ void set_help_string( const char* s ) { if ( s ) help_str = s; }; int open( const char* a_fname ); void close(); void status( const char* format, ... ); int expand_tabs( VString &str, VString &map ); int real_col( int row = -1 ); void set_cursor(); void draw_line( int n ); void draw( int from = 0 ); int save(); int request_quit(); void up() { sv.up(); }; void down() { sv.down(); }; void left(); void right(); void home(); void end(); void go_to(); void kdel(); void kbs(); void kenter(); void kinsert( wchar_t wch ); void insert_file( const char* fn ); void remove_line( int n = -1 ); void remove_all(); void remove_trails( int n = -1 ); /* remove trailing spaces/tabs */ void insert_pipe_cmd(); int find_next(); int find( int no_case ); void help(); int run(); }; #endif /* _SEE_H_ */ vfu-5.09/vfu/vfuuti.h0000644000175000017500000000460314371545165013132 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #ifndef _VFUUTI_H_ #define _VFUUTI_H_ #include "vfu.h" /*###########################################################################*/ fsize_t file_st_size( struct stat* st ); VString vfu_readlink( const char* fname ); /*###########################################################################*/ int vfu_update_shell_line( VString &a_line, VString &a_options ); int vfu_break_op(); /* return != 0 if ESC pressed, non blocking */ wchar_t vfu_ask( const wchar_t *prompt, const wchar_t *allowed ); /* blocking */ /* used before copy/move to calc estimated size */ fsize_t vfu_update_sel_size( int one ); VString& vfu_expand_mask( VString& mask ); char* time_str_compact( const time_t tim, char* buf ); void vfu_beep(); VString size_str_compact( const fsize_t siz ); char* vfu_str_comma( char* target ); VString& vfu_str_comma( VString& target ); VString vfu_str_comma( fsize_t size ); /*###########################################################################*/ void vfu_hist_add( int hist_id, const char* str ); const char* vfu_hist_get( int hist_id, int index = 0 ); char* vfu_hist_get( int hist_id, int index, char* str ); int vfu_hist_index( int hist_id, const char* value ); int vfu_hist_count( int hist_id ); void vfu_hist_remove( int hist_id, int index ); int vfu_hist_menu( int x, int y, const wchar_t* title, int hist_id ); void vfu_get_str_history( int key, VString &s, int &pos ); /* internal! */ int vfu_get_str( const char *prompt, VString& target, int hist_id, int x = -1, int y = -1 ); const char* vfu_temp(); const char* vfu_temp_dir(); /*###########################################################################*/ void vfu_con_out( int x, int y, const char *s ); void vfu_con_out( int x, int y, const char *s, int attr ); void vfu_con_out( int x, int y, const wchar_t *s ); void vfu_con_out( int x, int y, const wchar_t *s, int attr ); /*###########################################################################*/ #endif//_VFUUTI_H_ vfu-5.09/vfu/vfuarc.cpp0000644000175000017500000001631214372237730013426 0ustar cadecade/**************************************************************************** # # Copyright (c) 1996-2023 Vladi Belperchinov-Shabanski "Cade" # https://cade.noxrun.com/ # https://cade.noxrun.com/projects/vfu https://github.com/cade-vs/vfu # # SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! # ****************************************************************************/ #include "vfuarc.h" #include "vfuuti.h" #include "vfuopt.h" #include "vfudir.h" #include "vfucopy.h" #include "vfufiles.h" /*---------------------------------------------------------------------------*/ VString find_rx_auto() { if( file_exist( "/usr/libexec/vfu/rx_auto" ) ) return VString( "/usr/libexec/vfu/rx_auto" ); if( file_exist( "/usr/lib/vfu/rx_auto" ) ) return VString( "/usr/lib/vfu/rx_auto" ); return VString( "rx_auto" ); } void vfu_read_archive_files( int a_recursive ) { char line[2048] = ""; struct stat st; memset( &st, 0, sizeof(st)); if ( a_recursive ) archive_path = ""; /* cannot have path when recursing archive */ VString an = archive_name; VString ap = archive_path; shell_escape( an ); shell_escape( ap ); VString s; s = find_rx_auto() + " "; s += ( a_recursive ) ? "v" : "l"; s += " " + an + " " + ap + " 2> /dev/null"; /* NOTE: calling rx_* should be safe and result should be proper. all bugs must be traced outside VFU ... */ FILE *f = popen( s, "r" ); s = ""; if ( !f ) { say2( "Archive cannot be recognized or cannot be read" ); } else while( fgets(line, 2048-1, f) ) { str_cut( line, "\n\r" ); if ( strncmp( line, "NAME:", 5 ) == 0 ) { s = line+5; if ( str_get_ch( s, -1 ) == '/' ) /* i.e. last char */ { str_trim_right( s, 1 ); st.st_mode |= S_IFDIR; } } else if ( strncmp( line, "MODE:", 5 ) == 0 ) { VString ms = line + 5; if( ms[0] == 'd' ) st.st_mode |= S_IFDIR; if (ms[1] == 'r') st.st_mode |= S_IRUSR; if (ms[2] == 'w') st.st_mode |= S_IWUSR; if (ms[3] == 'x') st.st_mode |= S_IXUSR; if (ms[4] == 'r') st.st_mode |= S_IRGRP; if (ms[5] == 'w') st.st_mode |= S_IWGRP; if (ms[6] == 'x') st.st_mode |= S_IXGRP; if (ms[7] == 'r') st.st_mode |= S_IROTH; if (ms[8] == 'w') st.st_mode |= S_IWOTH; if (ms[9] == 'x') st.st_mode |= S_IXOTH; if (ms[3] == 's') { st.st_mode |= S_ISUID; st.st_mode |= S_IXUSR; } if (ms[3] == 'S') st.st_mode |= S_ISUID; if (ms[6] == 's') { st.st_mode |= S_ISGID; st.st_mode |= S_IXGRP; } if (ms[6] == 'S') st.st_mode |= S_ISGID; if (ms[9] == 't') { st.st_mode |= S_ISVTX; st.st_mode |= S_IXOTH; } if (ms[9] == 'T') st.st_mode |= S_ISVTX; } else if ( strncmp( line, "SIZE:", 5 ) == 0 ) { st.st_size = atoi( line+5 ); } else if ( strncmp( line, "TIME:", 5 ) == 0 ) { struct tm t; memset( &t, 0, sizeof(t) ); VRegexp r( "^(....)(..)(..)(..)(..)(..)?" ); r.m( line + 5 ); t.tm_year = atoi( r[1] ) - 1900; t.tm_mon = atoi( r[2] ); t.tm_mday = atoi( r[3] ); t.tm_hour = atoi( r[4] ); t.tm_min = atoi( r[5] ); t.tm_sec = atoi( r[6] ); st.st_mtime = st.st_ctime = st.st_atime = mktime( &t ); } else if ( line[0] == 0 ) { if ( str_len( s ) > 0 ) vfu_add_file( s, &st, 0 ); /* FIXME: there's no links for now */ s = ""; memset( &st, 0, sizeof(st) ); } } pclose( f ); } /*---------------------------------------------------------------------------*/ void vfu_browse_archive_file() { VString tmpdir = vfu_temp_dir(); if( tmpdir == "" || chdir( tmpdir ) ) { say1( "error: cannot create temp directory: " + tmpdir ); say2errno(); return; } VString fn = files_list_get(FLI)->full_name(); VString wp = work_path; VString an = archive_name; shell_escape( wp ); shell_escape( an ); shell_escape( fn ); VString s; s = find_rx_auto() + " x " + wp + an + " " + fn + " 2> /dev/null"; vfu_shell( s, "" ); if( chdir( tmpdir ) ) /* FIXME: a little hack -- vfu_shell() changes current path */ { say1( "error: cannot chdir to temp directory: " + tmpdir ); say2errno(); return; } vfu_browse( fn ); if(chdir( work_path )) { say1( "error: cannot chdir back to work directory: " + work_path ); say2errno(); return; } __vfu_dir_erase( tmpdir ); say1( "" ); } /*---------------------------------------------------------------------------*/ void vfu_user_external_archive_exec( VString &shell_line ) { VString tmpdir = vfu_temp_dir(); if( tmpdir == "" || chdir( tmpdir ) ) { say1( "error: cannot create temp directory: " + tmpdir ); say2errno(); return; } VString fn = files_list_get(FLI)->full_name(); VString wp = work_path; VString an = archive_name; shell_escape( wp ); shell_escape( an ); shell_escape( fn ); VString s; s = find_rx_auto() + " x " + wp + an + " " + fn + " 2> /dev/null"; vfu_shell( s, "" ); if(chdir( tmpdir )) /* FIXME: a little hack -- vfu_shell() changes current path */ { say1( "error: cannot chdir to temp directory: " + tmpdir ); say2errno(); return; } str_replace( shell_line, "%f", shell_escape( fn ) ); str_replace( shell_line, "%F", shell_escape( fn ) ); vfu_shell( shell_line, "" ); if(chdir( work_path )) { say1( "error: cannot chdir back to work directory: " + work_path ); say2errno(); return; } __vfu_dir_erase( tmpdir ); say1( "" ); } /*---------------------------------------------------------------------------*/ void vfu_extract_files( int one ) { if ( sel_count == 0 && one == 0 ) one = 1; fname_t t; VString target; if ( one == 0 ) sprintf( t, "EXTRACT SELECTION to: " ); else snprintf( t, sizeof(t), "EXTRACT `%s' to:", files_list_get(FLI)->full_name() ); target = opt.last_copy_path[ CM_COPY ]; if ( !vfu_get_dir_name( t, target ) ) return; strcpy( opt.last_copy_path[ CM_COPY ], target ); VArray va; int z; for( z = 0; z < files_list_count(); z++ ) if ((files_list_get(z)->sel && one == 0) || (z == FLI && one != 0)) va.push( files_list_get(z)->full_name() ); if (chdir(target)) { snprintf( t, sizeof(t), "Cannot chdir to: %s", target.data() ); say1( t ); say2errno(); return; } VString tmpfile = vfu_temp(); if (va.fsave( tmpfile )) { snprintf( t, sizeof(t), "Error writing list file: %s", tmpfile.data() ); say1( t ); return; } chmod( tmpfile, S_IRUSR|S_IWUSR ); VString wp = work_path; VString an = archive_name; shell_escape( wp ); shell_escape( an ); VString s; s = find_rx_auto() + " x " + wp + an + " @" + tmpfile + " 2> /dev/null"; vfu_shell( s, "" ); if (unlink( tmpfile )) { /* snprintf( t, sizeof(t), "Cannot unlink/erase temp file: %s", tmpfile ); say2( t ); */ } if (chdir(work_path)) { snprintf( t, sizeof(t), "Cannot chdir back to to: %s", work_path.data() ); say1( t ); say2errno(); return; } say1( "EXTRACT ok." ); } /*---------------------------------------------------------------------------*/ /* eof vfuarc.cpp */ vfu-5.09/vfu/AUTHORS0000644000175000017500000000021414145574042012474 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-5.09/CONFIG0000644000175000017500000000367014372204606011523 0ustar cadecade VFU CONFIGURATION, OPTIONS AND CACHE FILES LOCATIONS ------------------------------------------------------------------------ By default VFU searches these locations for `vfu.conf' config file: 1. personal vfu.conf ( explained below ) 2. /etc/ 3. /usr/local/etc/ 4. /usr/local/ The personal vfu.conf is located in `$HOME/.vfu/' directory and it is created if not exist. The same directory is used for keeping options and cache files ( directory tree, history, options, etc. ) Well, More than 1/3 of all files in my home directory are various configuration ( dotfiles -> `.name' ) files for many applications ( and directories as well ). I think that if all these files/dirs are moved to `$HOME/etc/' or `$HOME/.conf/' or similar directory, the home directory will look a lot more clean. I know that dotfiles are considered `hidden' but I'd like to see all files w/o such restrictions. I also know that keeping configs in `$HOME/.name' files is usual since the beginning, but it doesn't help. Simple solution to this problem is to check for exported environment variable called `RC_PREFIX' and if found to check for config files in `$HOME/$RC_PREFIX/.name' ( where `.name' is the config file or dir ). Is $RC_PREFIX is not defined all configs will go as usual in the `$HOME/.name'. VFU works in this way and I have got always $RC_PREFIX defined to `etc' ( i.e. vfu.conf goes in `$HOME/etc/.vfu/' ). NOTE: The text above is probably more than 20 years old :) I don't know for wide-spread idea to keep all dot files separate but lately $HOME/.config started to gather more and more configuration files, so I think that is a safe choice :) Vladi Belperchinov-Shabanski https://cade.noxrun.com/projects/vfu ------------------------------------------------------------------------ vfu-5.09/build.netbsd0000755000175000017500000000115514145574023013133 0ustar cadecade#!/bin/sh # # note: `-D__NetBSD__' is removed `CCDEF' -- it is defined by default... echo "Compiling VSLIB..." cd vslib make CCDEF="-I${LOCALBASE}/include" LDDEF="-Wl,-R${LOCALBASE}/lib -L${LOCALBASE}/lib" if [ -e libvslib.a ]; then echo "VSLIB compiled ok." else echo "VSLIB compilation failed..." fi cd .. echo "Compiling VFU..." cd vfu make CCDEF="-I${LOCALBASE}/include -DFILENAME_CONF_GLOBAL0=\\\"${PREFIX}/etc/\\\" -DFILENAME_CONF_GLOBAL1=\\\"${PREFIX}/\\\" " LDDEF="-Wl,-R${LOCALBASE}/lib -L${LOCALBASE}/lib" if [ -e vfu ]; then echo "VFU compiled ok." else echo "VFU compilation failed..." fi cd .. vfu-5.09/TODO0000644000175000017500000000072114145574023011316 0ustar cadecade IMPORTANT IMMEDIATE: -- allow reedit when copy to new dir failed to create (offers only CANCEL/CONTINUE now) MOST IMPORTANT (LONG TERM?) TODO NOTES: -- fix makemake.pl to handle install/uninstall targets -- write rx_* tools for different archives/storages (HELP WANTED!) -- mount in JumpToMountpoints menu -- build tree from local file systems only (option) -- check if source exist when move/copy/symlink (warning really) -- save/load selection with key vfu-5.09/COPYING0000644000175000017500000004307014145574023011665 0ustar cadecade GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vfu-5.09/vfu.lsm0000644000175000017500000000236614444676573012171 0ustar cadecadeBegin3 Title: VFU File Manager Version: 5.09 Entered-date: 22JUN2023 Description: VFU is console (text mode) file manager for UNIX. It has all usual file oprations plus more: - extended completion and wildcard expansion - directory tree with sizes (incl. sizes cache) - file-type colorization (extension and type) - archives support (TAR, TGZ, BZ2, and many more) - simple FTP support through archive-like interface - internal text/hex file viewer and editor (optional) - extensive user-defined external support/utils - regular expressions selection and search - multiple file masks - one-key commands - session saving - more... Keywords: console file manager unix portable Author: cade@noxrun.com (Vladi Belperchinov-Shabanski) Maintained-by: cade@bis.bg (Vladi Belperchinov-Shabanski) Primary-site: http://cade.noxrun.com/projects/vfu/ 290kB vfu-5.07.tar.gz Original-site: http://cade.noxrun.com/projects/vfu/ Platforms: UNIX (Linux, NetBSD, Mac OS X, Solaris), DOS/Win9X Copying-policy: GPL End vfu-5.09/vfu.wiki0000644000175000017500000002365614367030452012326 0ustar cadecadevfu ia a nifty little text-based file-manager which can be run from a terminal. <> == Helpful Commands: == * '''h''' - help * '''q''' - exit to the (VFU) current directory (using optional shell script, see below) * '''x''' - exit to the last working directory * '''ENTER''' - view file or enter folder * '''BKSPACE''' - up a level * '''a''' - arrange (sort) files list * '''e''' - erase file/folder * '''c''' - copy files * '''m''' - move files * '''l''' - symlink files (lower L) * '''b''' - browse (view) files * '''g''' - global select files by criteria. also modifies files list (hide entries for example) * '''s''' - search for filename, wildcards are available (advance with '''TAB''' during search) * '''i''' - edit file * '''1'''..'''9''' - toggle display of files details (mode, time, size...) * '''0''' - switch between simple and detailed listings (full info or just filenames) * '''~''' - to home directory * '''.''' - show/hide hidden files (.dot files) * '''f''' - mask files (CTRL+f resets mask back to '''*''') * '''d''' - change working directory * '''ALT+d''' - change directory history * '''`''' - directory bookmarks * '''u''' - user menu * '''TAB''' - edit file name/properties * '''o''' - runtime options (those are not saved/read from the config file) * '''p''' - files clipboard (copy, move files from several locations...) * '''v''' - edit VFU config file VFU will execute actions either on the currently pointed file (if no selection exists) or on the currently selected entries. Using ALT+key exacutes action only on the currently pointed file, regardless selection. Those rules apply to '''copy''' (ALT+c), '''move''' (ALT+m), '''symlink''' (ALT+l), '''browse''' (ALT+b) operations. == Changing location of config file: == To change where the configuration file is stored ( such as moving it to .config so that it does not clutter the home folder) add {{{ # to get vfu to put config file into ~/.conf # have to export $RC_PREFIX as .conf export RC_PREFIX=.config }}} into the file {{{.bashrc}}}. The configiuraton file can now be found in {{{~/.config/vfu/vfu.conf}}} If you decide not to change the location of the config file, it will be stored in {{{~/.vfu/vfu.conf}}} == Quit To Current (VFU) Working Directory == VFU offers two exits. One to the last working directory ('''x''' key) and the other is to the directory VFU is currently in ('''q''' key). To use the latter you need to start VFU with shell function. Here is example for bash(1): {{{ function vfu() { /usr/local/bin/vfu $*; cd "`cat /tmp/vfu.exit.$USER`"; rm -f /tmp/vfu.exit.$USER; } alias vf="vfu" # you need to add this to your ~/.profile or ~/.bash_profile }}} == Custom File Editor: == To change the text editor used by vfu find, within the {{{vfu.conf}}} file, the line (near the top): {{{ Editor=vim %f }}} and change it so that `vim` is the editor of your choice, such as `gedit`, `kwrite`, `mousepad`, `vi`, `vim`, `nano`, `emacs`. You may have to turn off the 'use internal editor' option in the options menu (press `o` to bring up menu and `SPACE` to turn off option). It is also possible to use a different viewer if you so wish. == Custom File Associations: == Vfu can be customised to open different files with different programs; Entries are put in the comma separated format such as: {{{ ux=SEE DOC,ENTER,.doc.DOC.odt.sxw.,ooffice2 -writer "%f" & }}} Where: * '''SEE DOC''' is just a short descriptive name * '''ENTER''' is the action on the file * '''.ext.ext.ext.ext.''' the file extensions to which the actions apply (dot separated list) * '''ooffice2 -writer "%f"''' is the command to use on the file, "%f" is the location of the file * '''&''' indicates that you wish to continue using vfu whist the file is open Thus, vfu will open ooffice2 -writer when I press ENTER on a .odt file (or .doc , .sxw etc). If you don't add '''&''' at the end, VFU will wait ooffice2 to exit before returning back to files list. General syntax is: {{{ description,keyname,ext.mask,command }}} * '''description''' is just free text, keep it small, first letter can be used as menu hotkey * '''keyname''' is key you want to bind * '''ext.mask''' is dot-separated list of required extensions and/or file type strings (extension mask) * '''command''' is the shell line you want to execute (as on the command prompt for example) Available keys (keynames) are: {{{ ENTER, INSERT, F1..F10, @F1..@F10, #F1..#F10, ^F1..^F10 Modifiers are: #=shift @=alt ^=ctrl note: ^KEY and @KEY may not always work, try pressing ESC then Fx as alternative to @Fx }}} Extension mask can be '''*''' to discard file type and run command for any/all files. It may also contain file types: {{{ ** -- executable file [] -- directory -> -- symbolic link <> -- symbolic link to directory == -- block device ++ -- character device () -- fifo (pipe) ## -- socket }}} You can mix file extensions with file type strings in the same mask. Don't confuse '''*''' (any/all files) with '''**''' (executables). In any '''command''' string (shell exec lines) you can use those placeholders: {{{ %f -- replaced with current filename (w/o path) %F -- replaced with full pathname of the current file %g -- same as %f but for each selected filename %G -- same as %F but for each selected filename %g and %G produce list of filenames. each filename is surrounded by ' if filename contains ' then " is used. example: 'file1' "file2's" 'file"3"' ... %e -- current file name without extension %E -- current file extension %s -- current file size %c -- current path ( with slash at the end ) %C -- startup path ( with slash at the end ) %R -- re-read directory content after shell %? -- prompt for user input and replace it in %i -- simulates DownArrow after execution (advances file pointer to the next file in the list) %n -- don't clear and redraw screen on user external command %w -- wait a key after shell. replaced with empty string %x -- replaced with `x' (i.e. if '''x''' is not recognised as placeholder) %! -- request shell line to be shown before execution (debug mode) }}} Example section: {{{ # view pictures ux=SEE JPEG,ENTER,.JPG.jpg.JPEG.jpeg.gif.xpm.png.,feh "%f" 2> /dev/null & # view HTML documents -- now moved to SEE filters below ux=SEE HTML,ENTER,.htm.html.shtml.,firefox "%F" & #ux=SEE HTM,ENTER,.htm.html.,w3m "%F" ux=SEE HTML,INSERT,.htm.html.shtml.,w3m "%F" # office document viewing ux=SEE DOC,ENTER,.doc.DOC.odt.sxw.,ooffice2 -writer "%f" & ux=SEE SHEET,ENTER,.ods.sxc.xls.,ooffice2 -calc "%f" & ux=SEE PRESENTATION,ENTER,.odp.ppt.sxi.,ooffice2 -impress "%f" & ux=SEE DRAW,ENTER,.odg.sxd.sda.sdd.,ooffice2 -draw "%f" & #music x=PLAY MP3,ENTER,.ogg.mp3.wav.wma.,vlc -I skins2 "%f" %i 1> /dev/null 2> /dev/null & ux=PLAY MP3,INSERT,*,xmms *.mp3 *.wav 1> /dev/null 2> /dev/null & # view PDF and PS document ux=VIEW PDF,ENTER,.pdf.PDF.,acroread "%f"& ux=VIEW PS,ENTER,.ps.,gv "%f"& # ux=VIEW TAR,INS,.gz.,gunzip -c "%f" | tar tvf - | less # view man pages -- note you can add and see filter for this ux=VIEW MAN,ENTER,.1.2.3.4.5.6.7.8.,man "%F" # play mpeg's ux=PLAY MPEG,ENTER,.mpg.MPG.mpeg.asf.avi.mov.wmv.,vlc -I skins2 "%f" 1> /dev/null 2> /dev/null & ux=PLAY MPEG,INS,.mpg.MPG.mpeg.asf.avi.mov.wmv.,vlc -I skins2 "%f" 1> /dev/null 2> /dev/null & }}} == Custom User Menu: == The user menu is reached by pressing the `u` key. Custom options in this menu can be added by finding the section; {{{ # # following user commands are bound to the UserMenu -- key `u' # note that instead of keyname there's `menu' string! # first letter is hotkey! # ux=lLocate file,menu,*,locate %? %w ux=---,menu,*, ux=ompg123: Stop,menu,*,killall -TERM mpg123 1> /dev/null 2> /dev/null & ux=smpg123: Suspend,menu,*,killall -STOP mpg123 1> /dev/null 2> /dev/null & ux=cmpg123: Continue,menu,*,killall -CONT mpg123 1> /dev/null 2> /dev/null & ux=vmpg123: View running/queue,menu,*,ps xeo "%%p %%a" | grep mpg123 | grep -v grep | less }}} The format is of comma separated fields: {{{ ux=kname,menu,*,command }}} where: * `k` is the shortcut key in the menu. * `name` is the name of the entry in the menu. * `menu` states that it is a menu item. * `*` is the filetype filter - leave it as an asterix to enable it to be performed on all files/folders. * `command` is the command to be performed the variable "%f" is used to add in the location of the file/folder. An '&' can be added on at the end if you want to continue using vfu whilst the action is being performed. Example menu section: {{{ # # following user commands are bound to the UserMenu -- key `u` # note that instead of keyname there's `menu` string! # first letter is hotkey! # ux=lLocate file,menu,*,locate %? %w ux=---,menu,*, ux=ffeh: show pics in feh,menu,*,feh "%f" & ux=gfeh: show pics fullscreen,menu,*,feh -F "%f" ux=pplay: play in xfmedia,menu,*,xfmedia "%f" & ux=wwatch: play in vlc,menu,*,vlc -I skins2 "%f" 1> /dev/null 2> /dev/null & ux=---,menu,*, ux=ompg123: Stop,menu,*,killall -TERM mpg123 1> /dev/null 2> /dev/null & ux=smpg123: Suspend,menu,*,killall -STOP mpg123 1> /dev/null 2> /dev/null & ux=cmpg123: Continue,menu,*,killall -CONT mpg123 1> /dev/null 2> /dev/null & ux=vmpg123: View running/queue,menu,*,ps xeo "%%p %%a" | grep mpg123 | grep -v grep | less }}} Where extra options have been added to play music/videos in xfmedia or vlc and to show pictures in feh. i.e. It is now possible to open a folder of pictures using feh or a folder of music using vlc (with the options for a nice skin) But the best way to find out about vfu (and most other programs) is to make a backup of the file and then play around with it to see what you can get it to do. == Links == [[https://cade.noxrun.com/projects/vfu|vfu homepage]] CategoryDocumentation vfu-5.09/Makefile0000644000175000017500000000244014367030452012265 0ustar cadecadePKG_CONFIG?=pkg-config YASCREEN_LD?=$(shell $(PKG_CONFIG) --libs yascreen) NCURSES_LD?=$(shell $(PKG_CONFIG) --libs ncursesw) BINS:=vfu/vfu vfu/vfu.yas ifeq ($(YASCREEN_LD),) BINS:=$(filter-out vfu/vfu.yas,$(BINS)) endif ifeq ($(NCURSES_LD),) BINS:=$(filter-out vfu/vfu,$(BINS)) endif ifeq ("$(V)","1") Q:= E:=@true else Q:=@ E:=@echo endif all: $(BINS) vfu/vfu: vstring/libvstring.a vslib/libvslib.a vslib/libvscon.a $(E) MAKE vfu $(Q)$(MAKE) -C vfu vfu vfu/vfu.yas: vstring/libvstring.a vslib/libvslib.a vslib/libvscony.a $(E) MAKE vfu.yas $(Q)$(MAKE) -C vfu vfu.yas vstring/libvstring.a: $(E) MAKE libvstring.a $(Q)$(MAKE) -C vstring libvstring.a vslib/libvslib.a: $(E) MAKE libvslib.a $(Q)$(MAKE) -C vslib libvslib.a vslib/libvscon.a: $(E) MAKE libvscon.a $(Q)$(MAKE) -C vslib libvscon.a vslib/libvscony.a: $(E) MAKE libvscony.a $(MAKE) -C vslib libvscony.a clean: clean-vslib clean-vstring clean-vfu clean-vslib: $(E) CLEAN vslib $(Q)$(MAKE) --no-print-directory -C vslib clean clean-vstring: $(E) CLEAN vstring $(Q)$(MAKE) --no-print-directory -C vstring clean clean-vfu: $(E) CLEAN vfu $(Q)$(MAKE) --no-print-directory -C vfu clean re: $(Q)$(MAKE) --no-print-directory clean $(Q)$(MAKE) --no-print-directory -j .PHONY: all clean clean-vfu clean-vslib clean-vstring re vfu-5.09/build.sh0000755000175000017500000000147614145574023012274 0ustar cadecade#!/bin/sh echo "-------------------------------------------------" echo "Compiling VSTRING..." make -C vstring if [ -e vstring/libvstring.a ]; then echo "VSTRING compiled ok." else echo "VSTRING compilation failed..." fi echo "-------------------------------------------------" echo "Compiling VSLIB..." make -C vslib if [ -e vslib/libvslib.a ]; then echo "VSLIB compiled ok." else echo "VSLIB compilation failed..." fi echo "-------------------------------------------------" echo "Compiling YASCREEN..." make -C yascreen if [ -e yascreen/yascreen.a ]; then echo "VSLIB compiled ok." else echo "VSLIB compilation failed..." fi echo "-------------------------------------------------" echo "Compiling VFU..." make -C vfu if [ -e vfu/vfu ]; then echo "VFU compiled ok." else echo "VFU compilation failed..." fi vfu-5.09/vfu.html0000644000175000017500000001220314367030452012311 0ustar cadecade vfu - VFU is console file manager for UNIX/Linux

NAME

vfu - VFU is console (text-mode) file manager for UNIX/Linux


SYNOPSIS

vfu [options]


DESCRIPTION

vfu has following features:

 - Fast one-key commands
 - Extended completition and wildcard expansion
 - Directory tree with sizes (incl. sizes cache)
 - File-type colorization (extension and type)
 - Archives support (TAR, TGZ, BZ2, and many more)
 - Simple FTP support through archive-like interface
 - Internal text/hex file viewer and editor (optional)
 - Extensive user-defined external support/utils
 - Regular expressions selection and search
 - Multiple file masks
 - and much more...


OPTIONS

 -h
prints help for command line options
 -i
runs vfu in interactive mode (can be used with \-d)
 -d path
changes working directory to `path'
 -r
rebuild directory tree
 -t
view directory tree only

These options are for using vfu in partial non-interactive mode.

 example: vfu -d /usr/local -i


CONFIGURATION

vfu configuration is divided in two parts (files):

vfu.conf

This file contains configuration for external editor and pager, favorite directories, file-type colorization. This is plain text file which format is described below. vfu only reads vfu.conf, it never writes in it!

vfu.options

This file contains all Options/Toggles which can be changed from inside vfu -- Options menu. vfu.options is binary file and is overwritten on vfu exit or on change option event! Deleting this file will reset vfu toggles to default values. vfu.options is not portable between vfu versions!

Location of vfu.conf

vfu.conf can be placed as any of:

 $HOME/.vfu/vfu.conf

 $HOME/$RC_PREFIX/vfu/vfu.conf  (if $RC_PREFIX is exported)

 /etc/vfu.conf

 /usr/local/etc/vfu.conf

 /usr/local/vfu.conf

Other files location

All other files including vfu.options are placed in:

 $HOME/.vfu/

 $HOME/$RC_PREFIX/vfu/

What is $RC_PREFIX and why to use it? Some more information for this can be found in the CONFIG file which is supplied with vfu package.


VFU.CONF

This is a sample copy of vfu.conf with appropriate comments:


DISTRIBUTION

It is supposed that only source packages will be distributed. However sometimes binary packages will be released, but not often. VFU packages are named in this way:

 vfu-X.xx.src.tgz

source package for version X.xx
 vfu-X.xx.bin.platform.tgz

binary package for version X.xx for `platform' platform

examples:

 vfu-4.00.src.tgz
 vfu-4.00.bin.linux.glibc.tgz
 vfu-4.00.bin.linux.libc5.tgz
 vfu-4.00.bin.dos.tgz

All packages are TAR+GZIP .tgz, bz2 and zip are available on request.

NOTE: Always check HISTORY document -- it often contains some usefull notes!


FILES

 $HOME/$RC_PREFIX/vfu/vfu.conf

configuration, explained above

 $HOME/$RC_PREFIX/vfu/vfu.options

options, explained above

 $HOME/$RC_PREFIX/vfu/vfu.history

contains history lines

 $HOME/$RC_PREFIX/vfu/vfu.tree

contains directory tree

If you don't set $RC_PREFIX configuration files are:

 $HOME/.vfu/vfu.conf
 $HOME/.vfu/vfu.options
 $HOME/.vfu/vfu.history
 $HOME/.vfu/vfu.tree


TODO

see the TODO file


BUGS

unknown


AUTHOR

 Vladi Belperchinov-Shabanski "Cade"
 <cade@bis.bg> <cade@noxrun.com>
 https://cade.noxrun.com/projects/vfu
vfu-5.09/yascreen/0000755000175000017500000000000014444676575012460 5ustar cadecadevfu-5.09/yascreen/yascreen.c0000644000175000017500000014003014444676575014433 0ustar cadecade// $Id: yascreen.c,v 1.97 2023/02/16 04:40:29 bbonev Exp $ // // Copyright © 2015-2023 Boian Bonev (bbonev@ipacct.com) {{{ // // SPDX-License-Identifer: LGPL-3.0-or-later // // This file is part of yascreen - yet another screen library. // // yascreen is free software, released under the terms of GNU Lesser General Public License v3.0 or later // }}} // {{{ includes #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include // }}} // {{{ definitions #define mymax(a,b) (((a)>(b))?(a):(b)) #define mymin(a,b) (((a)<(b))?(a):(b)) #define ESC "\x1b" // size of string that can be stored immediately w/o allocation #define PSIZE (sizeof(char *)) // step to allocate key buffer #define KEYSTEP (4096/sizeof(int)) // default timeout in milliseconds before escape is returned #define YAS_DEFAULT_ESCTO 300 // check if a given value is a valid simple color value #define YAS_ISCOLOR(c) ((c)>=8&&(c)<=15) // check if a given value is a valid extended color value #define YAS_ISXCOLOR(c) ((c)&0x100) #define YAS_STORAGE 0x80000000 // data is longer than PSIZE and is stored in allocated memory #define YAS_TOUCHED 0x40000000 // there are changes in this line, update cannot skip it #define YAS_INTERNAL (YAS_STORAGE|YAS_TOUCHED) #define TELNET_EOSN 240 // 0xf0 // end of subnegotiation #define TELNET_NOP 241 // 0xf1 // NOP #define TELNET_SYNCH 242 // 0xf2 // SYNCH #define TELNET_NVTBRK 243 //0xf3 // NVTBRK #define TELNET_IP 244 // 0xf4 // IP #define TELNET_AO 245 // 0xf5 // AO #define TELNET_AYT 246 //0xf6 // AYT are you there #define TELNET_EC 247 // 0xf7 // EC #define TELNET_EL 248 // 0xf8 // EL #define TELNET_GOA 249 // 0xf9 // go ahead #define TELNET_SOSN 250 // 0xfa // start of subnegotiation #define TELNET_WILL 251 // 0xfb // will #define TELNET_WONT 252 // 0xfc // wont #define TELNET_DO 253 // 0xfd // do #define TELNET_DONT 254 // 0xfe // dont #define TELNET_IAC 255 // 0xff // telnet protocol escape code (IAC) #define TELNET_NOOP 0x100 // telnet protocol handler have eaten a byte w/o yielding any result #define TELNET_SIZE 0x101 // telnet protocol handler have detected screen size change notification #define TELNET_NAWS 31 // 0x1f // negotiate about window size // data is kept as utf8, including its combining chars // if it fits in PSZIE, it is in d, with 0 terminating char // if the char at previous position requires 2 columns, current char should be empty // after initialization all chars are set to ' ' (0x20) typedef struct _cell { uint32_t style; // color, style and storage type union { char *p; char d[PSIZE]; }; } cell; typedef enum { // ansi sequence state machine ST_NORM, // normal input, check for ESC ST_ENTER, // eat LF/NUL after CR ST_ESC, // escape sequence ST_ESC_SQ, // escape [ sequence ST_ESC_SQ_D, // escape [ digit sequence ST_ESC_O, // escape O sequence } yas_k_state; typedef enum { // telnet sequence state machine T_NORM, // expect regular byte or IAC T_IAC, // telnet IAC, expect option T_IAC_O, // telnet IAC option, expect opt code T_IAC_SB, // telnet IAC extended, expect IAC T_IAC_SE, // telnet IAC extended, expect SE } yas_t_state; typedef enum { // utf8 sequence state machine U_NORM, // expect single byte or leading byte U_L2C1, // expect 1/1 continuation byte U_L3C1, // expect 1/2 continuation bytes U_L3C2, // expect 2/2 continuation bytes U_L4C1, // expect 1/3 continuation bytes U_L4C2, // expect 2/3 continuation bytes U_L4C3, // expect 3/3 continuation bytes U_L5C1, // expect 1/4 continuation bytes U_L5C2, // expect 2/4 continuation bytes U_L5C3, // expect 3/4 continuation bytes U_L5C4, // expect 4/4 continuation bytes U_L6C1, // expect 1/5 continuation bytes U_L6C2, // expect 2/5 continuation bytes U_L6C3, // expect 3/5 continuation bytes U_L6C4, // expect 4/5 continuation bytes U_L6C5, // expect 5/5 continuation bytes } yas_u_state; struct _yascreen { int sx,sy; // size of screen ssize_t (*outcb)(yascreen *s,const void *data,size_t len); // output callback cell *mem; // memory state cell *scr; // screen state struct termios *tsstack; // saved terminal state int tssize; // number of items in the stack int escto; // single ESC key timeout (in milliseconds) int keysize; // saved key storage size int keycnt; // saved key count int *keys; // saved key array unsigned char ansibuf[20]; // buffer for escape sequence parsing unsigned char ansipos; // next byte will go in this pos unsigned char sosnbuf[20]; // buffer for telnet SOSN options parsing unsigned char sosnpos; // next byte will go in this pos unsigned char utf[3]; // buffer for utf8 sequence parsing; last byte is not put here, its never zero terminated // must be increased to 4 or 5 if some day unicode permits 5 or 6 byte sequences int64_t escts; // single ESC key timestamp yas_k_state state; // input parser state yas_t_state tstate; // telnet parser state yas_u_state ustate; // utf8 parser state int cursorx; // position to place cursor on update int cursory; // position to place cursor on update int scrx; // last reported screen size int scry; // last reported screen size uint8_t haveansi:1; // we do have a reported screen size from ansi sequence uint8_t havenaws:1; // we do have a reported screen size from telent naws uint8_t istelnet:1; // do process telnet sequences uint8_t isunicode:1; // do process unicode sequences uint8_t cursor:1; // last cursor state uint8_t redraw:1; // flag to redraw from scratch uint8_t lineflush:1; // always flush after line operations int hint; // user defined hint (scalar) void *phint; // user defined hint (pointer) uint8_t outb[256]; // buffered output uint16_t outp; // position in outb }; // helpers for versioned symbols #if YASCREEN_VERSIONED // implementation always follows the attribute #if defined __GNUC__ && __GNUC__ >= 10 && !defined(__clang__) #define symver_o(impl,sym,ver) __attribute__((symver(#sym"@"#ver))) #define symver_d(impl,sym,ver) __attribute__((symver(#sym"@@"#ver))) #else #define symver_o(impl,sym,ver) asm(".symver "#impl","#sym"@"#ver); #define symver_d(impl,sym,ver) asm(".symver "#impl","#sym"@@"#ver); #endif #define VV(foo,ver) foo##ver #define V(foo,ver) VV(foo,ver) #define V193 _193 #else #define symver_o(impl,sym,ver) #define symver_d(impl,sym,ver) #define V(foo,ver) foo #define V193 #endif #define symver_V(impl,sym,ver) symver_d(impl,sym,ver) #define YASCREEN_193 YASCREEN_1.93 // }}} static inline int64_t mytime() { // {{{ struct timespec ts; int64_t res; clock_gettime(CLOCK_MONOTONIC,&ts); res=ts.tv_sec*1000; res+=ts.tv_nsec/1000000; return res; } // }}} static inline ssize_t out(yascreen *s,const void *vbuf,size_t len) { // {{{ const uint8_t *buf=vbuf; size_t olen=len; repeat: if (len) { if (sizeof s->outb-s->outp>=len) { memcpy(s->outb+s->outp,buf,len); s->outp+=len; } else { size_t brem=sizeof s->outb-s->outp; ssize_t wr; memcpy(s->outb+s->outp,buf,brem); s->outp+=brem; buf+=brem; len-=brem; wr=write(STDOUT_FILENO,s->outb,s->outp); if (wr<=0) // error return wr; if (wr==s->outp) { s->outp-=wr; goto repeat; } // wr>0 and wroutp memmove(s->outb,s->outb+wr,s->outp-wr); s->outp-=wr; goto repeat; } } if (!olen&&s->outp) { // flush is requested ssize_t wr=write(STDOUT_FILENO,s->outb,s->outp); if (wr==s->outp) s->outp=0; else if (wr>0) { memmove(s->outb,s->outb+wr,s->outp-wr); s->outp-=wr; } } return olen; } // }}} static inline void outs(yascreen *s,const char *str) { // {{{ ssize_t (*o)(yascreen *s,const void *buf,size_t len); size_t len; if (!s) return; if (!str) return; len=strlen(str); o=s->outcb?s->outcb:out; if (!len) // explicit flush o(s,"",0); while (len) { ssize_t r=o(s,str,len); if (r>=0) { len-=r; str+=r; } else break; } } // }}} static inline void outf(yascreen *s,const char *format,...) __attribute__((format(printf,2,3))); // {{{ // }}} static inline void outf(yascreen *s,const char *format,...) { // {{{ va_list ap; char *ns; int size; if (!s) return; if (!format) return; va_start(ap,format); size=vasprintf(&ns,format,ap); va_end(ap); if (size==-1) // some error, nothing more to do return; outs(s,ns); free(ns); } // }}} inline void yascreen_set_hint_i(yascreen *s,int hint) { // {{{ if (!s) return; s->hint=hint; } // }}} inline int yascreen_get_hint_i(yascreen *s) { // {{{ if (!s) return 0; return s->hint; } // }}} inline void yascreen_set_hint_p(yascreen *s,void *hint) { // {{{ if (!s) return; s->phint=hint; } // }}} inline void *yascreen_get_hint_p(yascreen *s) { // {{{ if (!s) return NULL; return s->phint; } // }}} static char myver[]="\0Yet another screen library (https://github.com/bbonev/yascreen) $Revision: 1.97 $\n\n"; // {{{ // }}} inline const char *yascreen_ver(void) { // {{{ return myver; } // }}} inline yascreen *yascreen_init(int sx,int sy) { // {{{ yascreen *s; int i; if (myver[0]==0) { // reformat the static version string char *rev=strstr(myver+1,"$Revision: "); int vermaj,vermin; if (rev) { sscanf(rev+strlen("$Revision: "),"%d.%d",&vermaj,&vermin); vermaj+=vermin/100; vermin=vermin%100; memmove(myver,myver+1,strlen(myver+1)+1); snprintf(rev-1,sizeof myver-(rev-1-myver),"%d.%02d\n\n",vermaj,vermin); } } if (sx<0||sy<0) return NULL; s=(yascreen *)calloc(1,sizeof *s); if (!s) return NULL; if (/*!s->outcb&&*/isatty(STDOUT_FILENO)) { // output is a terminal s->tsstack=(struct termios *)calloc(1,sizeof(struct termios)); if (!s->tsstack) { free(s); return NULL; } s->tssize=1; tcgetattr(STDOUT_FILENO,s->tsstack); if (!sx||!sy) { struct winsize ws={0}; if (!ioctl(STDOUT_FILENO,TIOCGWINSZ,&ws)) { if (!sx) sx=ws.ws_col; if (!sy) sy=ws.ws_row; } } } if (sx<=0||sy<=0) { if (s->tsstack) free(s->tsstack); free(s); return NULL; } s->sx=sx; s->sy=sy; // s->outcb is already NULL // s->mem is initialized below // s->scr is initialized below // s->tstack is initialized above // s->tssize is initialized above s->escto=YAS_DEFAULT_ESCTO; // s->keysize is initialized below // s->keycnt is already zero // s->keys is initialized below // s->ansibuf is already zeroes // s->ansipos is already zero // s->sosnbuf is already zeroes // s->sosnpos is already zero // s->utf is already zeroes // s->escts is already zero s->state=ST_NORM; s->tstate=T_NORM; s->ustate=U_NORM; // s->cursorx is already zero // s->cursory is already zero // s->scrx is already zero // s->scry is already zero // s->haveansi is already zero // s->havenaws is already zero // s->istelnet is already zero s->isunicode=1; // previous versions were unicode only s->cursor=1; // cursor is visible by default s->redraw=1; // leave scr empty, so that on first refresh everything is redrawn s->lineflush=1; // be compatible with earlier versions that worked without output buffering normally a recent client will set this to 0 and use explicit flush // s->hint is already zero // s->phint is already NULL // s->outb is already zeroes // s->outp is already zero s->keys=(int *)calloc(KEYSTEP,sizeof(int)); if (!s->keys) { if (s->tsstack) free(s->tsstack); free(s); return NULL; } s->keysize=KEYSTEP; s->mem=(cell *)calloc(sx*sy,sizeof(cell)); s->scr=(cell *)calloc(sx*sy,sizeof(cell)); if (!s->mem||!s->scr) { if (s->mem) free(s->mem); if (s->scr) free(s->scr); if (s->tsstack) free(s->tsstack); if (s->keys) free(s->keys); free(s); return NULL; } for (i=0;imem[i].d," ",sizeof s->mem[i].d); return s; } // }}} inline int yascreen_setout(yascreen *s,ssize_t (*out)(yascreen *s,const void *data,size_t len)) { // {{{ if (!s) return -1; s->outcb=out; s->redraw=1; return 0; } // }}} inline void yascreen_set_telnet(yascreen *s,int on) { // {{{ if (!s) return; s->istelnet=!!on; } // }}} inline void yascreen_set_unicode(yascreen *s,int on) { // {{{ if (!s) return; s->isunicode=!!on; s->keycnt=0; // flush input buffer - it may not be verified unicode } // }}} inline void yascreen_init_telnet(yascreen *s) { // {{{ if (!s) return; if (s->istelnet) outs(s, "\xff\xfb\x03" // will(251) suppress go ahead "\xff\xfb\x01" // will(251) echo "\xff\xfd\x03" // do(253) suppress go ahead "\xff\xfd\x01" // do(253) echo "\xff\xfb\x1f" // will(251) negotiate terminal size "\xff\xfd\x1f" // do(253) negotiate terminal size ); else outs(s, "\xff\xfc\x03" // wont(251) suppress go ahead "\xff\xfc\x01" // wont(251) echo "\xff\xfe\x03" // dont(253) suppress go ahead "\xff\xfe\x01" // dont(253) echo "\xff\xfc\x1f" // wont(251) negotiate terminal size "\xff\xfe\x1f" // dont(253) negotiate terminal size ); } // }}} inline int yascreen_resize(yascreen *s,int sx,int sy) { // {{{ cell *mem,*scr; int i; if (!s) return -1; if (sx<0||sy<0) return -1; if (!sx||!sy) if (!s->outcb&&isatty(STDOUT_FILENO)) { struct winsize ws={0}; if (!ioctl(STDOUT_FILENO,TIOCGWINSZ,&ws)) { if (!sx) sx=ws.ws_col; if (!sy) sy=ws.ws_row; } } if (sx<=0||sy<=0) return -1; if (s->sx==sx&&s->sy==sy) return 0; for (i=0;isx*s->sy;i++) { // free old allocated data and set for reusage if (s->mem[i].style&YAS_STORAGE) free(s->mem[i].p); if (s->scr[i].style&YAS_STORAGE) free(s->scr[i].p); if (imem[i].style=s->scr[i].style=0; s->mem[i].d[0]=' '; s->scr[i].d[0]=0; } } if (sx*sy>s->sx*s->sy) { // allocate bigger buffer mem=(cell *)realloc(s->mem,sx*sy*sizeof(cell)); if (!mem) return -1; s->mem=mem; scr=(cell *)realloc(s->scr,sx*sy*sizeof(cell)); if (!scr) return -1; s->scr=scr; for (i=s->sx*s->sy;imem[i].style=s->scr[i].style=0; s->mem[i].d[0]=' '; s->scr[i].d[0]=0; } } s->redraw=1; s->sx=sx; s->sy=sy; return 0; } // }}} inline void yascreen_free(yascreen *s) { // {{{ int i; if (!s) return; if (!s->mem||!s->scr) { // error condition that will happen only if mem is corrupt if (s->mem) free(s->mem); if (s->scr) free(s->scr); if (s->tsstack) free(s->tsstack); if (s->keys) free(s->keys); free(s); // most probably will crash, because there is no way to have s partally initialized return; } for (i=0;isx*s->sy;i++) { if (s->mem[i].style&YAS_STORAGE) free(s->mem[i].p); if (s->scr[i].style&YAS_STORAGE) free(s->scr[i].p); } free(s->mem); free(s->scr); if (s->tsstack) free(s->tsstack); if (s->keys) free(s->keys); outs(s,ESC"[0m"); free(s); } // }}} inline void yascreen_update_attr(yascreen *s,uint32_t oattr,uint32_t nattr) { // {{{ if (!s) return; if (oattr==0xffffffff) { oattr=~nattr; // force setting all outs(s,ESC"[0m"); } if ((oattr&YAS_BOLD)!=(nattr&YAS_BOLD)) outs(s,(nattr&YAS_BOLD)?ESC"[1m":ESC"[22m"); if ((oattr&YAS_ITALIC)!=(nattr&YAS_ITALIC)) outs(s,(nattr&YAS_ITALIC)?ESC"[3m":ESC"[23m"); if ((oattr&YAS_UNDERL)!=(nattr&YAS_UNDERL)) outs(s,(nattr&YAS_UNDERL)?ESC"[4m":ESC"[24m"); if ((oattr&YAS_BLINK)!=(nattr&YAS_BLINK)) outs(s,(nattr&YAS_BLINK)?ESC"[5m":ESC"[25m"); if ((oattr&YAS_INVERSE)!=(nattr&YAS_INVERSE)) outs(s,(nattr&YAS_INVERSE)?ESC"[7m":ESC"[27m"); if ((oattr&YAS_STRIKE)!=(nattr&YAS_STRIKE)) outs(s,(nattr&YAS_STRIKE)?ESC"[9m":ESC"[29m"); if (YAS_FG(oattr)!=YAS_FG(nattr)) { if (YAS_ISXCOLOR(YAS_FG(nattr))) outf(s,ESC"[38;5;%dm",YAS_FG(nattr)-0x100); else { if (YAS_ISCOLOR(YAS_FG(nattr))) outf(s,ESC"[%dm",YAS_FG(nattr)-8+30); else outs(s,ESC"[39m"); } } if (YAS_BG(oattr)!=YAS_BG(nattr)) { if (YAS_ISXCOLOR(YAS_BG(nattr))) outf(s,ESC"[48;5;%dm",YAS_BG(nattr)-0x100); else { if (YAS_ISCOLOR(YAS_BG(nattr))) outf(s,ESC"[%dm",YAS_BG(nattr)-8+40); else outs(s,ESC"[49m"); } } } // }}} static inline int yascreen_update_range(yascreen *s,int y1,int y2) { // {{{ int i,j,ob=0,redraw=0; char ra[]=ESC"[0m"; uint32_t lsty=0,nsty; if (!s) return -1; if (s->redraw) { redraw=1; s->redraw=0; outf(s,ESC"[2J"ESC"[H%s",ra); // clear and position on topleft *ra=0; } y1=mymin(s->sy-1,mymax(0,y1)); y2=mymin(s->sy,mymax(0,y2)); for (j=y1;jmem[s->sx*j].style&YAS_TOUCHED)) // skip untouched lines continue; s->mem[s->sx*j].style&=~YAS_TOUCHED; // mark updated lines as not touched for (i=0;isx;i++) { int diff=redraw; // forced redraw if (!diff) // compare attributes diff=(s->mem[i+s->sx*j].style&~YAS_INTERNAL)!=(s->scr[i+s->sx*j].style&~YAS_INTERNAL); if (!diff) // compare content diff=!!strcmp((s->mem[i+s->sx*j].style&YAS_STORAGE)?s->mem[i+s->sx*j].p:s->mem[i+s->sx*j].d,(s->scr[i+s->sx*j].style&YAS_STORAGE)?s->scr[i+s->sx*j].p:s->scr[i+s->sx*j].d); if (diff||!skip) { if (skip) { outf(s,ESC"[%d;%dH%s",1+j,1+i,ra); *ra=0; skip=0; } if (diff) { if (cnt>7) { outf(s,ESC"[%d;%dH%s",1+j,1+i,ra); *ra=0; cnt=0; } while (cnt>=0) { nsty=s->mem[j*s->sx+i-cnt].style&~YAS_INTERNAL; if (lsty!=nsty) { yascreen_update_attr(s,lsty,nsty); lsty=nsty; } outs(s,(s->mem[j*s->sx+i-cnt].style&YAS_STORAGE)?s->mem[j*s->sx+i-cnt].p:s->mem[j*s->sx+i-cnt].d); cnt--; } cnt=0; // loop above leaves cnt at -1 } else cnt++; } if (s->scr[j*s->sx+i].style&YAS_STORAGE) free(s->scr[j*s->sx+i].p); s->scr[j*s->sx+i].style=s->mem[j*s->sx+i].style; if (s->mem[j*s->sx+i].style&YAS_STORAGE) s->scr[j*s->sx+i].p=strdup(s->mem[j*s->sx+i].p); else strncpy(s->scr[j*s->sx+i].d,s->mem[j*s->sx+i].d,sizeof s->scr[j*s->sx+i].d); } } if (s->cursor) outf(s,ESC"[%d;%dH",s->cursory+1,s->cursorx+1); outs(s,""); // request a flush return ob; } // }}} inline int yascreen_update(yascreen *s) { // {{{ if (!s) return -1; return yascreen_update_range(s,0,s->sy); } // }}} static inline void yascreen_putcw(yascreen *s,uint32_t attr,const char *str,int width) { // {{{ if (!*str) // noop return; if (!str[1]) { // handle CR/LF switch (*str) { case '\n': s->cursory++; // fall through case '\r': s->cursorx=0; return; } } if (s->cursory<0||s->cursory>=s->sy) return; if (width&&s->cursorx>=0&&s->cursorxsx&&s->cursorx+width<=s->sx) { int i; // normal char if (s->mem[s->cursorx+s->cursory*s->sx].style&YAS_STORAGE) { s->mem[s->cursorx+s->cursory*s->sx].style&=~YAS_STORAGE; free(s->mem[s->cursorx+s->cursory*s->sx].p); s->mem[s->cursorx+s->cursory*s->sx].p=0; } if (strlen(str)mem[s->cursorx+s->cursory*s->sx].d,str,sizeof s->mem[s->cursorx+s->cursory*s->sx].d); s->mem[s->cursorx+s->cursory*s->sx].style=attr; } else { char *ts=strdup(str); if (!ts) return; // nothing more to do s->mem[s->cursorx+s->cursory*s->sx].p=ts; s->mem[s->cursorx+s->cursory*s->sx].style=YAS_STORAGE|attr; } s->mem[s->cursory*s->sx].style|=YAS_TOUCHED; s->cursorx++; for (i=1;icursorxsx) { if (s->mem[s->cursorx+s->cursory*s->sx].style&YAS_STORAGE) { s->mem[s->cursorx+s->cursory*s->sx].style&=~YAS_STORAGE; free(s->mem[s->cursorx+s->cursory*s->sx].p); s->mem[s->cursorx+s->cursory*s->sx].p=0; } *s->mem[s->cursorx+s->cursory*s->sx].d=0; s->mem[s->cursorx+s->cursory*s->sx].style=attr; } s->cursorx++; } return; } if (s->cursorx<0&&s->cursorx+width>=0) { // wide character spanning left bound int x; for (x=0;xcursorx+width;x++) { // zap spanned chars if (s->mem[x+s->cursory*s->sx].style&YAS_STORAGE) { s->mem[x+s->cursory*s->sx].style&=~YAS_STORAGE; free(s->mem[x+s->cursory*s->sx].p); s->mem[x+s->cursory*s->sx].p=0; } strncpy(s->mem[x+s->cursory*s->sx].d,"<",sizeof s->mem[x+s->cursory*s->sx].d); s->mem[x+s->cursory*s->sx].style=attr; s->mem[s->cursory*s->sx].style|=YAS_TOUCHED; } s->cursorx+=width; return; } if (s->cursorx<0||s->cursorx>=s->sx) { // noop for characters out of screen s->cursorx+=width; return; } if (!width&&s->cursorx==0) // nowhere to append - noop return; if (!width&&s->cursorx>0&&s->cursorx<=s->sx) { // combining char, add to previous int clen; char *ts; s->cursorx--; clen=strlen((s->mem[s->cursorx+s->cursory*s->sx].style&YAS_STORAGE)?s->mem[s->cursorx+s->cursory*s->sx].p:s->mem[s->cursorx+s->cursory*s->sx].d); if (clen+strlen(str)mem[s->cursorx+s->cursory*s->sx].d+clen,str,sizeof s->mem[s->cursorx+s->cursory*s->sx].d-clen); s->mem[s->cursorx+s->cursory*s->sx].style=attr; // as a side effect combining chars set attr for main char } else { size_t tslen=clen+strlen(str)+1; ts=malloc(tslen); if (!ts) { s->cursorx++; return; // nothing more we could do } strcpy(ts,(s->mem[s->cursorx+s->cursory*s->sx].style&YAS_STORAGE)?s->mem[s->cursorx+s->cursory*s->sx].p:s->mem[s->cursorx+s->cursory*s->sx].d); strcat(ts,str); if (s->mem[s->cursorx+s->cursory*s->sx].style&YAS_STORAGE) free(s->mem[s->cursorx+s->cursory*s->sx].p); s->mem[s->cursorx+s->cursory*s->sx].p=ts; s->mem[s->cursorx+s->cursory*s->sx].style=attr|YAS_STORAGE; } s->mem[s->cursory*s->sx].style|=YAS_TOUCHED; s->cursorx++; } if (!width) // noop return; if (s->cursorx+width>s->sx) { // wide character spanning right bound int x; for (x=s->cursorx;xsx;x++) { // zap spanned chars if (s->mem[x+s->cursory*s->sx].style&YAS_STORAGE) { s->mem[x+s->cursory*s->sx].style&=~YAS_STORAGE; free(s->mem[x+s->cursory*s->sx].p); s->mem[x+s->cursory*s->sx].p=0; } strncpy(s->mem[x+s->cursory*s->sx].d,">",sizeof s->mem[x+s->cursory*s->sx].d); s->mem[x+s->cursory*s->sx].style=attr; s->mem[s->cursory*s->sx].style|=YAS_TOUCHED; } s->cursorx+=width; return; } return; } // }}} inline int yascreen_putsxy(yascreen *s,int x,int y,uint32_t attr,const char *str) { // {{{ yas_u_state st=U_NORM; char utf[5]; // 4 byte sequence + 1 for terminating 0 size_t i; if (!s) return EOF; if (attr&YAS_INTERNAL) return EOF; s->cursorx=x; // set cursor position to whatever is requested s->cursory=y; if (x>=s->sx||y>=s->sy) { s->cursorx=mymax(0,mymin(s->sx-1,s->cursorx)); // fixup position to be within screen s->cursory=mymax(0,mymin(s->sy-1,s->cursory)); return 1; // somewhat successful print outside bounds } for (i=0;icursorx=mymax(0,mymin(s->sx-1,s->cursorx)); // fixup position to be within screen s->cursory=mymax(0,mymin(s->sy-1,s->cursory)); return 1; } // }}} inline int yascreen_putsxyu(yascreen *s,int x,int y,uint32_t attr,const char *str) { // {{{ int rv,ny; if (!s) return EOF; rv=yascreen_putsxy(s,x,y,attr,str); if (rv==EOF) return rv; ny=s->cursory; yascreen_update_range(s,y,ny+1); return rv; } // }}} inline int yascreen_printxy(yascreen *s,int x,int y,uint32_t attr,const char *format,...) { // {{{ va_list ap; char *ns; int size; int rv; if (!s) return -1; if (!format) return -1; va_start(ap,format); size=vasprintf(&ns,format,ap); va_end(ap); if (size==-1) // some error, nothing more to do return size; rv=yascreen_putsxy(s,x,y,attr,ns); free(ns); return rv; } // }}} inline int yascreen_printxyu(yascreen *s,int x,int y,uint32_t attr,const char *format,...) { // {{{ va_list ap; char *ns; int size; int rv; if (!s) return -1; if (!format) return -1; va_start(ap,format); size=vasprintf(&ns,format,ap); va_end(ap); if (size==-1) // some error, nothing more to do return size; rv=yascreen_putsxyu(s,x,y,attr,ns); free(ns); return rv; } // }}} inline int yascreen_write(yascreen *s,const char *str,int len) { // {{{ ssize_t (*o)(yascreen *s,const void *buf,size_t len); int rv; if (!s) return -1; if (!str) return -1; o=s->outcb?s->outcb:out; rv=o(s,str,len); if (s->lineflush) outs(s,""); // request a flush return rv; } // }}} inline int yascreen_puts(yascreen *s,const char *str) { // {{{ if (!s) return -1; if (!str) return -1; outs(s,str); if (s->lineflush) outs(s,""); // request a flush return 1; } // }}} inline int yascreen_print(yascreen *s,const char *format,...) { // {{{ va_list ap; char *ns; int size; int rv; if (!s) return -1; if (!format) return -1; va_start(ap,format); size=vasprintf(&ns,format,ap); va_end(ap); if (size==-1) // some error, nothing more to do return size; rv=yascreen_puts(s,ns); free(ns); if (s->lineflush) outs(s,""); // request a flush return rv; } // }}} inline const char *yascreen_clearln_s(yascreen *s __attribute__((unused))) { // {{{ return ESC"[2K"; } // }}} inline void yascreen_dump(yascreen *s) { // {{{ int i,j; if (!s) return; printf("PSIZE: %zu\n",PSIZE); for (j=0;jsy;j++) for (i=0;isx;i++) printf("x: %3d y: %3d len: %3zu attr: %08x s: %s\n",i,j,strlen((s->mem[i+s->sx*j].style&YAS_STORAGE)?s->mem[i+s->sx*j].p:s->mem[i+s->sx*j].d),s->mem[i+s->sx*j].style,(s->mem[i+s->sx*j].style&YAS_STORAGE)?s->mem[i+s->sx*j].p:s->mem[i+s->sx*j].d); } // }}} inline void yascreen_redraw(yascreen *s) { // {{{ if (!s) return; s->redraw=1; } // }}} inline void yascreen_cursor(yascreen *s,int on) { // {{{ if (!s) return; s->cursor=!!on; if (on) outs(s,ESC"[?25h"); // show cursor else outs(s,ESC"[?25l"); // hide cursor if (s->lineflush) outs(s,""); // request a flush } // }}} inline void yascreen_cursor_xy(yascreen *s,int x,int y) { // {{{ if (!s) return; s->cursorx=mymin(mymax(x,0),s->sx-1); s->cursory=mymin(mymax(y,0),s->sy-1); outf(s,ESC"[%d;%dH",s->cursory+1,s->cursorx+1); if (s->lineflush) outs(s,""); // request a flush } // }}} inline void yascreen_altbuf(yascreen *s,int on) { // {{{ if (!s) return; if (on) outs(s,ESC"[?1049h"); // go to alternative buffer else outs(s,ESC"[?1049l"); // go back to normal buffer if (s->lineflush) outs(s,""); // request a flush } // }}} inline void yascreen_clear(yascreen *s) { // {{{ if (!s) return; outs(s,ESC"[0m"ESC"[2J"ESC"[H"); // reset attributes, clear screen and reset position if (s->lineflush) outs(s,""); // request a flush } // }}} inline void yascreen_clearln(yascreen *s) { // {{{ if (!s) return; outs(s,yascreen_clearln_s(s)); // clear line if (s->lineflush) outs(s,""); // request a flush } // }}} inline void yascreen_term_save(yascreen *s) { // {{{ if (!s) return; if (s->outcb) return; if (!isatty(STDOUT_FILENO)) return; if (!s->tssize) { // no saved state, allocate new one s->tsstack=(struct termios *)calloc(1,sizeof(struct termios)); if (!s->tsstack) return; s->tssize=1; } tcgetattr(STDOUT_FILENO,s->tsstack); } // }}} inline void yascreen_term_restore(yascreen *s) { // {{{ if (!s) return; if (s->outcb) return; if (!isatty(STDOUT_FILENO)) return; if (!s->tssize) // no saved state return; outs(s,""); // request a flush tcsetattr(STDOUT_FILENO,TCSANOW,s->tsstack); } // }}} inline void yascreen_term_push(yascreen *s) { // {{{ struct termios *t; if (!s) return; if (s->outcb) return; if (!isatty(STDOUT_FILENO)) return; t=(struct termios *)realloc(s->tsstack,(s->tssize+1)*sizeof(struct termios)); if (!t) return; s->tsstack=t; s->tssize++; memmove(s->tsstack+1,s->tsstack,(s->tssize-1)*sizeof(struct termios)); tcgetattr(STDOUT_FILENO,s->tsstack); } // }}} inline void yascreen_term_pop(yascreen *s) { // {{{ if (!s) return; if (s->outcb) return; if (!isatty(STDOUT_FILENO)) return; if (!s->tssize) return; tcsetattr(STDOUT_FILENO,TCSANOW,s->tsstack); if (s->tssize>1) { memmove(s->tsstack,s->tsstack+1,(s->tssize-1)*sizeof(struct termios)); s->tssize--; } } // }}} inline void yascreen_term_set(yascreen *s,int mode) { // {{{ struct termios t; if (!s) return; if (s->outcb) return; if (!isatty(STDOUT_FILENO)) return; // get the terminal state tcgetattr(STDOUT_FILENO,&t); // turn off canonical mode if (mode&YAS_NOBUFF) t.c_lflag&=~(ICANON|IEXTEN); if (mode&YAS_NOSIGN) t.c_lflag&=~(ISIG); if (mode&YAS_NOECHO) t.c_lflag&=~(ECHO); // set input modes t.c_iflag&=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON); // no post processing t.c_oflag&=~(OPOST|ONLCR|OCRNL); if (mode&YAS_ONLCR) t.c_oflag|=ONLCR|OPOST; // 8bit mode t.c_cflag|=CS8; // minimum of number input read. t.c_cc[VMIN]=1; // no timeout t.c_cc[VTIME]=0; tcsetattr(STDOUT_FILENO,TCSANOW,&t); } // }}} inline int yascreen_sx(yascreen *s) { // {{{ if (!s) return -1; return s->sx; } // }}} inline int yascreen_sy(yascreen *s) { // {{{ if (!s) return -1; return s->sy; } // }}} inline int yascreen_x(yascreen *s) { // {{{ if (!s) return -1; return s->cursorx; } // }}} inline int yascreen_y(yascreen *s) { // {{{ if (!s) return -1; return s->cursory; } // }}} inline void yascreen_ckto(yascreen *s) { // {{{ if (!s) return; if (s->state==ST_ESC&&s->ansipos==1&&s->ansibuf[0]==YAS_K_ESC&&s->escto&&s->escts+s->esctoansipos=0; s->state=ST_NORM; yascreen_pushch(s,YAS_K_ESC); } } // }}} inline uint64_t yascreen_willto(yascreen *s) { // {{{ int64_t now; if (!s) return 0; if (s->state!=ST_ESC||s->ansipos!=1||s->ansibuf[0]!=YAS_K_ESC||!s->escto) return 0; // no timeout pending now=mytime(); if (s->escts+s->escto<=now) return 1; // timeout already expired else return s->escts+s->escto-now; } // }}} static inline int yascreen_feed_telnet(yascreen *s,unsigned char c) { // {{{ if (!s) return TELNET_NOOP; switch (s->tstate) { case T_NORM: if (c==TELNET_IAC) { s->tstate=T_IAC; return TELNET_NOOP; } else return c; case T_IAC: switch (c) { // naive, not so RFC compliant telnet protocol handling default: // treat as 2 byte sequence case TELNET_EOSN: // end of subnegotiation case TELNET_NOP: // NOP case TELNET_SYNCH: // SYNCH case TELNET_NVTBRK: // NVT BRK case TELNET_IP: // IP case TELNET_AO: // AO case TELNET_AYT: // AYT are you there case TELNET_EC: // EC case TELNET_EL: // EL case TELNET_GOA: // go ahead s->tstate=T_NORM; return TELNET_NOOP; case TELNET_SOSN: // start of subnegotiation s->sosnpos=0; s->tstate=T_IAC_SB; return TELNET_NOOP; case TELNET_WILL: // will case TELNET_WONT: // wont case TELNET_DO: // do case TELNET_DONT: // dont s->tstate=T_IAC_O; // treat as 3 bytes sequence return TELNET_NOOP; case TELNET_IAC: // escaped 255 s->tstate=T_NORM; return TELNET_IAC; } break; case T_IAC_O: s->tstate=T_NORM; return TELNET_NOOP; case T_IAC_SB: if (c==TELNET_IAC) s->tstate=T_IAC_SE; else if (s->sosnpossosnbuf) s->sosnbuf[s->sosnpos++]=c; return TELNET_NOOP; case T_IAC_SE: switch (c) { case TELNET_EOSN: // try to redetect terminal size s->tstate=T_NORM; if (s->sosnpos==5&&s->sosnbuf[0]==TELNET_NAWS) { // get terminal size from NAWS uint16_t sx,sy; sx=(s->sosnbuf[1]<<8)+s->sosnbuf[2]; sy=(s->sosnbuf[3]<<8)+s->sosnbuf[4]; if (sx>10&&sy>3&&sx<=999&&sy<=999) { // ignore non-sane values s->scrx=sx; s->scry=sy; s->havenaws=1; yascreen_pushch(s,YAS_SCREEN_SIZE); return TELNET_NOOP; } } // fallback to the old way of redetecting screen size via ansi return TELNET_SIZE; case TELNET_IAC: // escaped 255 if (s->sosnpossosnbuf) s->sosnbuf[s->sosnpos++]=c; s->tstate=T_IAC_SB; return TELNET_NOOP; default: // protocol error s->tstate=T_NORM; return TELNET_NOOP; } break; } return TELNET_NOOP; } // }}} #if YASCREEN_VERSIONED inline void yascreen_feed_193(yascreen *s,unsigned char c); inline void yascreen_feed_179(yascreen *s,unsigned char c); #endif static inline int yascreen_getch_to_gen(yascreen *s,int timeout,int key_none) { // {{{ int64_t toms=timeout*1000,tto; struct timeval to,*pto=&to; fd_set r; if (!s) return key_none; memset(&r,0,sizeof r); // make clang static analyzer happier (llvm bug #8920) if (s->outcb) // we do not handle the input, so return immediately timeout=-1; if (timeout==0&&s->escto==0) // wait forever pto=NULL; else if (timeout<0) // return immediately toms=0; tto=s->escto?mymin(s->escto,timeout==0?s->escto:toms):toms; if (toms) toms-=tto; // remaining time to wait is in toms to.tv_sec=tto/1000; to.tv_usec=(tto%1000)*1000; for (;;) { yascreen_ckto(s); // check for esc timeout to return it as a key if (s->keycnt) { // check if we have stored key int key=s->keys[0]; s->keycnt--; memmove(s->keys,s->keys+1,sizeof(int)*s->keycnt); return key; } if (s->outcb) return key_none; if (STDOUT_FILENO<0) return key_none; FD_ZERO(&r); FD_SET(STDOUT_FILENO,&r); if (-1!=select(STDOUT_FILENO+1,&r,NULL,NULL,pto)) { unsigned char c; // important to be unsigned, so codes>127 do not expand as negative int values if (FD_ISSET(STDOUT_FILENO,&r)&&sizeof c==read(STDOUT_FILENO,&c,sizeof c)) { #if YASCREEN_VERSIONED if (key_none==YAS_K_NONE) // default behaviour, new symbols yascreen_feed_193(s,c); else yascreen_feed_179(s,c); #else yascreen_feed(s,c); #endif continue; // check if feed has yielded a key } } if (pto&&(timeout>0||s->escto)&&to.tv_sec==0&&to.tv_usec==0) { // return because of timeout if (timeout<0) // nowait is set return key_none; if (!toms&&timeout>0) // timeout is finished return key_none; if (!toms) tto=s->escto; else tto=s->escto?mymin(s->escto,toms):toms; if (toms) toms-=tto; // remaining time to wait is in toms to.tv_sec=tto/1000; to.tv_usec=(tto%1000)*1000; } } } // }}} symver_d(yascreen_getch_to_193,yascreen_getch_to,YASCREEN_1.93) // {{{ inline int V(yascreen_getch_to,V193)(yascreen *s,int timeout) { return yascreen_getch_to_gen(s,timeout,YAS_K_NONE); } // }}} inline void yascreen_ungetch(yascreen *s,int key) { // {{{ int *tk; if (!s) return; if (s->keysize<=s->keycnt) { // need to reallocate key storage int newsize=s->keysize+KEYSTEP; tk=(int *)realloc(s->keys,sizeof(int)*newsize); if (!tk) return; s->keys=tk; s->keysize=newsize; } memmove(s->keys+1,s->keys,sizeof(int)*s->keycnt); s->keys[0]=key; s->keycnt++; } // }}} inline void yascreen_pushch(yascreen *s,int key) { // {{{ int *tk; if (!s) return; if (s->keysize<=s->keycnt) { // need to reallocate key storage int newsize=s->keysize+KEYSTEP; tk=(int *)realloc(s->keys,sizeof(int)*newsize); if (!tk) return; s->keys=tk; s->keysize=newsize; } s->keys[s->keycnt++]=key; } // }}} inline void yascreen_esc_to(yascreen *s,int timeout) { // {{{ if (!s) return; s->escto=(timeout>=0)?timeout:YAS_DEFAULT_ESCTO; } // }}} symver_d(yascreen_peekch_193,yascreen_peekch,YASCREEN_1.93) // {{{ inline int V(yascreen_peekch,V193)(yascreen *s) { int ch=yascreen_getch_nowait(s); if (ch!=YAS_K_NONE) yascreen_ungetch(s,ch); return ch; } // }}} inline void yascreen_clear_mem(yascreen *s,uint32_t attr) { // {{{ int i; if (!s) return; attr&=~YAS_STORAGE; for (i=0;isx*s->sy;i++) { if (s->mem[i].style&YAS_STORAGE) free(s->mem[i].p); s->mem[i].style=attr; strncpy(s->mem[i].d," ",sizeof s->mem[i].d); } } // }}} inline void yascreen_getsize(yascreen *s,int *sx,int *sy) { // {{{ if (!s) return; if (sx) *sx=s->scrx; if (sy) *sy=s->scry; } // }}} inline void yascreen_reqsize(yascreen *s) { // {{{ outs(s,ESC"[s"ESC"[999;999H"ESC"[6n"ESC"[u"); outs(s,""); // request a flush } // }}} inline void yascreen_line_flush(yascreen *s,int on) { // {{{ if (!s) return; s->lineflush=!!on; } // }}} inline wchar_t yascreen_getwch_to(yascreen *s,int timeout) { // {{{ int ch; if (!s) return YAS_K_NONE; if (!s->isunicode) return YAS_K_NONE; ch=yascreen_getch_to(s,timeout); if (YAS_IS_CC(ch)) return ch; if (ch>=0&&ch<=0x7f) return ch; if ((ch&0xe0)==0xc0) { // 2 byte seq // unicode mode guarantees that we have a valid sequence unsigned char c2=yascreen_getch_nowait(s); wchar_t w; w=((ch&0x1f)<<6)|(c2&0x3f); return w; } if ((ch&0xf0)==0xe0) { // 3 byte seq // unicode mode guarantees that we have a valid sequence unsigned char c2=yascreen_getch_nowait(s); unsigned char c3=yascreen_getch_nowait(s); wchar_t w; w=((ch&0x0f)<<12)|((c2&0x3f)<<6)|(c3&0x3f); return w; } if ((ch&0xf8)==0xf0) { // 4 byte seq // unicode mode guarantees that we have a valid sequence unsigned char c2=yascreen_getch_nowait(s); unsigned char c3=yascreen_getch_nowait(s); unsigned char c4=yascreen_getch_nowait(s); wchar_t w; w=((ch&0x07)<<18)|((c2&0x3f)<<12)|((c3&0x3f)<<6)|(c4&0x3f); return w; } //if ((ch&0xfc)==0xf8) { // 5 byte seq //} //if ((str[i]&0xfe)==0xfc) { // 6 byte seq //} return YAS_K_NONE; } // }}} inline void yascreen_ungetwch(yascreen *s,wchar_t key) { // {{{ if (!s) return; if (!s->isunicode) return; if (YAS_IS_CC(key)) yascreen_ungetch(s,key); else { char ns[MB_CUR_MAX+1]; int rc=wctomb(ns,key); int i; if (rc<=0) return; for (i=rc-1;i>0;i--) yascreen_ungetch(s,ns[i]); } } // }}} inline wchar_t yascreen_peekwch(yascreen *s) { // {{{ wchar_t ch; if (!s) return YAS_K_NONE; if (!s->isunicode) return YAS_K_NONE; ch=yascreen_getwch_nowait(s); if (ch!=YAS_K_NONE) yascreen_ungetwch(s,ch); return ch; } // }}} // get yascreen_feed with the new symbol or unversioned #include "yascreen_feed.c" #if YASCREEN_VERSIONED // redefine YAS_K_* {{{ #undef YAS_K_ALT #define YAS_K_ALT(code) (((code)&0xff)|0x200) #define YAS_K_NONE -1 // no key available // extended keys send as escape sequences // function keys with ALT/CTRL/SHIFT #define YAS_K_F1 0x100 #define YAS_K_F2 0x101 #define YAS_K_F3 0x102 #define YAS_K_F4 0x103 #define YAS_K_F5 0x104 #define YAS_K_F6 0x105 #define YAS_K_F7 0x106 #define YAS_K_F8 0x107 #define YAS_K_F9 0x108 #define YAS_K_F10 0x109 #define YAS_K_F11 0x10a #define YAS_K_F12 0x10b #define YAS_K_S_F1 0x10c #define YAS_K_S_F2 0x10d #define YAS_K_S_F3 0x10e #define YAS_K_S_F4 0x10f #define YAS_K_S_F5 0x110 #define YAS_K_S_F6 0x111 #define YAS_K_S_F7 0x112 #define YAS_K_S_F8 0x113 #define YAS_K_S_F9 0x114 #define YAS_K_S_F10 0x115 #define YAS_K_S_F11 0x116 #define YAS_K_S_F12 0x117 #define YAS_K_C_F1 0x118 #define YAS_K_C_F2 0x119 #define YAS_K_C_F3 0x11a #define YAS_K_C_F4 0x11b #define YAS_K_C_F5 0x11c #define YAS_K_C_F6 0x11d #define YAS_K_C_F7 0x11e #define YAS_K_C_F8 0x11f #define YAS_K_C_F9 0x120 #define YAS_K_C_F10 0x121 #define YAS_K_C_F11 0x122 #define YAS_K_C_F12 0x123 #define YAS_K_A_F1 0x124 #define YAS_K_A_F2 0x125 #define YAS_K_A_F3 0x126 #define YAS_K_A_F4 0x127 #define YAS_K_A_F5 0x128 #define YAS_K_A_F6 0x129 #define YAS_K_A_F7 0x12a #define YAS_K_A_F8 0x12b #define YAS_K_A_F9 0x12c #define YAS_K_A_F10 0x12d #define YAS_K_A_F11 0x12e #define YAS_K_A_F12 0x12f #define YAS_K_LEFT 0x130 #define YAS_K_UP 0x131 #define YAS_K_DOWN 0x132 #define YAS_K_RIGHT 0x133 #define YAS_K_HOME 0x134 #define YAS_K_END 0x135 #define YAS_K_PGUP 0x136 #define YAS_K_PGDN 0x137 #define YAS_K_INS 0x138 #define YAS_K_DEL 0x139 #define YAS_K_C_LEFT 0x13a #define YAS_K_C_UP 0x13b #define YAS_K_C_DOWN 0x13c #define YAS_K_C_RIGHT 0x13d // ALT+letter #define YAS_K_A_BT YAS_K_ALT('`') #define YAS_K_A_1 YAS_K_ALT('1') #define YAS_K_A_2 YAS_K_ALT('2') #define YAS_K_A_3 YAS_K_ALT('3') #define YAS_K_A_4 YAS_K_ALT('4') #define YAS_K_A_5 YAS_K_ALT('5') #define YAS_K_A_6 YAS_K_ALT('6') #define YAS_K_A_7 YAS_K_ALT('7') #define YAS_K_A_8 YAS_K_ALT('8') #define YAS_K_A_9 YAS_K_ALT('9') #define YAS_K_A_0 YAS_K_ALT('0') #define YAS_K_A_MINUS YAS_K_ALT('-') #define YAS_K_A_EQ YAS_K_ALT(' ') #define YAS_K_A_BSP YAS_K_ALT(0x7f) #define YAS_K_A_TLD YAS_K_ALT('~') #define YAS_K_A_EXCL YAS_K_ALT('!') #define YAS_K_A_AT YAS_K_ALT('@') #define YAS_K_A_HASH YAS_K_ALT('#') #define YAS_K_A_POUND YAS_K_ALT('$') #define YAS_K_A_PERC YAS_K_ALT('%') #define YAS_K_A_CARRET YAS_K_ALT('^') #define YAS_K_A_AND YAS_K_ALT('&') #define YAS_K_A_STAR YAS_K_ALT('*') #define YAS_K_A_OBRA YAS_K_ALT('(') #define YAS_K_A_CBRA YAS_K_ALT(')') #define YAS_K_A_UND YAS_K_ALT('_') #define YAS_K_A_PLUS YAS_K_ALT('+') #define YAS_K_A_a YAS_K_ALT('a') #define YAS_K_A_b YAS_K_ALT('b') #define YAS_K_A_c YAS_K_ALT('c') #define YAS_K_A_d YAS_K_ALT('d') #define YAS_K_A_e YAS_K_ALT('e') #define YAS_K_A_f YAS_K_ALT('f') #define YAS_K_A_g YAS_K_ALT('g') #define YAS_K_A_h YAS_K_ALT('h') #define YAS_K_A_i YAS_K_ALT('i') #define YAS_K_A_j YAS_K_ALT('j') #define YAS_K_A_k YAS_K_ALT('k') #define YAS_K_A_l YAS_K_ALT('l') #define YAS_K_A_m YAS_K_ALT('m') #define YAS_K_A_n YAS_K_ALT('n') #define YAS_K_A_o YAS_K_ALT('o') #define YAS_K_A_p YAS_K_ALT('p') #define YAS_K_A_q YAS_K_ALT('q') #define YAS_K_A_r YAS_K_ALT('r') #define YAS_K_A_s YAS_K_ALT('s') #define YAS_K_A_t YAS_K_ALT('t') #define YAS_K_A_u YAS_K_ALT('u') #define YAS_K_A_v YAS_K_ALT('v') #define YAS_K_A_w YAS_K_ALT('w') #define YAS_K_A_x YAS_K_ALT('x') #define YAS_K_A_y YAS_K_ALT('y') #define YAS_K_A_z YAS_K_ALT('z') #define YAS_SCREEN_SIZE 0x800 #define YAS_TELNET_SIZE 0x801 // }}} // {{{ symver_o(yascreen_getch_to_179,yascreen_getch_to,YASCREEN_1.79) // {{{ inline int yascreen_getch_to_179(yascreen *s,int timeout) { return yascreen_getch_to_gen(s,timeout,YAS_K_NONE); } // }}} symver_o(yascreen_peekch_179,yascreen_peekch,YASCREEN_1.79) // {{{ inline int yascreen_peekch_179(yascreen *s) { int ch=yascreen_getch_to_179(s,-1); if (ch!=YAS_K_NONE) yascreen_ungetch(s,ch); return ch; } // }}} // now get yascreen_feed old version #undef symver_V #define symver_V(impl,sym,ver) symver_o(impl,sym,ver) #undef V193 #define V193 _179 #undef YASCREEN_193 #define YASCREEN_193 YASCREEN_1.79 #include "yascreen_feed.c" #endif vfu-5.09/yascreen/yascreen.vers0000644000175000017500000000253214444676575015174 0ustar cadecadeYASCREEN_1.79 { global: yascreen_clearln; yascreen_print; yascreen_clear; yascreen_puts; yascreen_set_hint_i; yascreen_altbuf; yascreen_ckto; yascreen_getch_to; yascreen_putsxyu; yascreen_term_pop; yascreen_getsize; yascreen_esc_to; yascreen_set_hint_p; yascreen_free; yascreen_printxy; yascreen_get_hint_i; yascreen_cursor_xy; yascreen_write; yascreen_init; yascreen_ver; yascreen_resize; yascreen_peekch; yascreen_redraw; yascreen_term_save; yascreen_update_attr; yascreen_dump; yascreen_get_hint_p; yascreen_setout; yascreen_pushch; yascreen_update; yascreen_clearln_s; yascreen_init_telnet; yascreen_set_telnet; yascreen_printxyu; yascreen_feed; yascreen_reqsize; yascreen_term_push; yascreen_term_restore; yascreen_term_set; yascreen_putsxy; yascreen_cursor; yascreen_x; yascreen_clear_mem; yascreen_sx; yascreen_sy; yascreen_y; yascreen_ungetch; local: *; }; YASCREEN_1.83 { global: # newly added yascreen_line_flush; } YASCREEN_1.79; YASCREEN_1.93 { global: # different constants, compatible with unicode yascreen_getch_to; yascreen_peekch; yascreen_feed; # newly added yascreen_set_unicode; yascreen_getwch_to; yascreen_ungetwch; yascreen_peekwch; } YASCREEN_1.83; YASCREEN_1.97 { global: # newly added yascreen_willto; } YASCREEN_1.93; vfu-5.09/yascreen/yascreen.h0000644000175000017500000003375614444676575014460 0ustar cadecade// $Id: yascreen.h,v 1.49 2023/02/16 04:40:29 bbonev Exp $ // // Copyright © 2015-2023 Boian Bonev (bbonev@ipacct.com) {{{ // // SPDX-License-Identifer: LGPL-3.0-or-later // // This file is part of yascreen - yet another screen library. // // yascreen is free software, released under the terms of GNU Lesser General Public License v3.0 or later // }}} #ifndef ___YASCREEN_H___ #define ___YASCREEN_H___ #include #include #include #ifdef __cplusplus extern "C" { #endif // extract colors from combined style attribute #define YAS_FG(s) ((s)&0x1ff) #define YAS_BG(s) (((s)>>9)&0x1ff) // bit masks for different styles #define YAS_ITALIC (1u<<(2*9+0)) #define YAS_UNDERL (1u<<(2*9+1)) #define YAS_STRIKE (1u<<(2*9+2)) #define YAS_INVERSE (1u<<(2*9+3)) #define YAS_BOLD (1u<<(2*9+4)) #define YAS_BLINK (1u<<(2*9+5)) // values representing default terminal's colors #define YAS_FGCOLORDEF 0 #define YAS_BGCOLORDEF 0 // construnct simple colors to be ored into attribute #define YAS_FGCOLOR(c) ((((c)&0x7)|8)<<0) #define YAS_BGCOLOR(c) ((((c)&0x7)|8)<<9) // construnct 256 extended colors to be ored into attribute #define YAS_FGXCOLOR(c) ((((c)&0xff)|0x100)<<0) #define YAS_BGXCOLOR(c) ((((c)&0xff)|0x100)<<9) // simple color table #define YAS_BLACK 0 #define YAS_RED 1 #define YAS_GREEN 2 #define YAS_YELLOW 3 #define YAS_BLUE 4 #define YAS_MAGENTA 5 #define YAS_CYAN 6 #define YAS_WHITE 7 // input modes #define YAS_NOBUFF 1 #define YAS_NOSIGN 2 #define YAS_NOECHO 4 #define YAS_ONLCR 8 #define YAS_K_ALT(code) (((code)&0xff)+0xf0101) #define YAS_IS_CC(code) ((code)>=0xf0000&&(code)<=0xffffd) // key codes // first part are 0x00-0x7f and these map directly to ASCII characters, also 1:1 in wchar_t // second part are also directly representable in wchar_t, in the 0xf0000-0xffffd reserved range typedef enum { YAS_K_NONE=0xf0000, // no key available YAS_K_NUL=0x00, YAS_K_C_A=0x01, YAS_K_C_B=0x02, YAS_K_C_C=0x03, YAS_K_C_D=0x04, YAS_K_C_E=0x05, YAS_K_C_F=0x06, YAS_K_C_G=0x07, YAS_K_C_H=0x08, YAS_K_C_I=0x09, YAS_K_TAB=0x09, YAS_K_C_J=0x0a, YAS_K_C_K=0x0b, YAS_K_C_L=0x0c, YAS_K_C_M=0x0d, YAS_K_RET=0x0d, YAS_K_C_N=0x0e, YAS_K_C_O=0x0f, YAS_K_C_P=0x10, YAS_K_C_Q=0x11, YAS_K_C_R=0x12, YAS_K_C_S=0x13, YAS_K_C_T=0x14, YAS_K_C_U=0x15, YAS_K_C_V=0x16, YAS_K_C_W=0x17, YAS_K_C_X=0x18, YAS_K_C_Y=0x19, YAS_K_C_Z=0x1a, YAS_K_ESC=0x1b, YAS_K_C_3=0x1b, YAS_K_C_4=0x1c, YAS_K_C_5=0x1d, YAS_K_C_6=0x1e, YAS_K_C_7=0x1f, YAS_K_SPACE=0x20, // ' ' YAS_K_EXCL=0x21, // '!' YAS_K_DQUOT=0x22, // '"' YAS_K_HASH=0x23, // '#' YAS_K_POUND=0x24, // '$' YAS_K_PERC=0x25, // '%' YAS_K_AND=0x26, // '&' YAS_K_QUOT=0x27, // '\'' YAS_K_OBRA=0x28, // '(' YAS_K_CBRA=0x29, // ')' YAS_K_STAR=0x2a, // '*' YAS_K_PLUS=0x2b, // '+' YAS_K_COMMA=0x2c, // ',' YAS_K_MINUS=0x2d, // '-' YAS_K_DOT=0x2e, // '.' YAS_K_SLASH=0x2f, // '/' YAS_K_0=0x30, // '0' YAS_K_1=0x31, // '1' YAS_K_2=0x32, // '2' YAS_K_3=0x33, // '3' YAS_K_4=0x34, // '4' YAS_K_5=0x35, // '5' YAS_K_6=0x36, // '6' YAS_K_7=0x37, // '7' YAS_K_8=0x38, // '8' YAS_K_9=0x39, // '9' YAS_K_COLON=0x3a, // ':' YAS_K_SEMI=0x3b, // ';' YAS_K_LT=0x3c, // '<' YAS_K_EQ=0x3d, // '=' YAS_K_GT=0x3e, // '>' YAS_K_QUEST=0x3f, // '?' YAS_K_AT=0x40, // '@' YAS_K_A=0x41, // 'A' YAS_K_B=0x42, // 'B' YAS_K_C=0x43, // 'C' YAS_K_D=0x44, // 'D' YAS_K_E=0x45, // 'E' YAS_K_F=0x46, // 'F' YAS_K_G=0x47, // 'G' YAS_K_H=0x48, // 'H' YAS_K_I=0x49, // 'I' YAS_K_J=0x4a, // 'J' YAS_K_K=0x4b, // 'K' YAS_K_L=0x4c, // 'L' YAS_K_M=0x4d, // 'M' YAS_K_N=0x4e, // 'N' YAS_K_O=0x4f, // 'O' YAS_K_P=0x50, // 'P' YAS_K_Q=0x51, // 'Q' YAS_K_R=0x52, // 'R' YAS_K_S=0x53, // 'S' YAS_K_T=0x54, // 'T' YAS_K_U=0x55, // 'U' YAS_K_V=0x56, // 'V' YAS_K_W=0x57, // 'W' YAS_K_X=0x58, // 'X' YAS_K_Y=0x59, // 'Y' YAS_K_Z=0x5a, // 'Z' YAS_K_OSQ=0x5b, // '[' YAS_K_BSLASH=0x5c, // '\\' YAS_K_CSQ=0x5d, // ']' YAS_K_CARRET=0x5e, // '^' YAS_K_USCORE=0x5f, // '_' YAS_K_BTICK=0x60, // '`' YAS_K_a=0x61, // 'a' YAS_K_b=0x62, // 'b' YAS_K_c=0x63, // 'c' YAS_K_d=0x64, // 'd' YAS_K_e=0x65, // 'e' YAS_K_f=0x66, // 'f' YAS_K_g=0x67, // 'g' YAS_K_h=0x68, // 'h' YAS_K_i=0x69, // 'i' YAS_K_j=0x6a, // 'j' YAS_K_k=0x6b, // 'k' YAS_K_l=0x6c, // 'l' YAS_K_m=0x6d, // 'm' YAS_K_n=0x6e, // 'n' YAS_K_o=0x6f, // 'o' YAS_K_p=0x70, // 'p' YAS_K_q=0x71, // 'q' YAS_K_r=0x72, // 'r' YAS_K_s=0x73, // 's' YAS_K_t=0x74, // 't' YAS_K_u=0x75, // 'u' YAS_K_v=0x76, // 'v' YAS_K_w=0x77, // 'w' YAS_K_x=0x78, // 'x' YAS_K_y=0x79, // 'y' YAS_K_z=0x7a, // 'z' YAS_K_OCUR=0x7b, // '{' YAS_K_PIPE=0x7c, // '|' YAS_K_CCUR=0x7d, // '}' YAS_K_TLD=0x7e, // '~' YAS_K_C_8=0x7f, YAS_K_BSP=0x7f, // extended keys, send as escape sequences // function keys with ALT/CTRL/SHIFT YAS_K_F1=0xf0001, YAS_K_F2=0xf0002, YAS_K_F3=0xf0003, YAS_K_F4=0xf0004, YAS_K_F5=0xf0005, YAS_K_F6=0xf0006, YAS_K_F7=0xf0007, YAS_K_F8=0xf0008, YAS_K_F9=0xf0009, YAS_K_F10=0xf000a, YAS_K_F11=0xf000b, YAS_K_F12=0xf000c, YAS_K_S_F1=0xf000d, YAS_K_S_F2=0xf000e, YAS_K_S_F3=0xf000f, YAS_K_S_F4=0xf0010, YAS_K_S_F5=0xf0011, YAS_K_S_F6=0xf0012, YAS_K_S_F7=0xf0013, YAS_K_S_F8=0xf0014, YAS_K_S_F9=0xf0015, YAS_K_S_F10=0xf0016, YAS_K_S_F11=0xf0017, YAS_K_S_F12=0xf0018, YAS_K_C_F1=0xf0019, YAS_K_C_F2=0xf001a, YAS_K_C_F3=0xf001b, YAS_K_C_F4=0xf001c, YAS_K_C_F5=0xf001d, YAS_K_C_F6=0xf001e, YAS_K_C_F7=0xf001f, YAS_K_C_F8=0xf0020, YAS_K_C_F9=0xf0021, YAS_K_C_F10=0xf0022, YAS_K_C_F11=0xf0023, YAS_K_C_F12=0xf0024, YAS_K_A_F1=0xf0025, YAS_K_A_F2=0xf0026, YAS_K_A_F3=0xf0027, YAS_K_A_F4=0xf0028, YAS_K_A_F5=0xf0029, YAS_K_A_F6=0xf002a, YAS_K_A_F7=0xf002b, YAS_K_A_F8=0xf002c, YAS_K_A_F9=0xf002d, YAS_K_A_F10=0xf002e, YAS_K_A_F11=0xf002f, YAS_K_A_F12=0xf0030, YAS_K_LEFT=0xf0031, YAS_K_UP=0xf0032, YAS_K_DOWN=0xf0033, YAS_K_RIGHT=0xf0034, YAS_K_HOME=0xf0035, YAS_K_END=0xf0036, YAS_K_PGUP=0xf0037, YAS_K_PGDN=0xf0038, YAS_K_INS=0xf0039, YAS_K_DEL=0xf003a, YAS_K_C_LEFT=0xf003b, YAS_K_C_UP=0xf003c, YAS_K_C_DOWN=0xf003d, YAS_K_C_RIGHT=0xf003e, YAS_K_S_LEFT=0xf003f, YAS_K_S_UP=0xf0040, YAS_K_S_DOWN=0xf0041, YAS_K_S_RIGHT=0xf0042, // ALT+letter YAS_K_A_BT=YAS_K_ALT('`'), YAS_K_A_1=YAS_K_ALT('1'), YAS_K_A_2=YAS_K_ALT('2'), YAS_K_A_3=YAS_K_ALT('3'), YAS_K_A_4=YAS_K_ALT('4'), YAS_K_A_5=YAS_K_ALT('5'), YAS_K_A_6=YAS_K_ALT('6'), YAS_K_A_7=YAS_K_ALT('7'), YAS_K_A_8=YAS_K_ALT('8'), YAS_K_A_9=YAS_K_ALT('9'), YAS_K_A_0=YAS_K_ALT('0'), YAS_K_A_MINUS=YAS_K_ALT('-'), YAS_K_A_EQ=YAS_K_ALT('='), YAS_K_A_BSP=YAS_K_ALT(0x7f), YAS_K_A_TLD=YAS_K_ALT('~'), YAS_K_A_EXCL=YAS_K_ALT('!'), YAS_K_A_AT=YAS_K_ALT('@'), YAS_K_A_HASH=YAS_K_ALT('#'), YAS_K_A_POUND=YAS_K_ALT('$'), YAS_K_A_PERC=YAS_K_ALT('%'), YAS_K_A_CARRET=YAS_K_ALT('^'), YAS_K_A_AND=YAS_K_ALT('&'), YAS_K_A_STAR=YAS_K_ALT('*'), YAS_K_A_OBRA=YAS_K_ALT('('), YAS_K_A_CBRA=YAS_K_ALT(')'), YAS_K_A_UND=YAS_K_ALT('_'), YAS_K_A_PLUS=YAS_K_ALT('+'), YAS_K_A_a=YAS_K_ALT('a'), YAS_K_A_b=YAS_K_ALT('b'), YAS_K_A_c=YAS_K_ALT('c'), YAS_K_A_d=YAS_K_ALT('d'), YAS_K_A_e=YAS_K_ALT('e'), YAS_K_A_f=YAS_K_ALT('f'), YAS_K_A_g=YAS_K_ALT('g'), YAS_K_A_h=YAS_K_ALT('h'), YAS_K_A_i=YAS_K_ALT('i'), YAS_K_A_j=YAS_K_ALT('j'), YAS_K_A_k=YAS_K_ALT('k'), YAS_K_A_l=YAS_K_ALT('l'), YAS_K_A_m=YAS_K_ALT('m'), YAS_K_A_n=YAS_K_ALT('n'), YAS_K_A_o=YAS_K_ALT('o'), YAS_K_A_p=YAS_K_ALT('p'), YAS_K_A_q=YAS_K_ALT('q'), YAS_K_A_r=YAS_K_ALT('r'), YAS_K_A_s=YAS_K_ALT('s'), YAS_K_A_t=YAS_K_ALT('t'), YAS_K_A_u=YAS_K_ALT('u'), YAS_K_A_v=YAS_K_ALT('v'), YAS_K_A_w=YAS_K_ALT('w'), YAS_K_A_x=YAS_K_ALT('x'), YAS_K_A_y=YAS_K_ALT('y'), YAS_K_A_z=YAS_K_ALT('z'), YAS_SCREEN_SIZE=0xf0701, YAS_TELNET_SIZE=0xf0702, } yas_keys; struct _yascreen; typedef struct _yascreen yascreen; // allocate and initialize screen data // output defaults to stdout inline yascreen *yascreen_init(int sx,int sy); // get library version as static string inline const char *yascreen_ver(void); // change output; if output is NULL, default is to stdout inline int yascreen_setout(yascreen *s,ssize_t (*out)(yascreen *s,const void *data,size_t len)); // enable/disable handling of unicode input (enabled by default) inline void yascreen_set_unicode(yascreen *s,int on); // enable/disable handling of telnet protocol (disabled by default) inline void yascreen_set_telnet(yascreen *s,int on); // init remote telnet client inline void yascreen_init_telnet(yascreen *s); // resize screen; should redraw afterwards // since allocation is involved, this may fail and return -1 inline int yascreen_resize(yascreen *s,int sx,int sy); // free screen data inline void yascreen_free(yascreen *s); // save current terminal state on top of state stack inline void yascreen_term_save(yascreen *s); // restore previously saved terminal state from top of state stack inline void yascreen_term_restore(yascreen *s); // push current terminal state to state stack inline void yascreen_term_push(yascreen *s); // pop and restore previously saved terminal state from state stack inline void yascreen_term_pop(yascreen *s); // set terminal for proper screen operation inline void yascreen_term_set(yascreen *s,int mode); // print at position, if data exceeds buffer, then it gets truncated inline int yascreen_printxy(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6))); inline int yascreen_putsxy(yascreen *s,int x,int y,uint32_t attr,const char *str); // print at position, if data exceeds buffer, then it gets truncated; and update immediately inline int yascreen_printxyu(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6))); inline int yascreen_putsxyu(yascreen *s,int x,int y,uint32_t attr,const char *str); // sync memory state to screen // since allocation is involved, this may fail and return -1 inline int yascreen_update(yascreen *s); // set next update to be a full redraw inline void yascreen_redraw(yascreen *s); // clear memory buffer inline void yascreen_clear_mem(yascreen *s,uint32_t attr); // calls suitable for line mode and init of fullscreen mode // hide or show cusror; screen is updated immediately inline void yascreen_cursor(yascreen *s,int on); // set cursor position; screen is updated immediately inline void yascreen_cursor_xy(yascreen *s,int x,int y); // switch between regular and alternative buffer; screen is updated immediately inline void yascreen_altbuf(yascreen *s,int on); // clear real screen, no change to memory buffers inline void yascreen_clear(yascreen *s); // clear current line, no change to memory buffers inline void yascreen_clearln(yascreen *s); // apply difference between two attrs inline void yascreen_update_attr(yascreen *s,uint32_t oattr,uint32_t nattr); // reset all attrs and set specific one #define yascreen_set_attr(s,attr) yascreen_update_attr(s,0xffffffff,attr) // print in line mode inline int yascreen_print(yascreen *s,const char *format,...) __attribute__((format(printf,2,3))); inline int yascreen_write(yascreen *s,const char *str,int len); inline int yascreen_puts(yascreen *s,const char *str); // set if the above three calls should flush inline void yascreen_line_flush(yascreen *s,int on); // returns an escape sequence to clear line inline const char *yascreen_clearln_s(yascreen *s); // get current x size inline int yascreen_sx(yascreen *s); // get current y size inline int yascreen_sy(yascreen *s); // get current x inline int yascreen_x(yascreen *s); // get current y inline int yascreen_y(yascreen *s); // keyboard input // set timeout in milliseconds for single ESC key press inline void yascreen_esc_to(yascreen *s,int timeout); // in case of external event loop, this call will check for single ESC key // should be called regularly enough so that the above specified timeout is not extended too much // if not called often enough then single ESC will be yielded after longer timeout // if not called at all then single ESC will be yielded with next key press inline void yascreen_ckto(yascreen *s); // single ESC key and a timeout afterwards is quite the rare event // help the application to optimize its event loop by not constantly polling yascreen_ckto // yascreen_willto tells if there is a pending timeout and when // returns 0 if there is no pending timeout or the remaining time in milliseconds inline uint64_t yascreen_willto(yascreen *s); // wait for a key, return ASCII or extended keycode, wait no more than timeout in milliseconds inline int yascreen_getch_to(yascreen *s,int timeout); // zero timeout=wait forever #define yascreen_getch(s) yascreen_getch_to(s,0) // negative timeout=do not wait #define yascreen_getch_nowait(s) yascreen_getch_to(s,-1) // put back key value in key buffer inline void yascreen_ungetch(yascreen *s,int key); // push key value at end of key buffer inline void yascreen_pushch(yascreen *s,int key); // feed key sequence state machine with byte stream // this is useful to implement external event loop and // read key codes by yascreen_getch_nowait until it returns -1 inline void yascreen_feed(yascreen *s,unsigned char c); // peek for key without removing it from input queue inline int yascreen_peekch(yascreen *s); // get last reported screen size; set both to 0 if there is none // this will yield valid result after YAS_SCREEN_SIZE is returned as keypress inline void yascreen_getsize(yascreen *s,int *sx,int *sy); // request terminal to report its size inline void yascreen_reqsize(yascreen *s); // wchar_t input // wait for a key, return wchar_t or extended keycode in 0xf0000-0xffffd range // wait no more than timeout in milliseconds inline wchar_t yascreen_getwch_to(yascreen *s,int timeout); // zero timeout=wait forever #define yascreen_getwch(s) yascreen_getwch_to(s,0) // negative timeout=do not wait #define yascreen_getwch_nowait(s) yascreen_getwch_to(s,-1) // put back key value in key buffer inline void yascreen_ungetwch(yascreen *s,wchar_t key); // peek for key without removing it from input queue inline wchar_t yascreen_peekwch(yascreen *s); // hints api inline void yascreen_set_hint_i(yascreen *s,int hint); inline int yascreen_get_hint_i(yascreen *s); inline void yascreen_set_hint_p(yascreen *s,void *hint); inline void *yascreen_get_hint_p(yascreen *s); #ifdef __cplusplus } #endif #endif vfu-5.09/yascreen/yascreen.pc.in0000644000175000017500000000036414145574104015204 0ustar cadecadeprefix=YASCREENPREFIX exec_prefix=YASCREENPREFIX libdir=YASCREENLIBDIR includedir=YASCREENINCDIR Name: libyascreen Description: Yet Another Screen Library Version: YASCREENVERSION Requires: Cflags: -I${includedir} Libs: -L${libdir} -lyascreen vfu-5.09/yascreen/yastest.c0000644000175000017500000000713114145574104014301 0ustar cadecade#include #include #include #include #include #include #include #include #define SIZEX 20 #define SIZEY 20 inline void yascreen_dump(yascreen *s); int main(void) { //uint32_t s1=YAS_ITALIC|YAS_FGCOLOR(1)|YAS_BGCOLOR(2); // red on green italic //uint32_t s2=YAS_UNDERL|YAS_FGCOLOR(2)|YAS_BGCOLOR(1); // green on red under //uint32_t s3=YAS_FGCOLORDEF|YAS_BGCOLORDEF; // default colors uint32_t s4=YAS_FGCOLOR(YAS_BLACK)|YAS_BGCOLOR(YAS_WHITE)|YAS_BLINK|YAS_ITALIC|YAS_BOLD; // distinct colors int i; // int j; int ccnt=0; int chrs[SIZEY]; //FILE *f=fopen("/dev/null","w"); yascreen *s=yascreen_init(SIZEX,SIZEY); setlocale(LC_ALL,"C.UTF-8"); if (!s) { printf("couldn't init screen %dx%d\n",SIZEX,SIZEY); return 0; } #if 0 // test 1 yascreen_cursor(s,0); printf("style: %08x fg: %d bg: %d\n",s1,YAS_FG(s1),YAS_BG(s1)); yascreen_putsxy(s,0,0,s1,"abcdef日̀本aaaa"); yascreen_putsxy(s,1,3,s2,"±µя̀́и́̀̂a̴̴̷̠̰̐"); yascreen_putsxy(s,5,3,s1,"ź;wllllccc"); //yascreen_dump(s); yascreen_update(s); sleep(1); yascreen_putsxy(s,0,1,s2,"abcdef日̀本aaaa"); yascreen_putsxy(s,1,4,s1,"±µя̀́и́̀̂a̴̴̷̠̰̐"); yascreen_putsxy(s,5,4,s2,"ź;wllllccc"); yascreen_update(s); sleep(1); yascreen_putsxy(s,0,2,s3,"abcdef日̀本aaaa"); yascreen_putsxy(s,1,5,s3,"±µя̀́и́̀̂a̴̴̷̠̰̐"); yascreen_putsxy(s,5,5,s3,"ź;wllllccc"); yascreen_update(s); sleep(1); yascreen_putsxy(s,0,0,s3," "); yascreen_putsxy(s,0,1,s3," "); yascreen_putsxy(s,0,2,s3," "); yascreen_putsxy(s,0,3,s3," "); yascreen_putsxy(s,0,4,s3," "); yascreen_putsxy(s,0,5,s3," "); yascreen_update(s); sleep(1); for (i=0;i<9;i++) for (j=0;j<9;j++) { uint32_t st=YAS_FGCOLOR(i)|YAS_BGCOLOR(j); if (i==8||j==8) st=0; yascreen_putsxy(s,i*2,j,st,"日̀"); } yascreen_putsxy(s,0,9,0,"0123456789012345678901234567890"); yascreen_update(s); sleep(2); yascreen_cursor(s,1); #endif #if 0 // test 2 yascreen_putsxy(s,5,5,50,"abc"); yascreen_update(s); sleep(1); yascreen_putsxy(s,6,5,50,"B"); yascreen_update(s); #endif #if 1 // test 3 yascreen_term_set(s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO); yascreen_cursor(s,0); yascreen_altbuf(s,1); yascreen_clear(s); yascreen_printxy(s,0,0,YAS_BLINK,"press 'q' to quit%*s",SIZEY,""); yascreen_printxy(s,0,1,YAS_BLINK,"press 't' to perform test%*s",SIZEY,""); yascreen_update(s); //yascreen_ungetch(s,'1'); //yascreen_ungetch(s,'2'); //yascreen_ungetch(s,'3'); //yascreen_pushch(s,'a'); //yascreen_pushch(s,'b'); //yascreen_pushch(s,'c'); for (;;) { int ch; ch=yascreen_getch(s); switch (ch) { case 't': // test for reuse yascreen_clear(s); yascreen_cursor(s,1); yascreen_altbuf(s,0); yascreen_term_restore(s); sleep(2); yascreen_term_set(s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO); yascreen_cursor(s,0); yascreen_altbuf(s,1); yascreen_redraw(s); yascreen_update(s); break; case 'q': // exit yascreen_clear(s); yascreen_cursor(s,1); yascreen_altbuf(s,0); yascreen_term_restore(s); yascreen_free(s); printf("sizeof wchar_t: %zu\n",sizeof(wchar_t)); return 0; default: if (ccnt/dev/null)" ]; then echo gmake is required; false; fi @gmake --no-print-directory -f GNUmakefile $@ .PHONY: all vfu-5.09/yascreen/LICENSE0000644000175000017500000001674414145574104013460 0ustar cadecade GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. vfu-5.09/yascreen/Makefile.main0000644000175000017500000001060114444676575015041 0ustar cadecade############################# configurable section ############################# # # Copyright © 2015-2023 Boian Bonev (bbonev@ipacct.com) {{{ # # SPDX-License-Identifer: LGPL-3.0-or-later # # This file is part of yascreen - yet another screen library. # # yascreen is free software, released under the terms of GNU Lesser General Public License v3.0 or later # # }}} # configure install path PREFIX?=/usr/local LIBDIR?=/lib/ INCDIR?=/include/ # configure debug or release build # for debug build, uncomment the line below #DEBUG?=-DDEBUG=1 -O0 -g3 -fno-inline -fstack-protector-all DEBUG?=-O3 # configure default tools # make defines AR, so we have to override to gcc-ar, so -flto works AR=gcc-ar RANLIB?=gcc-ranlib INSTALL?=install ########################## end of configurable section ######################### VER=$(shell grep Revision yascreen.c|head -n1|sed -e 's/.\+Revision: \([0-9.]\+\) \+.\+/\1/'|tr . ' '|awk '{printf "%i.%02u\n",$$1+$$2/100,$$2%100}') # shared library version SOVERM:=0 SOVERF:=0.0.0 SONAME:=-Wl,-soname,libyascreen.so.$(SOVERM) ifeq ($(shell uname -s),Darwin) # tested on 17.7 AR=ar RANLIB=ranlib NO_VERSIONED:=1 NO_FLTO:=1 SONAME:= endif ifeq ($(shell uname -s),FreeBSD) # tested on 14.0 AR=ar RANLIB=ranlib endif ifeq ($(shell uname -s),OpenBSD) # tested on 7.2 AR=ar RANLIB=ranlib NO_VERSIONED:=1 endif ifeq ($(shell uname -s),NetBSD) # tested on 9.3 AR=ar RANLIB=ranlib NO_VERSIONED:=1 NO_FLTO:=1 endif ifndef NO_FLTO CHECK_CC_FLAGS:=-Wall -Wextra -I. --std=gnu89 -fPIC -flto=auto else CHECK_CC_FLAGS:=-Wall -Wextra -I. --std=gnu89 -fPIC endif # remove unsupported flags from CCOPT define check_cc_flag $(shell echo 'int main(){return 0;}'|$(CC) $(1) -xc - -o /dev/null 2>/dev/null && echo $(1)) endef HAVE_FLAGS+=$(foreach flag,$(CHECK_CC_FLAGS),$(call check_cc_flag,$(flag))) CCOPT:=$(HAVE_FLAGS) # build versioned library by default ifndef NO_VERSIONED CCOPT+=-DYASCREEN_VERSIONED=1 LDOPT:=-Wl,--version-script,yascreen.vers else CCOPT+=-DYASCREEN_VERSIONED=0 endif # allow to pass additional compiler flags MYCFLAGS=$(DEBUG) $(CPPFLAGS) $(CFLAGS) $(CCOPT) MYLDFLAGS=$(LDFLAGS) $(LDOPT) all: libyascreen.a libyascreen.so yascreen.pc yascreen.o: yascreen.c yascreen.h yascreen_feed.c $(CC) $(MYCFLAGS) -o $@ -c $< yastest.o: yastest.c yascreen.h $(CC) $(MYCFLAGS) -o $@ -c $< yastest: yastest.o yascreen.o $(CC) $(MYCFLAGS) -o $@ $^ yastest.shared: yastest.o libyascreen.so $(CC) $(MYCFLAGS) -o $@ $^ -L. -lyascreen libyascreen.a: yascreen.o $(AR) r $@ $^ $(RANLIB) $@ libyascreen.so: libyascreen.so.$(SOVERM) ln -fs $^ $@ libyascreen.so.$(SOVERM): libyascreen.so.$(SOVERF) ln -fs $^ $@ libyascreen.so.$(SOVERF): yascreen.o yascreen.vers $(CC) $(MYCFLAGS) $(MYLDFLAGS) -o $@ $< -fPIC -shared $(SONAME) yascreen.pc: yascreen.pc.in sed \ -e 's|YASCREENVERSION|$(VER)|' \ -e 's|YASCREENPREFIX|$(PREFIX)|' \ -e 's|YASCREENLIBDIR|$(PREFIX)$(LIBDIR)|' \ -e 's|YASCREENINCDIR|$(PREFIX)$(INCDIR)|' \ < $< > $@ install: libyascreen.a libyascreen.so yascreen.pc yascreen.3 $(INSTALL) -Ds -m 0644 -t $(DESTDIR)$(PREFIX)$(LIBDIR) libyascreen.a $(INSTALL) -D -m 0644 -t $(DESTDIR)$(PREFIX)$(LIBDIR)/pkgconfig/ yascreen.pc ln -fs libyascreen.so.$(SOVERF) $(DESTDIR)$(PREFIX)$(LIBDIR)libyascreen.so.$(SOVERM) ln -fs libyascreen.so.$(SOVERM) $(DESTDIR)$(PREFIX)$(LIBDIR)libyascreen.so $(INSTALL) -Ds -m 0644 -t $(DESTDIR)$(PREFIX)$(LIBDIR) libyascreen.so.$(SOVERF) $(INSTALL) -D -m 0644 -t $(DESTDIR)$(PREFIX)$(INCDIR) yascreen.h $(INSTALL) -D -m 0644 yascreen.3 $(DESTDIR)$(PREFIX)/share/man/man3/yascreen.3 clean: rm -f yastest yastest.shared yastest.o yascreen.o libyascreen.a libyascreen.so libyascreen.so.$(SOVERM) libyascreen.so.$(SOVERF) yascreen.pc re: rebuild rebuild: $(MAKE) clean $(MAKE) -j all mkotar: $(MAKE) clean dh_clean $(MAKE) yascreen.3 tar \ --xform 's,^[.],yascreen-$(VER),' \ --exclude ./.git \ --exclude ./.github/CVS \ --exclude ./.github/workflows/CVS \ --exclude ./.gitignore \ --exclude ./.cvsignore \ --exclude ./CVS \ --exclude ./debian \ --exclude ./fedora/CVS \ -Jcvf ../yascreen_$(VER).orig.tar.xz . -rm -f ../yascreen_$(VER).orig.tar.xz.asc gpg -a --detach-sign ../yascreen_$(VER).orig.tar.xz cp -fa ../yascreen_$(VER).orig.tar.xz ../yascreen-$(VER).tar.xz cp -fa ../yascreen_$(VER).orig.tar.xz.asc ../yascreen-$(VER).tar.xz.asc yascreen.3: README.md go-md2man < README.md > yascreen.3 .PHONY: install clean rebuild re all mkotar vfu-5.09/yascreen/yascreen_feed.c0000644000175000017500000004164714371545160015414 0ustar cadecade// $Id: yascreen_feed.c,v 1.4 2023/02/03 23:20:18 bbonev Exp $ symver_V(V(yascreen_feed,V193),yascreen_feed,YASCREEN_193) // {{{ inline void V(yascreen_feed,V193)(yascreen *s,unsigned char c) { if (!s) return; yascreen_ckto(s); if (s->istelnet) { // process telnet codes int tc=yascreen_feed_telnet(s,c); switch (tc) { case 0x00 ... 0xff: // normal character c=(unsigned char)tc; break; default: case TELNET_NOOP: // byte is eaten w/o valid input return; case TELNET_SIZE: // notify about screen size change yascreen_pushch(s,YAS_TELNET_SIZE); return; } } switch (s->state) { case ST_ENTER: if (c=='\n'||c==0) // ignore LF or NUL after CR break; s->state=ST_NORM; // fall through case ST_NORM: if (c==YAS_K_ESC) { // handle esc sequences s->escts=mytime(); s->ansipos=1; s->ansibuf[0]=c; s->state=ST_ESC; } else { // handle standard keys if (c=='\r') // shift state to ST_ENTER to eat optional LF/NUL after CR s->state=ST_ENTER; if (!s->isunicode) { // do not process unicode sequences, push the byte as-is yascreen_pushch(s,c); break; } switch (s->ustate) { case U_NORM: if (c&0x80) { if ((c&0xc0)==0x80) // unexpected continuation byte - ignore break; startbyte: if ((c&0xe0)==0xc0) { // 2 byte seq s->utf[0]=c; s->ustate=U_L2C1; break; } if ((c&0xf0)==0xe0) { // 3 byte seq s->utf[0]=c; s->ustate=U_L3C1; break; } if ((c&0xf8)==0xf0) { // 4 byte seq s->utf[0]=c; s->ustate=U_L4C1; break; } if ((c&0xfc)==0xf8) { // 5 byte seq //s->utf[0]=c; s->ustate=U_L5C1; break; } if ((c&0xfe)==0xfc) { // 6 byte seq //s->utf[0]=c; s->ustate=U_L6C1; break; } // pass 0xff and 0xfe - violates rfc yascreen_pushch(s,c); s->ustate=U_NORM; // in case we come from unexpected start byte } else yascreen_pushch(s,c); break; case U_L2C1: if ((c&0xc0)==0x80) { // continuation byte yascreen_pushch(s,s->utf[0]); yascreen_pushch(s,c); s->ustate=U_NORM; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L3C1: if ((c&0xc0)==0x80) { // continuation byte s->utf[1]=c; s->ustate=U_L3C2; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L3C2: if ((c&0xc0)==0x80) { // continuation byte yascreen_pushch(s,s->utf[0]); yascreen_pushch(s,s->utf[1]); yascreen_pushch(s,c); s->ustate=U_NORM; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L4C1: if ((c&0xc0)==0x80) { // continuation byte s->utf[1]=c; s->ustate=U_L4C2; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L4C2: if ((c&0xc0)==0x80) { // continuation byte s->utf[2]=c; s->ustate=U_L4C3; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L4C3: if ((c&0xc0)==0x80) { // continuation byte yascreen_pushch(s,s->utf[0]); yascreen_pushch(s,s->utf[1]); yascreen_pushch(s,s->utf[2]); yascreen_pushch(s,c); s->ustate=U_NORM; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L5C1: if ((c&0xc0)==0x80) { // continuation byte //s->utf[1]=c; s->ustate=U_L5C2; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L5C2: if ((c&0xc0)==0x80) { // continuation byte //s->utf[2]=c; s->ustate=U_L5C3; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L5C3: if ((c&0xc0)==0x80) { // continuation byte //s->utf[3]=c; s->ustate=U_L5C4; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L5C4: if ((c&0xc0)==0x80) { // continuation byte //yascreen_pushch(s,s->utf[0]); // sequence is parsed but ignored //yascreen_pushch(s,s->utf[1]); //yascreen_pushch(s,s->utf[2]); //yascreen_pushch(s,s->utf[3]); //yascreen_pushch(s,c); s->ustate=U_NORM; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L6C1: if ((c&0xc0)==0x80) { // continuation byte //s->utf[1]=c; s->ustate=U_L6C2; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L6C2: if ((c&0xc0)==0x80) { // continuation byte //s->utf[2]=c; s->ustate=U_L6C3; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L6C3: if ((c&0xc0)==0x80) { // continuation byte //s->utf[3]=c; s->ustate=U_L6C4; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L6C4: if ((c&0xc0)==0x80) { // continuation byte //s->utf[3]=c; s->ustate=U_L6C5; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; case U_L6C5: if ((c&0xc0)==0x80) { // continuation byte //yascreen_pushch(s,s->utf[0]); // sequence is parsed but ignored //yascreen_pushch(s,s->utf[1]); //yascreen_pushch(s,s->utf[2]); //yascreen_pushch(s,s->utf[3]); //yascreen_pushch(s,s->utf[4]); //yascreen_pushch(s,c); s->ustate=U_NORM; break; } if (c&0x80) // start another sequence goto startbyte; s->ustate=U_NORM; // normal byte kills current sequence and is processed yascreen_pushch(s,c); break; } } break; case ST_ESC: switch (c) { case '`': case '-': case '=': case 0x7f: case '~': case '!': case '@': case '#': case '$': case '%': case '^': case '&': case '*': case '(': case ')': case '_': case '+': case ':': case ';': case '"': case '\'': case '{': case '}': case '|': case '\\': case ',': case '.': case '/': case '<': case '>': case '?': case '0'...'9': case 'a'...'z': yascreen_pushch(s,YAS_K_ALT(c)); s->state=ST_NORM; break; case '[': s->ansibuf[s->ansipos++]=c; s->state=ST_ESC_SQ; break; case 'O': s->ansibuf[s->ansipos++]=c; s->state=ST_ESC_O; break; default: // ignore unknown sequence s->state=ST_NORM; break; } break; case ST_ESC_SQ: switch (c) { case 'A': // up yascreen_pushch(s,YAS_K_UP); s->state=ST_NORM; break; case 'B': // down yascreen_pushch(s,YAS_K_DOWN); s->state=ST_NORM; break; case 'C': // right yascreen_pushch(s,YAS_K_RIGHT); s->state=ST_NORM; break; case 'D': // left yascreen_pushch(s,YAS_K_LEFT); s->state=ST_NORM; break; case 'H': // home yascreen_pushch(s,YAS_K_HOME); s->state=ST_NORM; break; case 'F': // end yascreen_pushch(s,YAS_K_END); s->state=ST_NORM; break; case '0'...'9': s->state=ST_ESC_SQ_D; s->ansibuf[s->ansipos++]=c; break; default: // ignore unknown sequence s->state=ST_NORM; break; } break; case ST_ESC_SQ_D: if (s->ansipos>=sizeof s->ansibuf) { // buffer overrun, ignore the sequence s->state=ST_NORM; break; } s->ansibuf[s->ansipos++]=c; if (c>=0x40&&c<=0x7e) { // final char s->state=ST_NORM; s->ansibuf[s->ansipos]=0; switch (c) { case '~': // 0x7e if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='1') // F1 - \e[11~ yascreen_pushch(s,YAS_K_F1); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='2') // F2 - \e[12~ yascreen_pushch(s,YAS_K_F2); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='3') // F3 - \e[13~ yascreen_pushch(s,YAS_K_F3); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='4') // F4 - \e[14~ yascreen_pushch(s,YAS_K_F4); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='5') // F5 - \e[15~ yascreen_pushch(s,YAS_K_F5); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='7') // F6 - \e[17~ yascreen_pushch(s,YAS_K_F6); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='8') // F7 - \e[18~ yascreen_pushch(s,YAS_K_F7); if (s->ansipos==5&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='9') // F8 - \e[19~ yascreen_pushch(s,YAS_K_F8); if (s->ansipos==5&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='0') // F9 - \e[20~ yascreen_pushch(s,YAS_K_F9); if (s->ansipos==5&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='1') // F10 - \e[21~ yascreen_pushch(s,YAS_K_F10); if (s->ansipos==5&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='3') // F11 - \e[23~ yascreen_pushch(s,YAS_K_F11); if (s->ansipos==5&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='4') // F12 - \e[24~ yascreen_pushch(s,YAS_K_F12); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='1'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F1 \e[11;2~ yascreen_pushch(s,YAS_K_S_F1); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='2'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F2 \e[12;2~ yascreen_pushch(s,YAS_K_S_F2); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='3'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F3 \e[13;2~ yascreen_pushch(s,YAS_K_S_F3); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='4'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F4 \e[14;2~ yascreen_pushch(s,YAS_K_S_F4); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='5'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F5 \e[15;2~ yascreen_pushch(s,YAS_K_S_F5); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='7'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F6 \e[17;2~ yascreen_pushch(s,YAS_K_S_F6); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='8'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F7 \e[18;2~ yascreen_pushch(s,YAS_K_S_F7); if (s->ansipos==7&&s->ansibuf[2]=='1'&&s->ansibuf[3]=='9'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F8 \e[19;2~ yascreen_pushch(s,YAS_K_S_F8); if (s->ansipos==7&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='0'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F9 \e[20;2~ yascreen_pushch(s,YAS_K_S_F9); if (s->ansipos==7&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='1'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F10 \e[21;2~ yascreen_pushch(s,YAS_K_S_F10); if (s->ansipos==7&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='3'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F11 \e[23;2~ yascreen_pushch(s,YAS_K_S_F11); if (s->ansipos==7&&s->ansibuf[2]=='2'&&s->ansibuf[3]=='4'&&s->ansibuf[4]==';'&&s->ansibuf[5]=='2') // shift-F12 \e[24;2~ yascreen_pushch(s,YAS_K_S_F12); if (s->ansipos==4&&s->ansibuf[2]=='2') // insert - \e[2~ yascreen_pushch(s,YAS_K_INS); if (s->ansipos==4&&s->ansibuf[2]=='3') // delete - \e[3~ yascreen_pushch(s,YAS_K_DEL); if (s->ansipos==4&&s->ansibuf[2]=='5') // pgup - \e[5~ yascreen_pushch(s,YAS_K_PGUP); if (s->ansipos==4&&s->ansibuf[2]=='6') // pgdn - \e[6~ yascreen_pushch(s,YAS_K_PGDN); if (s->ansipos==4&&(s->ansibuf[2]=='1'||s->ansibuf[2]=='7')) // home - \e[1~ \e[7~ yascreen_pushch(s,YAS_K_HOME); if (s->ansipos==4&&(s->ansibuf[2]=='4'||s->ansibuf[2]=='8')) // end - \e[4~ \e[8~ yascreen_pushch(s,YAS_K_END); break; case 'P': // \e[1;2P - shift-F1 if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-F1 - \e[1;2P yascreen_pushch(s,YAS_K_S_F1); break; case 'Q': // \e[1;2Q - shift-F2 if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-F2 - \e[1;2Q yascreen_pushch(s,YAS_K_S_F2); break; case 'R': { // \e[n;mR - cursor position report, used for screen size detection int sx,sy; sscanf((char *)s->ansibuf+2,"%d;%dR",&sy,&sx); if (sx>10&&sy>3&&sx<=999&&sy<=999) { // ignore non-sane values s->scrx=sx; s->scry=sy; s->haveansi=1; yascreen_pushch(s,YAS_SCREEN_SIZE); } else if (!strcmp((char *)s->ansibuf+2,"1;2R")) // shift-F3 - \e[1;2R yascreen_pushch(s,YAS_K_S_F3); break; } case 'S': // \e[1;2S - shift-F4 if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-F4 - \e[1;2S yascreen_pushch(s,YAS_K_S_F4); break; case 'A': if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') // ^up - \e[1;5A yascreen_pushch(s,YAS_K_C_UP); if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-up - \e[1;2A yascreen_pushch(s,YAS_K_S_UP); break; case 'B': if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') // ^down - \e[1;5B yascreen_pushch(s,YAS_K_C_DOWN); if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-down - \e[1;2B yascreen_pushch(s,YAS_K_S_DOWN); break; case 'C': if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') // ^right - \e[1;5C yascreen_pushch(s,YAS_K_C_RIGHT); if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-right - \e[1;2C yascreen_pushch(s,YAS_K_S_RIGHT); break; case 'D': if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') // ^left - \e[1;5D yascreen_pushch(s,YAS_K_C_LEFT); if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='2') // shift-left - \e[1;2D yascreen_pushch(s,YAS_K_S_LEFT); break; } } break; case ST_ESC_O: switch (c) { case 'P': // F1 \eOP yascreen_pushch(s,YAS_K_F1); break; case 'Q': // F2 \eOQ yascreen_pushch(s,YAS_K_F2); break; case 'R': // F3 \eOR yascreen_pushch(s,YAS_K_F3); break; case 'S': // F4 \eOS yascreen_pushch(s,YAS_K_F4); break; case 'w': // F5 \eOw yascreen_pushch(s,YAS_K_F5); break; case 'x': // F6 \eOx yascreen_pushch(s,YAS_K_F6); break; case 'y': // F7 \eOy yascreen_pushch(s,YAS_K_F7); break; case 'm': // F8 \eOm yascreen_pushch(s,YAS_K_F8); break; case 't': // F9 \eOt yascreen_pushch(s,YAS_K_F9); break; case 'u': // F10 \eOu yascreen_pushch(s,YAS_K_F10); break; case 'v': // F11 \eOv yascreen_pushch(s,YAS_K_F11); break; case 'l': // F12 \eOl yascreen_pushch(s,YAS_K_F12); break; case 'H': // home \eOH yascreen_pushch(s,YAS_K_HOME); break; case 'F': // end \eOF yascreen_pushch(s,YAS_K_END); break; case 'a': // ^up \eOa yascreen_pushch(s,YAS_K_C_UP); break; case 'b': // ^down \eOb yascreen_pushch(s,YAS_K_C_DOWN); break; case 'c': // ^right \eOc yascreen_pushch(s,YAS_K_C_RIGHT); break; case 'd': // ^left \eOd yascreen_pushch(s,YAS_K_C_LEFT); break; } s->state=ST_NORM; break; } } // }}} vfu-5.09/yascreen/README.md0000644000175000017500000007014414365574235013735 0ustar cadecade# YASCREEN 3 "September 30, 2020" yascreen "User-Manual" # NAME yascreen - Yet Another Screen Library (curses replacement for daemons and embedded apps) # SYNOPSIS `#include ` # DESCRIPTION ## Main features - small footprint - does not have external dependencies - allows both internal and external event loop - allows stdin/stdout or external input/output (can work over socket) - supports basic set of telnet sequences, making it suitable for built-in terminal interfaces for daemons - supports a limited set of input keystroke sequences - fully unicode compatible (parts of this depend on wcwidth in libc) - supports utf8 verification of input - supports utf8 input and wide character input - supports non-utf8 input mode - relies only on a limited subset of ansi/xterm ESC sequences, making it compatible with mostly all modern terminals (inspired by [linenoise](https://github.com/antirez/linenoise)) - there is no curses API and ancient terminal compatibility, hence less bloat - there is no autoconf - there is no need to have one - clean API with opaque private data, usable from C/C++ - easy cross compilation setup (by setting CC, AR, STRIP and RANLIB) Current development is done on Linux, with additional testing on OpenBSD/FreeBSD; other platforms may need minimal fixes. On \*BSD a `gmake` is required to build. ## Architecture yascreen uses an opaque data structure, allocated by the library and dynamically resized when needed - `yascreen_init(int sx, int sy)` / `yascreen_resize(yascreen *s, int sx, int sy)`. An application may specify (0,0) for both calls to let yascreen detect the size or use a fixed size. There are two modes of operation - telnet protocol over socket or running in terminal. For sockets the event loop would typically be handled outside of the library while for terminals a built-in event loop may be used. Modes of operation can be modified at runtime. For terminal use signal handling (`SIGWINCH`) should always be handled by the application. ## Example initialization for terminal and handling of SIGWINCH ```c yascreen *s; int winch=0; void sigwinch(int sign) { winch=1; } s=yascreen_init(0,0); // let yascreen get term size yascreen_term_set(s,YAS_NOBUFF|YAS_NOSIGN|YAS_NOECHO); signal(SIGWINCH,sigwinch); for (;;) { // main loop if (winch) { winch=0; if (yascreen_resize(s,0,0)) // handle a fatal error - no memory // get the new sizes and redraw newsizex=yascreen_sx(s); newsizey=yascreen_sy(s); } … // option 1 // input is handled in external event loop and fed to yascreen via yascreen_feed if (FD_ISSET(STDIN_FILENO,&r)&&sizeof c==read(STDIN_FILENO,&c,sizeof c)) yascreen_feed(s,c); // pump state machine with bytestream // keys are processed only when available without delay/blocking while ((ch=yascreen_getch_nowait(s))!=-1) { // handle processed keys } … // option 2 // input is handled by yascreen and key or -1 is returned not longer than TIMEOUT ms // note: if screen update is based on this, keypresses will force it while ((ch=yascreen_getch_to(s,TIMEOUT))!=-1) { // handle processed keys } … // option 3 // input is handled by yascreen and the following call will block until a key is pressed if ((ch=yascreen_getch(s))!=-1) { // handle processed key } } ``` For sockets input is handled like option 1 in the example above and yascreen needs to be provided with a callback for output. In multiprocess mode daemons where `stdin`/`stdout` are redirected to a socket the same model from the example above can be used. Obviously SIGWINCH will work only for terminals and for sockets the screen size can get to be known either via telnet or ASNI sequences. ## Example initialization for socket with external event loop and telnet sequences processing ```c yascreen *s; s=yascreen_init(80,25); // there is no guarantee that screen size detection is supported on the remote end yascreen_setout(s,output_cb); // set callback for output yascreen_set_telnet(s,1); // enable processing of telnet sequences yascreen_init_telnet(s); // try to negotiate telnet options (regardless if telnet processing is enabled) yascreen_reqsize(s); // request initial screen size for (;;) { // main loop … yascreen_feed(s,c); // feed input from the socket to yascreen // keys are processed only when available without delay/blocking while ((ch=yascreen_getch_nowait(s))!=-1) { // handle processed keys // screen size change is reported as a special keypress code: if (ch==YAS_TELNET_SIZE||ch==YAS_SCREEN_SIZE) // screen size change reported via telnet or ANSI sequence // redraw } } ``` # API Reference ## Predefined constants and Helper macros Internally style is kept into bitfields in a single integer variable - that includes foreground/background colors, style modifiers (bold, italic, underline, inverse and blink. ### Style codes | Name | Function | |-------------|-----------| |`YAS_ITALIC` | italic | |`YAS_UNDERL` | underline | |`YAS_STRIKE` | stikeout | |`YAS_INVERSE`| inverse | |`YAS_BOLD` | bold | |`YAS_BLINK` | blink | ### Color codes | Name | Color | |----------------|-----------------------------| |`YAS_BLACK` | black | |`YAS_RED` | red | |`YAS_GREEN` | green | |`YAS_YELLOW` | yellow | |`YAS_BLUE` | blue | |`YAS_MAGENTA` | magenta | |`YAS_CYAN` | cyan | |`YAS_WHITE` | white | |`YAS_FGCOLORDEF`| default terminal foreground | |`YAS_BGCOLORDEF`| default terminal background | ### Helper macros | Name | Description | |----------------------|-----------------------------------------------------| |`YAS_FG(attribute)` | extract foreground color | |`YAS_BG(attribute)` | extract background color | |`YAS_FGCOLOR(color)` | shift simple color value into foreground color | |`YAS_BGCOLOR(color)` | shift simple color value into background color | |`YAS_FGXCOLOR(color)` | shift 256 palette color value into foreground color | |`YAS_BGXCOLOR(color)` | shift 256 palette color value into background color | All of the above can be or'ed into attribute, provided that the bits for foreground/background color are all zeroes. ### Key codes - Special, generated internally Previous versions of the library used -1 and 0x100+ for these codes. In order to achieve unicode wide character compatibility and simpler API, the reserved Unicode range 0xf0000-0xffffd is used for the special codes both in narrow and wide character input modes. There is a macro `YAS_IS_CC(code)` that will evaluate to non-zero for the special codes and to zero for normal characters. Note that all ASCII control characters in the range 0x00-0x7f are treated as normal ones. | Name | Value | Description | |------------------|--------:|-----------------------| |`YAS_K_NONE` | 0xf0000 | no key is available; in time limited mode means that the time limit expired | |`YAS_SCREEN_SIZE` | 0xf0701 | notification for screen size change (may come because of telnet or ANSI sequence) | |`YAS_TELNET_SIZE` | 0xf0702 | notification for screen size change; duplicates the above, may be used to differentiate how screen size change event was generated | - Normal keys | Name | Value | Description | |------------------|------:|-----------------------| |`YAS_K_NUL` | 0x00 | Nul; on some terminals Ctrl-2 | |`YAS_K_C_A` | 0x01 | Ctrl-A | |`YAS_K_C_B` | 0x02 | Ctrl-B | |`YAS_K_C_C` | 0x03 | Ctrl-C | |`YAS_K_C_D` | 0x04 | Ctrl-D | |`YAS_K_C_E` | 0x05 | Ctrl-E | |`YAS_K_C_F` | 0x06 | Ctrl-F | |`YAS_K_C_G` | 0x07 | Ctrl-G | |`YAS_K_C_H` | 0x08 | Ctrl-H; depends on terminal see YAS-K-BSP and YAS-K-C-8 | |`YAS_K_C_I` | 0x09 | Ctrl-I | |`YAS_K_TAB` | 0x09 | Tab | |`YAS_K_C_J` | 0x0a | Ctrl-J | |`YAS_K_C_K` | 0x0b | Ctrl-K | |`YAS_K_C_L` | 0x0c | Ctrl-L | |`YAS_K_C_M` | 0x0d | Enter, Return, Ctrl-M; see below | |`YAS_K_RET` | 0x0d | Enter, Return, Ctrl-M; see above | |`YAS_K_C_N` | 0x0e | Ctrl-N | |`YAS_K_C_O` | 0x0f | Ctrl-O | |`YAS_K_C_P` | 0x10 | Ctrl-O | |`YAS_K_C_Q` | 0x11 | Ctrl-Q | |`YAS_K_C_R` | 0x12 | Ctrl-R | |`YAS_K_C_S` | 0x13 | Ctrl-S | |`YAS_K_C_T` | 0x14 | Ctrl-T | |`YAS_K_C_U` | 0x15 | Ctrl-U | |`YAS_K_C_V` | 0x16 | Ctrl-V | |`YAS_K_C_W` | 0x17 | Ctrl-W | |`YAS_K_C_X` | 0x18 | Ctrl-X | |`YAS_K_C_Y` | 0x19 | Ctrl-Y | |`YAS_K_C_Z` | 0x1a | Ctrl-Z | |`YAS_K_ESC` | 0x1b | Esc, Ctrl-3; see below; All ANSI sequences start with Esc, this is returned after a timeout or double Esc | |`YAS_K_C_3` | 0x1b | Esc, Ctrl-3; see above; All ANSI sequences start with Esc, this is returned after a timeout or double Esc | |`YAS_K_C_4` | 0x1c | Ctrl-4 | |`YAS_K_C_5` | 0x1d | Ctrl-5 | |`YAS_K_C_6` | 0x1e | Ctrl-6 | |`YAS_K_C_7` | 0x1f | Ctrl-7 | |`YAS_K_SPACE` | 0x20 | Space | |`YAS_K_EXCL` | 0x21 | ! | |`YAS_K_DQUOT` | 0x22 | " | |`YAS_K_HASH` | 0x23 | # | |`YAS_K_POUND` | 0x24 | $ | |`YAS_K_PERC` | 0x25 | % | |`YAS_K_AND` | 0x26 | Ampersand | |`YAS_K_QUOT` | 0x27 | ' | |`YAS_K_OBRA` | 0x28 | ( | |`YAS_K_CBRA` | 0x29 | ) | |`YAS_K_STAR` | 0x2a | * | |`YAS_K_PLUS` | 0x2b | + | |`YAS_K_COMMA` | 0x2c | , | |`YAS_K_MINUS` | 0x2d | - | |`YAS_K_DOT` | 0x2e | . | |`YAS_K_SLASH` | 0x2f | / | |`YAS_K_0` | 0x30 | 0 | |`YAS_K_1` | 0x31 | 1 | |`YAS_K_2` | 0x32 | 2 | |`YAS_K_3` | 0x33 | 3 | |`YAS_K_4` | 0x34 | 4 | |`YAS_K_5` | 0x35 | 5 | |`YAS_K_6` | 0x36 | 6 | |`YAS_K_7` | 0x37 | 7 | |`YAS_K_8` | 0x38 | 8 | |`YAS_K_9` | 0x39 | 9 | |`YAS_K_COLON` | 0x3a | : | |`YAS_K_SEMI` | 0x3b | ; | |`YAS_K_LT` | 0x3c | < | |`YAS_K_EQ` | 0x3d | Equal | |`YAS_K_GT` | 0x3e | > | |`YAS_K_QUEST` | 0x3f | ? | |`YAS_K_AT` | 0x40 | @ | |`YAS_K_A` | 0x41 | A | |`YAS_K_B` | 0x42 | B | |`YAS_K_C` | 0x43 | C | |`YAS_K_D` | 0x44 | D | |`YAS_K_E` | 0x45 | E | |`YAS_K_F` | 0x46 | F | |`YAS_K_G` | 0x47 | G | |`YAS_K_H` | 0x48 | H | |`YAS_K_I` | 0x49 | I | |`YAS_K_J` | 0x4a | J | |`YAS_K_K` | 0x4b | K | |`YAS_K_L` | 0x4c | L | |`YAS_K_M` | 0x4d | M | |`YAS_K_N` | 0x4e | N | |`YAS_K_O` | 0x4f | O | |`YAS_K_P` | 0x50 | P | |`YAS_K_Q` | 0x51 | Q | |`YAS_K_R` | 0x52 | R | |`YAS_K_S` | 0x53 | S | |`YAS_K_T` | 0x54 | T | |`YAS_K_U` | 0x55 | U | |`YAS_K_V` | 0x56 | V | |`YAS_K_W` | 0x57 | W | |`YAS_K_X` | 0x58 | X | |`YAS_K_Y` | 0x59 | Y | |`YAS_K_Z` | 0x5a | Z | |`YAS_K_OSQ` | 0x5b | OpenSquareBracket | |`YAS_K_BSLASH` | 0x5c | Backslash | |`YAS_K_CSQ` | 0x5d | CloseSquareBracket | |`YAS_K_CARRET` | 0x5e | ^ | |`YAS_K_USCORE` | 0x5f | Underscore | |`YAS_K_BTICK` | 0x60 | Backtick | |`YAS_K_a` | 0x61 | a | |`YAS_K_b` | 0x62 | b | |`YAS_K_c` | 0x63 | c | |`YAS_K_d` | 0x64 | d | |`YAS_K_e` | 0x65 | e | |`YAS_K_f` | 0x66 | f | |`YAS_K_g` | 0x67 | g | |`YAS_K_h` | 0x68 | h | |`YAS_K_i` | 0x69 | i | |`YAS_K_j` | 0x6a | j | |`YAS_K_k` | 0x6b | k | |`YAS_K_l` | 0x6c | l | |`YAS_K_m` | 0x6d | m | |`YAS_K_n` | 0x6e | n | |`YAS_K_o` | 0x6f | o | |`YAS_K_p` | 0x70 | p | |`YAS_K_q` | 0x71 | q | |`YAS_K_r` | 0x72 | r | |`YAS_K_s` | 0x73 | s | |`YAS_K_t` | 0x74 | t | |`YAS_K_u` | 0x75 | u | |`YAS_K_v` | 0x76 | v | |`YAS_K_w` | 0x77 | w | |`YAS_K_x` | 0x78 | x | |`YAS_K_y` | 0x79 | y | |`YAS_K_z` | 0x7a | z | |`YAS_K_OCUR` | 0x7b | { | |`YAS_K_PIPE` | 0x7c | Pipe | |`YAS_K_CCUR` | 0x7d | } | |`YAS_K_TLD` | 0x7e | Tilde | |`YAS_K_C_8` | 0x7f | Backspace; see below; depends on terminal see YAS-K-C-H | |`YAS_K_BSP` | 0x7f | Backspace; see below; depends on terminal see YAS-K-C-H | - Extended keys, parsed from ANSI sequences | Name | Value | Description | |------------------|--------:|-----------------------| | `YAS_K_F1` | 0xf0001 | F1 | | `YAS_K_F2` | 0xf0002 | F2 | | `YAS_K_F3` | 0xf0003 | F3 | | `YAS_K_F4` | 0xf0004 | F4 | | `YAS_K_F5` | 0xf0005 | F5 | | `YAS_K_F6` | 0xf0006 | F6 | | `YAS_K_F7` | 0xf0007 | F7 | | `YAS_K_F8` | 0xf0008 | F8 | | `YAS_K_F9` | 0xf0009 | F9 | | `YAS_K_F10` | 0xf000a | F10 | | `YAS_K_F11` | 0xf000b | F11 | | `YAS_K_F12` | 0xf000c | F12 | | `YAS_K_S_F1` | 0xf000d | Shift-F1 | | `YAS_K_S_F2` | 0xf000e | Shift-F2 | | `YAS_K_S_F3` | 0xf000f | Shift-F3 | | `YAS_K_S_F4` | 0xf0010 | Shift-F4 | | `YAS_K_S_F5` | 0xf0011 | Shift-F5 | | `YAS_K_S_F6` | 0xf0012 | Shift-F6 | | `YAS_K_S_F7` | 0xf0013 | Shift-F7 | | `YAS_K_S_F8` | 0xf0014 | Shift-F8 | | `YAS_K_S_F9` | 0xf0015 | Shift-F9 | | `YAS_K_S_F10` | 0xf0016 | Shift-F10 | | `YAS_K_S_F11` | 0xf0017 | Shift-F11 | | `YAS_K_S_F12` | 0xf0018 | Shift-F12 | | `YAS_K_C_F1` | 0xf0019 | Ctrl-F1 | | `YAS_K_C_F2` | 0xf001a | Ctrl-F2 | | `YAS_K_C_F3` | 0xf001b | Ctrl-F3 | | `YAS_K_C_F4` | 0xf001c | Ctrl-F4 | | `YAS_K_C_F5` | 0xf001d | Ctrl-F5 | | `YAS_K_C_F6` | 0xf001e | Ctrl-F6 | | `YAS_K_C_F7` | 0xf001f | Ctrl-F7 | | `YAS_K_C_F8` | 0xf0020 | Ctrl-F8 | | `YAS_K_C_F9` | 0xf0021 | Ctrl-F9 | | `YAS_K_C_F10` | 0xf0022 | Ctrl-F10 | | `YAS_K_C_F11` | 0xf0023 | Ctrl-F11 | | `YAS_K_C_F12` | 0xf0024 | Ctrl-F12 | | `YAS_K_A_F1` | 0xf0025 | Alt-F1 | | `YAS_K_A_F2` | 0xf0026 | Alt-F2 | | `YAS_K_A_F3` | 0xf0027 | Alt-F3 | | `YAS_K_A_F4` | 0xf0028 | Alt-F4 | | `YAS_K_A_F5` | 0xf0029 | Alt-F5 | | `YAS_K_A_F6` | 0xf002a | Alt-F6 | | `YAS_K_A_F7` | 0xf002b | Alt-F7 | | `YAS_K_A_F8` | 0xf002c | Alt-F8 | | `YAS_K_A_F9` | 0xf002d | Alt-F9 | | `YAS_K_A_F10` | 0xf002e | Alt-F10 | | `YAS_K_A_F11` | 0xf002f | Alt-F11 | | `YAS_K_A_F12` | 0xf0030 | Alt-F12 | | `YAS_K_LEFT` | 0xf0031 | Left | | `YAS_K_UP` | 0xf0032 | Up | | `YAS_K_DOWN` | 0xf0033 | Down | | `YAS_K_RIGHT` | 0xf0034 | Right | | `YAS_K_HOME` | 0xf0035 | Home | | `YAS_K_END` | 0xf0036 | End | | `YAS_K_PGUP` | 0xf0037 | PageUp | | `YAS_K_PGDN` | 0xf0038 | PageDown | | `YAS_K_INS` | 0xf0039 | Insert | | `YAS_K_DEL` | 0xf003a | Delete | | `YAS_K_C_LEFT` | 0xf003b | Ctrl-Left | | `YAS_K_C_UP` | 0xf003c | Ctrl-Up | | `YAS_K_C_DOWN` | 0xf003d | Ctrl-Down | | `YAS_K_C_RIGHT` | 0xf003e | Ctrl-Right | | `YAS_K_S_LEFT` | 0xf003f | Shift-Left | | `YAS_K_S_UP` | 0xf0040 | Shift-Up | | `YAS_K_S_DOWN` | 0xf0041 | Shift-Down | | `YAS_K_S_RIGHT` | 0xf0042 | Shift-Right | - Alt-\ These codes are generated by a helper macro - `YAS_K_ALT(keycode)`. | Name | Description | |------------------|-----------------------| | `YAS_K_A_BT` | Alt-Backtick | | `YAS_K_A_1` | Alt-1 | | `YAS_K_A_2` | Alt-2 | | `YAS_K_A_3` | Alt-3 | | `YAS_K_A_4` | Alt-4 | | `YAS_K_A_5` | Alt-5 | | `YAS_K_A_6` | Alt-6 | | `YAS_K_A_7` | Alt-7 | | `YAS_K_A_8` | Alt-8 | | `YAS_K_A_9` | Alt-9 | | `YAS_K_A_0` | Alt-0 | | `YAS_K_A_MINUS` | Alt-Minus | | `YAS_K_A_EQ` | Alt-= | | `YAS_K_A_BSP` | Alt-Backspace | | `YAS_K_A_TLD` | Alt-Tilde | | `YAS_K_A_EXCL` | Alt-! | | `YAS_K_A_AT` | Alt-@ | | `YAS_K_A_HASH` | Alt-# | | `YAS_K_A_POUND` | Alt-$ | | `YAS_K_A_PERC` | Alt-% | | `YAS_K_A_CARRET` | Alt-^ | | `YAS_K_A_AND` | Alt-Ampersand | | `YAS_K_A_STAR` | Alt-Star | | `YAS_K_A_OBRA` | Alt-( | | `YAS_K_A_CBRA` | Alt-) | | `YAS_K_A_UND` | Alt-_ | | `YAS_K_A_PLUS` | Alt-+ | | `YAS_K_A_a` | Alt-a | | `YAS_K_A_b` | Alt-b | | `YAS_K_A_c` | Alt-c | | `YAS_K_A_d` | Alt-d | | `YAS_K_A_e` | Alt-e | | `YAS_K_A_f` | Alt-f | | `YAS_K_A_g` | Alt-g | | `YAS_K_A_h` | Alt-h | | `YAS_K_A_i` | Alt-i | | `YAS_K_A_j` | Alt-j | | `YAS_K_A_k` | Alt-k | | `YAS_K_A_l` | Alt-l | | `YAS_K_A_m` | Alt-m | | `YAS_K_A_n` | Alt-n | | `YAS_K_A_o` | Alt-o | | `YAS_K_A_p` | Alt-p | | `YAS_K_A_q` | Alt-q | | `YAS_K_A_r` | Alt-r | | `YAS_K_A_s` | Alt-s | | `YAS_K_A_t` | Alt-t | | `YAS_K_A_u` | Alt-u | | `YAS_K_A_v` | Alt-v | | `YAS_K_A_w` | Alt-w | | `YAS_K_A_x` | Alt-x | | `YAS_K_A_y` | Alt-y | | `YAS_K_A_z` | Alt-z | ## Functions All functions in the API work with a pointer to an opaque `yascreen` structure. The structure is allocated internally in the library by `yascreen_init` and it is the job of the user program to keep track of it. The library is thread safe, as long as each `struct yascreen` object is accessed by a single thread. ### yascreen\_init ```c inline yascreen *yascreen_init(int sx,int sy); ``` allocate and initialize screen data output defaults to stdout in case output is a terminal and initial size is (0,0), the screen size is autodetected in case of error, returns `NULL` ### yascreen\_ver ```c inline const char *yascreen_ver(void); ``` returns a string with the library version ### yascreen\_setout ```c inline int yascreen_setout(yascreen *s,ssize_t (*out)(yascreen *s,const void *data,size_t len)); ``` set callback that handles output if out=NULL, the output goes to `stdout` the callback may implement internal buffering, a flush is signalled by calling `out` with len=0 ### yascreen\_set\_unicode ```c inline void yascreen_set_unicode(yascreen *s,int on); ``` enable (on is non-zero) or disable (on=0) unicode input processing by default unicode mode is on changing the unicode input processing will flush all pending input ### yascreen\_set\_telnet ```c inline void yascreen_set_telnet(yascreen *s,int on); ``` enable (on is non-zero) or disable (on=0) telnet sequence processing by default telnet mode is off ### yascreen\_init\_telnet ```c inline void yascreen_init_telnet(yascreen *s); ``` depending on telnet sequence processing, sends a set of telnet initialization sequences ### yascreen\_resize ```c inline int yascreen_resize(yascreen *s,int sx,int sy); ``` resize screen should redraw afterwards since allocation is involved, this may fail and return -1 ### yascreen\_free ```c inline void yascreen_free(yascreen *s); ``` finish the lifecycle of `struct yascreen` - all internally allocated memory is freed ### yascreen\_term\_save ```c inline void yascreen_term_save(yascreen *s); ``` save current terminal state on top of state stack ### yascreen\_term\_restore ```c inline void yascreen_term_restore(yascreen *s); ``` restore previously saved terminal state from top of state stack ### yascreen\_term\_push ```c inline void yascreen_term_push(yascreen *s); ``` push current terminal state to state stack ### yascreen\_term\_pop ```c inline void yascreen_term_pop(yascreen *s); ``` pop and restore previously saved terminal state from state stack ### yascreen\_term\_set ```c inline void yascreen_term_set(yascreen *s,int mode); ``` set terminal for proper screen operation ### `mode` is a bitmask, containing one of | Name | Value | Description | |------------------|------:|-----------------------| | `YAS_NOBUFF` | 1 | turn off canonical mode (disable ICANON and IEXTEN) | | `YAS_NOSIGN` | 2 | disable ISIG | | `YAS_NOECHO` | 4 | disable local echo (ECHO) | | `YAS_ONLCR` | 8 | ONLCR or OPOST | ### yascreen\_printxy ```c inline int yascreen_printxy(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6))); ``` ### yascreen\_putsxy ```c inline int yascreen_putsxy(yascreen *s,int x,int y,uint32_t attr,const char *str); ``` print at position, if data exceeds buffer, then it gets truncated ### yascreen\_printxyu ```c inline int yascreen_printxyu(yascreen *s,int x,int y,uint32_t attr,const char *format,...) __attribute__((format(printf,5,6))); ``` ### yascreen\_putsxyu ```c inline int yascreen_putsxyu(yascreen *s,int x,int y,uint32_t attr,const char *str); ``` print at position, if data exceeds buffer, then it gets truncated screen is immediately updated ### yascreen\_update ```c inline int yascreen_update(yascreen *s); ``` sync memory state to screen since allocation is involved, this may fail and return -1 ### yascreen\_redraw ```c inline void yascreen_redraw(yascreen *s); ``` set next update to be a full redraw ### yascreen\_clear\_mem ```c inline void yascreen_clear_mem(yascreen *s,uint32_t attr); ``` clear memory buffer all cells in the screen are set to `Space`, using `attr` for colors and style ### yascreen\_cursor ```c inline void yascreen_cursor(yascreen *s,int on); ``` hide (`on`=0) or show (`on` is non-zero) cusror screen is updated immediately ### yascreen\_cursor\_xy ```c inline void yascreen_cursor_xy(yascreen *s,int x,int y); ``` set cursor position screen is updated immediately ### yascreen\_altbuf ```c inline void yascreen_altbuf(yascreen *s,int on); ``` switch between regular and alternative buffer screen is updated immediately ### yascreen\_clear ```c inline void yascreen_clear(yascreen *s); ``` clear real screen, no change to memory buffers ### yascreen\_clearln ```c inline void yascreen_clearln(yascreen *s); ``` clear current line, no change to memory buffers ### yascreen\_update\_attr ```c inline void yascreen_update_attr(yascreen *s,uint32_t oattr,uint32_t nattr); ``` apply difference between two attrs and output the optimized ANSI sequence to switch from `oattr` to `nattr` if `oattr`=0xffffffff, the full ANSI sequence will be generated no change to memory buffers ### yascreen\_set\_attr ```c yascreen_set_attr(s,attr) ``` reset all attrs and set specific one (`attr`) ### yascreen\_print ```c inline int yascreen_print(yascreen *s,const char *format,...) __attribute__((format(printf,2,3))); ``` ### yascreen\_write ```c inline int yascreen_write(yascreen *s,const char *str,int len); ``` ### yascreen\_puts ```c inline int yascreen_puts(yascreen *s,const char *str); ``` ### yascreen\_clearln\_s ```c inline const char *yascreen_clearln_s(yascreen *s); ``` print in line mode ### yascreen\_sx ```c inline int yascreen_sx(yascreen *s); ``` get current x size ### yascreen\_sy ```c inline int yascreen_sy(yascreen *s); ``` get current y size ### yascreen\_x ```c inline int yascreen_x(yascreen *s); ``` get current x ### yascreen\_y ```c inline int yascreen_y(yascreen *s); ``` get current y ### yascreen\_esc\_to ```c inline void yascreen_esc_to(yascreen *s,int timeout); ``` set timeout for single ESC key press ### yascreen\_ckto ```c inline void yascreen_ckto(yascreen *s); ``` in case of external event loop, this call will check for single ESC key should be called regularly enough so that the above specified timeout is not extended too much if not called often enough then single ESC will be yielded after longer timeout if not called at all then single ESC will be yielded with next key press ### yascreen\_getch\_to ```c inline int yascreen_getch_to(yascreen *s,int timeout); ``` wait for a key, return ASCII or extended keycode, wait no more than timeout in milliseconds ### yascreen\_getch ```c yascreen_getch(s) ``` get a key without timeout this macro expands to `yascreen_getch_to(s,0)` zero timeout=wait forever ### yascreen\_getch\_nowait ```c yascreen_getch_nowait(s) ``` get a key, if available, return immediately this macro expands to `yascreen_getch_to(s,-1)` negative timeout=do not wait ### yascreen\_ungetch ```c inline void yascreen_ungetch(yascreen *s,int key); ``` put back key value in key buffer the internal key buffer is dynamically allocated, hence there is no limit of how many key codes may be put back, but in case of memory allocation failure, the error will not be reported and the key will not be put into the buffer ### yascreen\_pushch ```c inline void yascreen_pushch(yascreen *s,int key); ``` push key value at end of key buffer similar to `yascreen_ungetch` but the `key` code will be returned after all other key codes currently in the buffer the internal key buffer is dynamically allocated, hence there is no limit of how many key codes may be put back, but in case of memory allocation failure, the error will not be reported and the key will not be put into the buffer ### yascreen\_feed ```c inline void yascreen_feed(yascreen *s,unsigned char c); ``` feed key sequence state machine with byte stream this is useful to implement external event loop and read key codes by `yascreen_getch_nowait` until it returns -1 ### yascreen\_peekch ```c inline int yascreen_peekch(yascreen *s); ``` peek for key without removing it from input queue ### yascreen\_getsize ```c inline void yascreen_getsize(yascreen *s,int *sx,int *sy); ``` get last reported screen size set both to 0 if there is none this will yield valid result after `YAS_SCREEN_SIZE` is returned as keypress ### yascreen\_reqsize ```c inline void yascreen_reqsize(yascreen *s); ``` request terminal to report its size via ANSI sequence ### yascreen\_set\_hint\_i ```c inline void yascreen_set_hint_i(yascreen *s,int hint); ``` ### yascreen\_get\_hint\_i ```c inline int yascreen_get_hint_i(yascreen *s); ``` ### yascreen\_set\_hint\_p ```c inline void yascreen_set_hint_p(yascreen *s,void *hint); ``` ### yascreen\_get\_hint\_p ```c inline void *yascreen_get_hint_p(yascreen *s); ``` get/set opaque hint values integer and pointer hints are stored separately and both can be used at the same time these are useful to link the `yascreen` instance to user program data for example a single output callback may output to socket or a terminal, depending on the hint values ### yascreen\_line\_flush ```c inline void yascreen_line_flush(yascreen *s,int on); ``` enable/disable auto flush for line and direct screen oriented operations yascreen versions before 1.77 didn't use buffered output and would immediately send the output to the screen disabling internal flush can help an application optimize the number of `write` calls at the cost of performing explicit flush after each group of operations explicit flush example: ```c yascreen_write(s,"",0); ``` ### yascreen\_getwch\_to ```c inline wchar_t yascreen_getwch_to(yascreen *s,int timeout); ``` wait for a key, return wide character or extended keycode, wait no more than timeout in milliseconds `yascreen_getwch_to` does not work in non-unicode mode and will always return `YAS_K_NONE` mixing the utf8 and wide character input modes will break on multibyte utf8 sequences and is not supported ### yascreen\_getwch ```c yascreen_getwch(s) ``` get a key as wide character without timeout this macro expands to `yascreen_getwch_to(s,0)` zero timeout=wait forever `yascreen_getwch` does not work in non-unicode mode and will always return `YAS_K_NONE` ### yascreen\_getwch\_nowait ```c yascreen_getwch_nowait(s) ``` get a key as a wide character, if available, return immediately this macro expands to `yascreen_getwch_to(s,-1)` negative timeout=do not wait `yascreen_getwch_nowait` does not work in non-unicode mode and will always return `YAS_K_NONE` ### yascreen\_ungetwch ```c inline void yascreen_ungetwch(yascreen *s,wchar_t key); ``` put back wide character key value in key buffer the internal key buffer is dynamically allocated, hence there is no limit of how many key codes may be put back, but in case of memory allocation failure, the error will not be reported and the key will not be put into the buffer the internal key buffer contains utf8 and the wide character will be expanded to the appropriate utf8 sequence `yascreen_ungetwch` does not do anything in non-unicode mode ### yascreen\_peekwch ```c inline wchar_t yascreen_peekwch(yascreen *s); ``` peek for key without removing it from input queue `yascreen_peekwch` does not work in non-unicode mode and will always return `YAS_K_NONE` vfu-5.09/THANKS.TO0000644000175000017500000000404014367030452012057 0ustar cadecade THANKS.TO ---------------------------------------------------------------------------- There are many people that I should thank... :) Few days ago ( about 1.Apr.2000(!) ) I was accused of not providing appropriate thanks/credits! Wow, it was a friend of mine :) so, from now on this file will keep track of all the people who did help the VFU project! BIG THANKS TO ALL OF THEM! ( note: the following list is in random order and is, probably, *not* complete! ) If someone is missing in this list, please tell me! The author of the old F (unknown to me) ( F is an old dos real-mode file manager ) -- for the original F itself Alexander Dimitrov for the extensive vfu testing and sample config file(s) Hubert Feyrer for NetBSD support & packaging Ryan Weaver for RedHat (rpm) support & packaging Mika Fischer for Debian support & packaging Ivaylo Baylov for bits of code (credits in the source) and ideas (*) Charles Hannum for several fixups BIS OnLine [http://www.biscom.net] for hosting the VFU site all these years ePay.bg Plc [http://soul.datamax.bg] for hosting the VFU site 2002-2022 William Vera for Debian support & packaging Larry De Coste for keeping in touch after so many years :) Boian Bonev for Yascreen lib, many suggestions and help, also with current Debian/Devuan package management. ...and all VFU users! :) Thanks! Ubuntu Community Documentation for VFU wiki docs: https://help.ubuntu.com/community/Vfu/ (current release copied as vfu.wiki here) 1996-2022 (c) Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/projects/vfu ---eof---------------------------------------------------------------------- vfu-5.09/COMPILE.NOTES0000644000175000017500000000246714372204606012540 0ustar cadecade This file contains some misc notes... -- For Debian/Devuan you need to: apt-get install make gcc g++ libpcre3-dev libncurses5-dev then run 'make' in vfu base directory -- If you try to compile vfu under NetBSD: try to use `build.netbsd'... If you fail to compile it try one of: 1. get newer vfu version at https://cade.noxrun.com/projects/vfu 2. mailto: Hubert Feyrer Hubert Feyrer is official VFU maintainer for NetBSD platform. 3. mailto: Vladi Belperchinov-Shabanski -- If you want to compile vfu static binary: 1. cd vfu 2. make LDDEF=-static voila! :) -- How to compile VFU for other UNIX platform? You have to edit vslib/target.h file and to add these lines just before `#ifndef _TARGET_DESCRIPTION_' line: (I'll give example as if the new target is RS6000) ---cut--- #ifdef _TARGET_UNKNOWN_ #if defined(__RS6000__) #define _TARGET_RS6000_ #define _TARGET_DESCRIPTION_ "UNIX/RS6000" #undef _TARGET_UNKNOWN_ #endif #endif ---cut--- and start make: make CCDEF=-D__RS6000__ in both vslib and vfu directories VFU should compile without big problems on any UNIX (hope so:)) P! Vladi. Vladi Belperchinov-Shabanski vfu-5.09/INSTALL0000644000175000017500000000652414367030452011665 0ustar cadecade 1. how to compile vfu run `make' from vfu base directory this should compile everything to compile each part of vfu manually run these commands: git clone https://github.com/bbonev/yascreen.git make -C yascreen git clone https://github.com/cade-vs/vslib.git make -C vslib git clone https://github.com/cade-vs/vfu.git make -C vfu if something goes wrong, check these: -- if your `curses.h' file locations is not `/usr/include/ncurses' you have to change this in the Makefile. -- if vslib library is not in the `../vslib' directory you also have to change this in the Makefile. to get file sizes above 4GB shown properly you need to make this way: export CCDEF="-D_FILE_OFFSET_BITS=64" make or make CCDEF="-D_FILE_OFFSET_BITS=64" 2. how to install vfu run `install' script from vfu base directory install script checks if all required files are available/built and then does this: cp vfu/vfu rx/rx_* /usr/local/bin cp vfu.1 /usr/local/man/man1 cp vfu.conf /usr/local/etc 3. how to install vfu manually -- you have to copy `vfu' in the `/usr/local/bin' or `/usr/bin' directory and set mode to 755 `rwxr-xr-x' the owner is not significant ( root is also possible ). -- there is preliminary man page ( vfu.1 ) which could be copied to /usr/man/man1. -- copy all `rx/rx_*' tools to /usr/local/bin -- install Net::FTP perl module if needed. (this is used for FTP support) 4. requirements: VFU uses pcre perl-like regexp library. If you get error messages like these: > ~/src/vfu-4.09$ make > make -C vslib > make[1]: Entering directory `/home/cade/src/vfu-4.09/vslib' > true > g++ -I. -O2 -c vstrlib.cpp -o .OBJ.libvslib.a/vstrlib.o > In file included from vstrlib.cpp:17: > vstrlib.h:28:18: error: pcre.h: No such file or directory > In file included from vstrlib.cpp:17: > vstrlib.h:178: error: ISO C++ forbids declaration of pcre with no type > vstrlib.h:178: error: expected ; before * token > vstrlib.h:179: error: ISO C++ forbids declaration of pcre_extra with then you need pcre library. you should have package named something like: libpcre-dev in debian, for example, the correct package name is: libpcre3-dev apt-get install libpcre3-dev vfu also needs curses: apt-get install libncurses5-dev with all dev tools, in Debian/Devuan you need to: apt-get install make gcc g++ libpcre3-dev libncurses5-dev if you dont have this package or do not want to install it, you can try VFU (VSLIB) internal pcre library (i.e. pcre snapshot, used with permission, see vslib/README file): tar xzvf vfu-4.09.tar.gz cd vfu-4.09 make -C vslib/pcre export CCDEF="-I../vslib/pcre" export LDDEF="-L../vslib/pcre" make this should help. WARNING: make sure to remove all old personal cache files! If you still have problems feel free to contact me anytime. P! Vladi. -- Vladi Belperchinov-Shabanski https://cade.noxrun.com/projects/vfu vfu-5.09/build.docs0000755000175000017500000000032314145574023012600 0ustar cadecade#!/bin/sh pod2man --center="VFU File Manager" --release="Version 4.xx" vfu.pod > vfu.1 pod2html vfu.pod > vfu.html rm -f pod2htmd.x~~ pod2htmi.x~~ pod2html-dircache pod2html-itemcache vfu.pod.tmp echo "done." vfu-5.09/extra/0000755000175000017500000000000014367030452011750 5ustar cadecadevfu-5.09/extra/PxPlus_IBM_VGA8.ttf0000644000175000017500000021245414367030452015206 0ustar cadecade`FFTMvۑbdGDEF'4<&OS/2wh`cmap3>}gasp4glyftvwhead ]6hhea$$hmtx<1$loca maxpOH nameOtpostuRxpropw tD_< @ҐRQҐRQp p  @ @@ @@ dX @ V.R.p`     dddddddddd,d,dddddddd,ddd,dd,dddddddddddddddddddddddddddddd,dd,,ddddddddddddddddddddddddd,ddddddddddd,dd,,,,,,,,dddddddddfQx~_&(   " ' 0 3 5 : < @ D T {  !!!!"!&!.!Q!^!!""""""""")"+"H"a"e"####!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k'Qx~&(    & 0 2 5 9 < > D T t  !!!!"!&!.!P!S!!""""""""")"+"H"`"d"#### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j'jRhWR,"!_CBA>65410/,ywlibA@lUS ߺ߹߬ߝݿݾݵݲݯݬݩݢݛݔݍzgeb_\PHC<;4/,$ܤܢܡܞ   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~acad34UV]^YZYpjk  b[_fpwstuxvq**H~8Lh .jZf2Jbb~&^p 0bx:Z@d  D f  > \ x  * @ b 2 D d p . R z  (D`0Jh,Fjj*<,8p|"BPf|J V"V>^(Nx(Hh RV Br0^:Rz$Rv 2 d !*!b!!!""L"~""##6#`###$ $T$$$%*%d%%& &:&t&&''$'L't''''((<(b(~((()),)b))))*"*B*h****++8+b+++,,6,X,z,,,--J-z---..R.~..//N//00@0|001 121`11122$2F2l22233N3r3334&4X4445&5T55566$6P666747f778 828\88888999*9@9Z9|9999::4:V:v:::;&;N;t;;;<<(>L>x>>>??@?`???@@2@P@~@@@AA*ALAjAAAABB2BNBfBBBCC"CFClCCCDDFDzDDDE&E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a"#$%&'bcdefghjikmlnoqprsutvwxzy{}|~()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     uni0000uni000Duni0001uni0002uni0003uni0004uni0005uni0006uni0007uni0008uni0009uni000Auni000Buni000Cuni000Euni000Funi0010uni0011uni0012uni0013uni0014uni0015uni0016uni0017uni0018uni0019uni001Auni001Buni001Cuni001Duni001Euni001Funi007Funi00A0uni00ADuni00B2uni00B3uni00B9AmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsohornuni01B7 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccentuni021Auni021Buni0251uni0278uni02C9uni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0uni03F4uni0400 afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061uni040D afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097uni0450 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109uni045D afii10110 afii10193 afii10050 afii10098 afii57645 afii57664 afii57665 afii57666 afii57667 afii57668 afii57669 afii57670 afii57671 afii57672 afii57673 afii57674 afii57675 afii57676 afii57677 afii57678 afii57679 afii57680 afii57681 afii57682 afii57683 afii57684 afii57685 afii57686 afii57687 afii57688 afii57689 afii57690 afii57716 afii57717 afii57718uni05F3uni05F4uni1D1Buni1D26uni1D28WgravewgraveWacutewacute Wdieresis wdieresisuni1E9FYgraveygraveuni2010 figuredash afii00208 underscoredbl quotereverseduni201Funi2027minuteseconduni2035 exclamdbluni203Euni203Funi2040uni2054uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Funi2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Blirapeseta afii57636Euro afii61248 afii61289 afii61352 estimateduni2150uni2151onethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighths arrowleftarrowup arrowright arrowdown arrowboth arrowupdn arrowupdnbseemptysetelementuni2215uni2219 orthogonal intersection equivalenceuni2299uni2300house revlogicalnot integraltp integralbtSF100000SF110000SF010000SF030000SF020000SF040000SF080000SF090000SF060000SF070000SF050000SF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000upblockuni2581dnblockblocklfblockrtblockltshadeshadedkshade filledboxH22073H18543H18551 filledrecttriaguptriagrttriagdntriaglfcircleH18533 invbullet invcircle openbullet smileface invsmilefacesunfemalemalespadeclubheartdiamond musicalnotemusicalnotedbluni2713uniFB01uniFB02uniFFFD  G+ҐRGC     !! #" &$(' )) ** ++ ,,--..0/:1;;<< == >> ?? A@ \\ ]] ^^ a_ || }} ~~      {p }} F&eS hfii jj kk ol ppqq wryx{       vfu-5.09/extra/vfu-bash0000644000175000017500000000076414145574023013416 0ustar cadecade most simple way to have VFU to exit to different directory is to have this bash function in ~/.profile or similar location: function vfu() { /usr/local/bin/vfu $*; cd "`cat /tmp/vfu.exit.$USER`"; rm -f /tmp/vfu.exit.$USER; } if you do not want this file to be in the system-wide /tmp directory, you can change it in ~/.profile or similar location: export VFU_EXIT=/home/cade/tmp/vfu.exit.tmp function vfu() { ~/bin/vfu $*; cd "`cat $VFU_EXIT`"; rm -f $VFU_EXIT; } vfu-5.09/extra/Xmodmap0000644000175000017500000000057414145574023013307 0ustar cadecadekeycode 22 = BackSpace osfBackSpace keycode 63 = asterisk keycode 112 = slash keycode 108 = Return keycode 79 = Home keycode 80 = Up keycode 81 = Page_Up keycode 82 = minus keycode 83 = Left keycode 85 = Right keycode 86 = plus keycode 87 = End keycode 88 = Down keycode 89 = Page_Down keycode 90 = Insert keycode 91 = Delete vfu-5.09/extra/Xdefaults0000644000175000017500000000045614367030452013637 0ustar cadecadeRxvt*font: -*-pxplus ibm vga8-medium-r-normal-*-*-*-*-*-*-*-iso10646-* Rxvt*boldFont: -*-pxplus ibm vga8-medium-r-normal-*-*-*-*-*-*-*-iso10646-* Rxvt*background: gray15 Rxvt*foreground: white Rxvt*cursorColor: green Rxvt*title: Term Rxvt*scrollBar: False ; Rxvt*backspacekey: ^H Rxvt*saveLines: 32000 vfu-5.09/extra/README0000644000175000017500000000077014367030452012634 0ustar cadecade This directory contains some extra files, not (directly) related to the VFU project Xmodmap is a modmap file that enables keypad keys as arrows/home/end etc... vfu-bash is a bash function that enables vfu to change working directory on exit, to merge with your current environment you have to: . ./vfu-bash note the leading dot! Xdefaults rxvt configuration and font selection vfu-5.09/AUTHORS0000644000175000017500000000034414204533152011671 0ustar cadecade 1996-2022 (c) Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/projects/vfu http://github.com/cade-vs/vfu-dist http://github.com/cade-vs/vfu vfu-5.09/FAQ0000644000175000017500000003062114444676573011201 0ustar cadecade FREQUENTLY ASKED QUESTIONS ABOUT: VF File Manager for Unix/Linux ( VFU ) 1996-2023 (c) Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com/projects/vfu http://github.com/cade-vs/vfu-dist http://github.com/cade-vs/vfu 1. What is 'TY' (formerly 'TP') field on the third line? This stands for "type" and means: [] -- directory <> -- link to directory -> -- link ** -- executable ++ -- character device ## -- socket () -- FIFO (pipe) == -- block device -- -- regular file it is used for quick visual hint about file entry type. 2. Does VFU follows directory links when calculating directory(ies) size? No. :) Well, not by default. There is an option to do so in the dir sizes menu (key Z) but it is not permanent option and will reset when VFU quits/starts. 3. I cannot see entire filename? Try '0' -- it will show only filenames... or try '1'..'6' to toggle different info entries. Also available from the options menu (key O). 4. What files are considered `executables' (i.e. marked `**') File is marked `**' if any of `x' modes are set (regardless owner, group, etc.) This means that if file has mode of -rwxr--r-- and you are not owner the file will be still marked as `**' but you won't be able to execute it! 5. Do I need to set `HOME' environment variable under DOS, and where it is supposed to point? Yes. In general, the HOME directory is used to keep all configuration and status files. Well, if you don't set it, VFU will set HOME directory into TEMP ( c:/tmp/ under DOS ) But keep in mind that VFU is UNIX file manager in nature so it can use HOME under DOS for something else in the future, so it is recommended to set it. ( just to be safe :) ) 7. How can I use `JumpToMountpoint' function under DOS? NOTE: since 2023 VFU drops support for DOS/DJGPP/GO32. However, you may still use pre-5.0 versions. The information for this function comes from the `/etc/mtab' file. This file show currently mounted file systems. Under DOS platform the only way is to simulate this file, which is quite simple -- You have just to create `_vfu.mtb' file in the HOME (see FAQ 6 above) directory. Content of this file is: (this is example) ---cut--- - c:/ - d:/ - j:/ ---cut--- Please note the leading `-', it must be here. However you can add whatever you want ( I mean paths ), but normally you just should add roots of all drives. 8. I've just created `some/newdir' in the current directory. VFU reported no error, but I cannot see it. Well, you just have to hit 'r' to reload directory content. I know this may be confusing at first, but currently it just works this way by design. 9. How can I exit to new directory from vfu (UNIX) On exit VFU always try to create file named `/tmp/vfu.exit.USERNAME` where `USERNAME' is current user's user name :) for example my filename will be named `/tmp/vfu.exit.cade'. To make use of this file you should define alias or function (bash) for it like this example: function vfu() { /usr/local/bin/vfu $*; cd `cat /tmp/vfu.exit.$USER`; rm -f /tmp/vfu.exit.$USER; } This is bash function that does the trick. If someone need this for other shell or with an alias then please contact me. After this you can use `q' to exit VFU and cd to the VFU's current working directory. You can change this filename by exporting new name in this way: export VFU_EXIT="/tmp/my.exit.file.$$" And VFU will use it instead of default one. NOTE: DOS version should work without this -- the `q'uit function will work out of the box. 10. What is `Name###' sort order? And why should I RTFM? :) This mode forces sort order as follows: file names are: `vs011.txt', `vs04.txt', `vs030.txt' sort order is : `Name' the result is : `vs011.txt', `vs030.txt', `vs04.txt' file names are: `vs011.txt', `vs04.txt', `vs030.txt' sort order is : `Name###' the result is : `vs04.txt', `vs011.txt', `vs030.txt' ...in a few words: if the file name (until first dot if exist) ends with number, VFU will sort them in regard to the numeric values found at the ends of the filenames. tech note: RegExp used is "^(.*)([0123456789]+)(\\.(.*))?$" NOTE: This mode drops all after the first dot! NOTE: This mode resets on exit! 11. How the tilde expansion is done in VFU? It works only in standard input line for getting directory name which is used for: chdir, copy, move... it won't work in vfu.conf or anywhere else. 12. What is the mask expansion and how it works? If no `*' or `?' found in the mask then: 1. add `*' at the end 2. if mask starts with `.' then add `*' to the front else 1. leave it `as is' If you don't like the expansion, you can switch it off in the options menu (key O): Options(Toggles)/FilesMaskExpand and FileFindMaskExpand options. 13. Where mask expansion applies and can I turn it off? Wherever you need to enter mask! (file list masking, global select, file find, etc...) You can turn it off form the options menu (key O). 14. Can I browse/view more than one file at the same time? NOTE: as of early 2023 this is temporarily disabled. Yes! You have just to select needed files and press 'B' (browse). Then use `1'..`0' to switch between files/slots. The limit is 10 files. 15. What `Show Real Free Space' option means? Some operating systems keep users from filling the entire filesystems. So there are two `free spaces' the one is the `real free space' ( i.e. physical one ) and `user free space' which is the free space available to the user. By default VFU shows the free space available to the user. If you want to use the real free space count -- turn this on. 16. How can I use the FTP support? NOTE! as of Nov.2002 VFU doesn't use `ftparc' anymore! Instead rx_ftp uses Net::FTP perl module. All you need to use FTP support is: a. install Net::FTP if you haven't done this already. on any linux you can install it with cpan: $ cpan Net::FTP on Debian Linux it is inside `perl-modules' package: $ apt-get install perl-modules b. create needed FTP file "archives": example: create plain text file: cade-at-jane.ftp ---cut--- jane.tetida.org cade password ---cut--- This file has to contain three lines: host username password c. Then from inside VFU press `+' or `ENTER' to enter FTP site just as it is plain archive file. More details could be found in the README file with rx_*. (rx directory under VFU main distribution directory) 17. I cannot enter archive/ftp! What's wrong? Since version 3.00 VFU uses external utilities to handle archives/ftp. These utilities are named rx_ext, where `ext' is archive type extensions. However VFU calls `rx_auto' which decides which specific `rx_*' to call. So the solution is: make sure rx_* utilities are in the path. Then test the result of `rx_auto l archive', it should return something like: ---cut--- NAME: some/name.ext SIZE: 1024 MODE: -rwx---rwx NAME: other/name.misc SIZE: 1110 MODE: ---------- ... ---cut--- 18. What is the interface to those rx_* tools? rx_* tools should provide the following commands: rx_ftp v archive directory view contents of `directory' of archive `archive' rx_ftp V archive directory view contents of `directory' of archive `archive' recursively (i.e. all filenames are with full path, `directory' is ignored ) rx_ftp x archive files... extract files from archive rx_ftp x archive @listfile extract files from archive (but get the list from file named `listfile') examples: rx_tar v vfu-3.01.tar.gz /vfu-3.01/vslib/ rx_tar V vfu-3.01.tar.gz rx_tar x vfu-3.01.tar.gz /vfu-3.01/ftparc/README The expected format that has to be returned for commands `v' and `V' is: ---cut--- NAME:filename SIZE:12345678 MODE:-rwxr-xr-- TIME:YYYYMMDDHHMMSS NAME:filename SIZE:12345678 MODE:-rwxr-xr-- TIME:YYYYMMDDHHMMSS ... ---cut--- Currently only NAME and SIZE are supported! The order for fields is random. Each entry is separated with empty line. `filename' contains only filename for `v' command and full path for `V' command. examples: `v' command: ---cut--- NAME:README SIZE:605 MODE:-rw-r--r-- TIME:200005231124 NAME:vfumenu.h SIZE:614 MODE:-rw-r--r-- TIME:200005191052 NAME:vfusetup.h SIZE:1327 MODE:-rw-r--r-- TIME:200008010017 NAME:makefile SIZE:3772 MODE:-rw-r--r-- TIME:200005191058 ---cut--- `V' command: ---cut--- NAME:vfu-3.02/vfu.1 SIZE:16773 MODE:-rw-r--r-- TIME:200008010118 NAME:vfu-3.02/build.netbsd SIZE:832 MODE:-rwxr-xr-x TIME:200006171605 NAME:vfu-3.02/README.DOS SIZE:430 MODE:-rw-r--r-- TIME:199902161511 NAME:vfu-3.02/TODO SIZE:685 MODE:-rw-r--r-- TIME:200006172251 NAME:vfu-3.02/ftparc/ftparc.cpp SIZE:7521 MODE:-rw-r--r-- TIME:200004161224 ---cut--- 19. What does Tools/RenameTools/SimplifyName do? First replaces all `special' symbols as `'&\"/ etc with _ then it compresses all repeating _'s and finally it replaces `_-_' with `-'. NOTE: suggestions are welcome. 20. How can I purge directory sizes cache? The sizes cache is flushed on directory tree rebuild. 21. Why I cannot execute image viewer (or any prog) on a file inside archive? It reports `file not found' error? The problem is when you want to spawn image viewer with `&' on background. At this point VFU has taken control back and temporary file/dir is removed. There is no slick solution to this and only workaround is to remove `&' from your command. 22. How to make VFU to handle Ctrl+Z, Ctrl+C? You have to export UNICON_NO_RAW environment variable: export UNICON_NO_RAW=1 vfu ... 23. Is it possible to see file sizes above 4GB properly? NOTE: since version 5.0 (early 2023) this is not needed. Yes, you have to compile this way: export CCDEF="-D_FILE_OFFSET_BITS=64" make or make CCDEF="-D_FILE_OFFSET_BITS=64" 24. When I change directory (key "D"), VFU prints this line: "use keys: TAB, PageUp, PageDown, Ctrl+X, Ctrl+A". What are those keys used for? -- TAB completes currently typed directory path (only rightmost level) -- PageUp brings history of all previously entered directories (including for copy/move/chdir) -- PageDown brings history of all previously visited directories (chdir history) -- Ctrl+X expands currently typed directory path to the true path (removes symlinks) -- Ctrl+A removes rightmost directory level from the typed path 25. What are those "*"s and "!"s appended to file time? "*" means that time is in the last 23 hours. "!" means that time is in the future. 99. I don't/do like feature X, can I turn it OFF/ON? VFU has a lot of options to alternate behaviour. Check in the options menu (key O) if there is applicable toggle. If not, contact me to discuss it :) vfu-5.09/GIT_HISTORY0000644000175000017500000006320714444676573012444 0ustar cadecadecommit a6110d493e715751f1dfec348ae79d34ccc739a0 Author: Vladi Belperchinov-Shabanski Date: Thu Jun 22 01:44:25 2023 +0300 vfu-5.09 commit 940f3da9e78dc51a1c79105409230f9bd15acb12 Author: Vladi Belperchinov-Shabanski Date: Mon Mar 20 23:38:39 2023 +0200 fixed-shell-escape-for-pipe-etc commit b71959cf25f073971d5e97cc907d04122b5a42d2 Author: Vladi Belperchinov-Shabanski Date: Mon Mar 20 23:17:50 2023 +0200 fixed-few-tools-commands-when-no-files-in-the-list commit 199192aab9a54493a0dc5d39c75d935e02c9b25d Author: Vladi Belperchinov-Shabanski Date: Mon Mar 6 13:38:54 2023 +0200 maint commit 8bf6f8fe73f212a8843aaee00c26f8d32b3bc8ed Author: Vladi Belperchinov-Shabanski Date: Mon Mar 6 11:47:09 2023 +0200 maint commit 3603858edf3d5ac5f113552bd180abf3348cac58 Author: Vladi Belperchinov-Shabanski Date: Sun Feb 12 23:13:22 2023 +0200 r5.07 commit f0111b708b41588a878b16a6f3ac68eb11df64d1 Author: Vladi Belperchinov-Shabanski Date: Sun Feb 12 22:53:11 2023 +0200 r5.05 commit 1cb1dbcc3903b7b9820cd66f41030b802dc5cc5b Author: Vladi Belperchinov-Shabanski Date: Sun Feb 12 22:51:24 2023 +0200 maint commit 704cdbee482bbf06376764be91569b9ac55fe908 Merge: bb7e01c dfc0e98 Author: Vladi Belperchinov-Shabanski Date: Sun Feb 12 22:30:09 2023 +0200 Merge branch 'master' of github.com:cade-vs/vfu-dist commit bb7e01c190fca9f262d673242aa83e570598b955 Author: Vladi Belperchinov-Shabanski Date: Sun Feb 12 22:29:50 2023 +0200 r5.04 commit dfc0e981c1be02979fb6bd6082c36fb173e1103b Merge: 1acfcc5 6ee5b95 Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 03:08:42 2023 +0200 Merge pull request #13 from bbonev/master half working commit 6ee5b950d23e11c5fbbe03000bfe2b66bdb10d8a Merge: 71a4b6c 1acfcc5 Author: Boian Bonev Date: Sat Feb 11 03:07:19 2023 +0200 Merge branch 'cade-vs:master' into master commit 71a4b6c324f472ac245526e27fb10a5e9c7dca2b Author: Boian Bonev Date: Sat Feb 11 01:02:32 2023 +0000 set ar/ranlib commit ae9b61027d2ddb7190c99f283dad2246add93f40 Author: Boian Bonev Date: Sat Feb 11 00:57:02 2023 +0000 always set cc commit 5c7eb95be49e9c45405465a8cbbcb8537a6426ba Author: Boian Bonev Date: Sat Feb 11 00:47:07 2023 +0000 apply flto config to all projects commit 9ddcbc78b8f72807d0321f9040509ca793d47993 Author: Boian Bonev Date: Sat Feb 11 00:43:39 2023 +0000 simplify commit 1cc1e73fd87e0339f087e00d80ca08e82170d505 Author: Boian Bonev Date: Sat Feb 11 00:41:10 2023 +0000 clang 13+ understands -flto=auto commit d7de1d04feea8214857473ad1b314f1f7a0dda3e Author: Boian Bonev Date: Sat Feb 11 00:36:30 2023 +0000 be more verbose commit 969dc323d1d9acbab9320398bfef3cce3d35f907 Author: Boian Bonev Date: Sat Feb 11 00:33:03 2023 +0000 use different flto build with old clang commit 66e6cf8c95f9e1e96c3bfb06b5498f4c9b380c47 Author: Boian Bonev Date: Sat Feb 11 00:25:16 2023 +0000 fix paths(2); verbose build commit 2de2d664d4d0d9ea6aa6d12f07f31a6a6f793955 Author: Boian Bonev Date: Sat Feb 11 00:21:24 2023 +0000 fix paths commit 45dbfa03a4fe2937339c2462bcef80155e32d4cd Author: Boian Bonev Date: Sat Feb 11 00:18:53 2023 +0000 properly prepare yascreen.pc in build dir commit caa7ef5a6010358ff5a814786c5a790b9fc040eb Author: Boian Bonev Date: Sat Feb 11 00:13:33 2023 +0000 do not build man page commit 6407b19931d4bd2fe8db8a244cbab69297113072 Author: Boian Bonev Date: Sat Feb 11 00:11:05 2023 +0000 build yascreen without flto with ancient compilers; install it afterwards commit 1acfcc53b04a05e8bc4439be9ede61cea5cbea0f Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 01:58:11 2023 +0200 maint commit a62c3ed165637998033ff3769f37ceb949fc9567 Merge: 4446bb6 599c4d3 Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 01:52:48 2023 +0200 maint commit 4446bb64dff9cd93213cc375cb24dd3e568be70e Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 01:52:15 2023 +0200 maint commit 599c4d35e26e201f13cd70e078952c3f8cda0eb9 Merge: ffeb537 dbb7b89 Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 01:27:54 2023 +0200 Merge pull request #12 from andy5995/ci/fix-workflow ci: fix workflow, add matrix commit ffeb537c76e03f9ea6988964567fc86f673171bc Merge: c36efbc 2954780 Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 00:59:48 2023 +0200 Merge branch 'master' of github.com:cade-vs/vfu-dist commit c36efbc192a356de08832c6f3475e7eb7af8cd23 Author: Vladi Belperchinov-Shabanski Date: Sat Feb 11 00:59:13 2023 +0200 5.02 commit dbb7b89f02c444419f80cdff3235d1c831b396b0 Author: andy5995 Date: Mon Feb 6 19:19:23 2023 -0600 ci: fix workflow, add matrix commit 2954780fbe7cf374fbf5b38208f84096d43f2cfa Merge: b8faed9 3df362e Author: Vladi Belperchinov-Shabanski Date: Tue Feb 7 01:42:37 2023 +0200 Merge pull request #11 from andy5995/ci/add-initial-workflow ci: add initial workflow commit b8faed984af37b53542874a81da80bd96fec9398 Author: Vladi Belperchinov-Shabanski Date: Fri Feb 3 00:09:51 2023 +0200 temporary-removed-am-files-3 commit 49343bed55d5385b783c701fd5cba8ccab37de6d Author: Vladi Belperchinov-Shabanski Date: Thu Feb 2 23:53:36 2023 +0200 temporary-removed-am-files-2 commit 5c69ca646a302916c075f532c830c18ad58b3eab Author: Vladi Belperchinov-Shabanski Date: Thu Feb 2 23:48:31 2023 +0200 temporary-removed-am-files commit 3d24fb529f95c42f0bb7edfb383eb85b63ef4290 Author: Vladi Belperchinov-Shabanski Date: Thu Feb 2 23:47:58 2023 +0200 cleanup-of-extras commit 3df362ee16e9b148ff1becd94f6063b0f657bf40 Author: andy5995 Date: Tue Jan 31 15:34:25 2023 -0600 ci: add initial workflow commit 3b6a99c7883f0cb8caf35e9b23ce0c813ccd49bf Author: Vladi Belperchinov-Shabanski Date: Tue Jan 31 12:52:08 2023 +0200 renamed-makefile commit 203986fe54f4b5172e88b8bbf2e7a599480bdbd7 Merge: c62c153 fef5e8b Author: Vladi Belperchinov-Shabanski Date: Tue Jan 31 12:51:08 2023 +0200 Merge pull request #10 from bbonev/master Single makefile allowing parallel build commit fef5e8b6f9fbd7f39acbb319cc7b07c74edbb81e Author: Boian Bonev Date: Tue Jan 31 10:26:24 2023 +0000 Single makefile allowing parallel build commit c62c1536967c265828a9048df5bbd79899eab69c Author: Vladi Belperchinov-Shabanski Date: Mon Jan 30 00:51:17 2023 +0200 r5.00 commit 68c1d3bbfa73ccc74cf25ebcdbc4fe08040dfe0b Author: Vladi Belperchinov-Shabanski Date: Sun Nov 20 03:01:48 2022 +0200 -- removed %f quotes since VFU escapes all special chars in filenames now commit c5c896531cdbdd958f9305ffe724b7909f82c7d2 Author: Vladi Belperchinov-Shabanski Date: Tue Oct 11 23:05:32 2022 +0300 maint commit e923698e3a5ca0cc71c1460a279d373bde22e7dc Author: Vladi Belperchinov-Shabanski Date: Thu Mar 10 00:59:42 2022 +0200 added option for scrolling pagestep. options are 1 line (smooth), 30% of the page size and 50% commit 6afcbd2dcd8f443d02d796dfc827c255bbcdf040 Author: Vladi Belperchinov-Shabanski Date: Sun Feb 20 23:44:50 2022 +0200 vfu-4.23 commit b29ff9aaaf2f732644a8b6c8107919472e7ae58e Author: Vladi Belperchinov-Shabanski Date: Wed Dec 1 23:06:41 2021 +0200 version-bump commit 729f7777c230608b13d57ab9a4f995ccec51e4cc Author: Vladi Belperchinov-Shabanski Date: Sun Oct 17 23:40:06 2021 +0300 panelizers-fixes-and-history commit 19168df4db5aecc7ee8e4bbbb0c4e0fa697092fd Author: Vladi Belperchinov-Shabanski Date: Sun Aug 9 04:25:59 2020 +0300 exec-bins-by-enter-is-now-disabled-by-default-in-vfu.conf commit a466e06b0a6dfe06339e59ffadfc42053db63341 Author: Vladi Belperchinov-Shabanski Date: Sun Aug 9 01:28:09 2020 +0300 fixed-term-resize commit 48d0f50493debce675ec57f686077d5b08d9423d Author: Vladi Belperchinov-Shabanski Date: Sat Aug 8 16:47:42 2020 +0300 copy-auth commit 61e6702381b3cd73d8d1727b0128d74f92834d81 Author: Vladi Belperchinov-Shabanski Date: Sat Aug 8 14:51:13 2020 +0300 added-tool-for-pulling-latest-vfu-git-snapshot commit e847051b99f1686d7ce756677f0b0129b11c0ec1 Author: Vladi Belperchinov-Shabanski Date: Sat Aug 8 02:03:51 2020 +0300 prepare-4.21-release commit 3e33b4d48f54c5e5362aa799002ae578c192cd53 Author: Vladi Belperchinov-Shabanski Date: Thu Aug 6 03:29:15 2020 +0300 maint commit 27bafe09351183f170dc48d1bae68078e4b743b7 Author: Vladi Belperchinov-Shabanski Date: Fri Jul 31 01:21:57 2020 +0300 fixed-debian-deb-rx commit d6f0963ecbab9b403aee4f54f122d7c2ac5ad4b0 Author: Vladi Belperchinov-Shabanski Date: Fri Jul 31 00:36:33 2020 +0300 maint commit dbb1b5827ac811d3aa0a2f2c24d29b21aab89bd0 Author: Vladi Belperchinov-Shabanski Date: Fri Jul 31 00:31:02 2020 +0300 lsm-fixed commit 177f04d02ac937eb3ae2ed841011ec2a998abacc Author: Vladi Belperchinov-Shabanski Date: Thu Jul 30 23:18:52 2020 +0300 added-local-rx commit d06e087b723945a1a67e9f5e4bd199d9999a44b3 Merge: 266deb9 0402a9c Author: Vladi Belperchinov-Shabanski Date: Thu Jul 30 21:57:46 2020 +0300 maint commit 266deb91932b3101b62bf3b9088de571d04c511f Author: Vladi Belperchinov-Shabanski Date: Thu Jul 30 21:57:21 2020 +0300 maint commit 0402a9cc5a3ba11f5d9d2da9275ee33c94af329a Merge: 605696c 1337dd2 Author: Vladi Belperchinov-Shabanski Date: Thu Jul 30 21:56:49 2020 +0300 Merge pull request #8 from bbonev/master spelling and similar small changes commit 1337dd20c1226954021e49439760a4075de96286 Author: Boian Bonev Date: Thu Jul 30 21:45:48 2020 +0300 add .txz and .tar.xz archive types commit 391f07e4991c3e271b1489d3e55f2ab1999b4aad Author: Boian Bonev Date: Thu Jul 30 21:15:51 2020 +0300 nuke trailing ws commit 57842e05f8e3c0082e693223d916650f44e675b2 Author: Boian Bonev Date: Thu Jul 30 21:15:29 2020 +0300 fix urls; fix version commit a46dee86d7c9899b652958355034da0411d62d27 Author: Boian Bonev Date: Thu Jul 30 21:12:09 2020 +0300 nuke trailing ws commit 22ba7ea98a64399bcdad398df34b02e09b38721d Author: Boian Bonev Date: Thu Jul 30 21:11:02 2020 +0300 fix spelling commit 605696c62f627195d2e7c86b43d7484db1b719d3 Author: Vladi Belperchinov-Shabanski Date: Thu Jul 30 02:07:25 2020 +0300 maint commit 97d4657379ddf2c45243f8e401cef45c9c6ac645 Author: Vladi Belperchinov-Shabanski Date: Fri Apr 3 02:10:24 2020 +0300 using-separated-vstring-lib-now commit 94ee3163b32e693377fd952f24616e836de123cb Author: Vladi Belperchinov-Shabanski Date: Mon Dec 9 02:35:46 2019 +0200 maint-docs-and-install commit f960c15939ea45ff975fe39ef21c12517596c010 Merge: 360ceb5 9c98d63 Author: Vladi Belperchinov-Shabanski Date: Sat Jan 19 01:30:06 2019 +0200 Merge pull request #5 from andy5995/add_vslib_to_autoconf add vslib and yascreen to build system commit 360ceb59a1133f68287cb6586601693b84409043 Merge: 44c92b5 c2b252e Author: Vladi Belperchinov-Shabanski Date: Sat Jan 19 01:29:40 2019 +0200 Merge pull request #7 from andy5995/01_debian_patch apply debian patch commit 9c98d63c908c351775dacd2eac8a46fab1d98597 Author: andy5995 Date: Wed Jan 16 17:01:56 2019 -0600 configure.ac:specify abs path to yascreen includes commit c2b252e884322cf1163756ba123be35cd2336fef Author: andy5995 Date: Wed Jan 16 08:58:00 2019 -0600 apply debian patch This applies a patch contained in vfu_4.16+repack-1.debian.tar.xz linked on http://deb.debian.org/debian/pool/main/v/vfu/ (Format corrections on the man page) commit 33dd5383719e1cb90fef65a330185193ddb6c17f Author: andy5995 Date: Fri Jan 11 09:42:48 2019 -0600 put USE_YASCREEN def into config.h Also make HAVE_CONFIG_H global commit 3a303608703a00e808e57a39bc0e16b514f83e66 Author: andy5995 Date: Fri Jan 11 08:51:10 2019 -0600 only build yascreen if needed This will only build libyascreen if --enable-yascreen is used, or if curses isn't found. commit b292648e01736c4ff52276b5e105669eef1127da Author: andy5995 Date: Fri Jan 11 03:14:37 2019 -0600 eliminate vfu.yas target This gives the configure option: `--enable-yascreen`. This will build a single binary with no .yas extension. The binary will have yascreen support if that configure option is given, or if curses can't be found. commit 72364b487c8ec1e7ff492308734632365781022a Author: andy5995 Date: Thu Jan 10 05:09:40 2019 -0600 allow 'make vfu.yas' from top build directory commit 63807448cb29792128d732003fddf3d544b9507b Author: andy5995 Date: Thu Jan 10 04:39:13 2019 -0600 allow make check to build library if it's not built yet commit f3283e611f5723c994dda4c607160199e2e87263 Author: andy5995 Date: Thu Jan 10 04:18:17 2019 -0600 add -Wall flag commit 83f508dfec6cf4561205724097103cfd0d551dd2 Author: andy5995 Date: Thu Jan 10 03:49:09 2019 -0600 add vslib and yascreen to build system closes #2 commit 44c92b5228d67a2b47fa8b93ae114e008e1fa033 Merge: 4d97f7b be2b906 Author: Vladi Belperchinov-Shabanski Date: Mon Jan 7 03:06:52 2019 +0200 Merge pull request #3 from andy5995/add_autoools Add autoools build system (#2) commit 4d97f7b3b7de8f856a94cb310489b9353d8651cc Author: Vladi Belperchinov-Shabanski Date: Sun Jan 6 02:04:30 2019 +0200 maint commit be2b906c4eb6a061e13b53299680c3f52860bf3d Author: andy5995 Date: Wed Jan 2 22:54:59 2019 -0600 configure.ac:bump version to 4.18, add PCRE check commit 381f0affa05719282b3940e55e8e0a12b2831f6c Author: Vladi Belperchinov-Shabanski Date: Wed Jan 2 02:36:33 2019 +0200 added-copy-speed-reporting commit f4e49ef2c1b2aa5c3b44a389460f503b720ccb7c Merge: 6f2feed ad742b4 Author: Vladi Belperchinov-Shabanski Date: Mon Dec 31 01:50:23 2018 +0200 Merge branch 'master' of github.com:cade-vs/vfu-dist commit 6f2feed968b4f0f41d219391cfe035de2477601e Author: Vladi Belperchinov-Shabanski Date: Mon Dec 31 01:50:00 2018 +0200 bump-4.18-vfu-dist-added-more-history commit adc4053abbdb484b798108c462354564ebc7a9b3 Author: andy5995 Date: Sat Dec 29 17:26:45 2018 -0600 add files generated and copied when autoreconf is run commit b6d96690b606a3495ec3c23f63a8e86a28e2cbed Author: andy5995 Date: Sat Dec 29 17:26:09 2018 -0600 add an autoconf file and top-level automake file commit ba8c4cacfcc013ef6b5068e2e01f0d1a86522360 Author: andy5995 Date: Sat Dec 29 17:25:24 2018 -0600 create .gitignore commit 68582ad8100704528efb9b977719cf56813b145c Author: andy5995 Date: Sat Dec 29 17:22:27 2018 -0600 rename build to build.sh so as to not conflict with the "build" directory commit ad742b4793c23ddb2f64e03071d3b531eb48e0c3 Merge: 036fe29 be6cab4 Author: Vladi Belperchinov-Shabanski Date: Sat Dec 29 02:37:48 2018 +0200 Merge pull request #1 from andy5995/yascreen_Install INSTALL:expand dependency information commit be6cab4299d0bb9ee0e79afa12990a83d7824f10 Author: andy5995 Date: Tue Dec 25 00:44:59 2018 -0600 INSTALL:expand dependency information I added this because I received this error when compiling vslib: ``` In file included from conmenu.cpp:12:0: ./unicon.h:22:26: fatal error: yascreen.h: No such file or directory #include ^ compilation terminated. makefile:242: recipe for target '.OBJ.libvscony.a/conmenu.o' failed make: *** [.OBJ.libvscony.a/conmenu.o] Error 1 ``` commit 036fe29881bde07605f104e16a3e1f97a005ac72 Author: Vladi Belperchinov-Shabanski Date: Wed Jun 13 18:49:44 2018 +0300 history-bump commit 48106e90e1d8688d8eb6cdd0ac72f28b695ae18c Author: Vladi Belperchinov-Shabanski Date: Wed Jun 13 03:51:29 2018 +0300 added-dynamic-files-list-to-v4.17 commit e74f07fd9d7dbf91b234165302029be9b817c6bb Author: Vladi Belperchinov-Shabanski Date: Sun Jun 10 17:09:25 2018 +0300 new dir sizes calc options: follow symlinks, keep on the same dev/filesystem commit f3b5e6b63620bb8a18abe5fad0cdf6cd1454a0d4 Author: Vladi Belperchinov-Shabanski Date: Fri Mar 24 03:02:12 2017 +0200 maint-docs commit 5509bd2a3688c43d059de12a341d45c568964c59 Author: Vladi Belperchinov-Shabanski Date: Wed Aug 19 02:46:41 2015 +0300 new-mm.conf commit da4849c6e4ec996109c32351f870d9d9dd951c7e Author: Vladi Belperchinov-Shabanski Date: Sun May 10 21:10:08 2015 +0300 maint commit 9637e129eeeaee93649cec3fab22b09ca45b9492 Author: Vladi Belperchinov-Shabanski Date: Sun Jan 5 13:32:08 2014 +0200 maint commit 9dba4d0a1436187d32f089456c67eef4f0ee9687 Author: Vladi Belperchinov-Shabanski Date: Fri Oct 11 01:45:25 2013 +0300 mm commit bf1f65ba16c617c74253e7395544165a745b24bb Author: Vladi Belperchinov-Shabanski Date: Sun Jun 10 23:21:48 2012 +0300 maint commit c09e52dee1cec9b6c5908229b262c006868f9c5f Author: Vladi Belperchinov-Shabanski Date: Tue Jun 5 03:04:00 2012 +0300 maint commit 068273e67c08c1f5df7e27291a3682dea4d840ff Author: Vladi Belperchinov-Shabanski Date: Sun Jun 3 03:06:13 2012 +0300 maint commit ea17c9a608c2794d853c4c0a04fc58b56ea4f3c5 Author: Vladi Belperchinov-Shabanski Date: Wed Dec 21 04:42:45 2011 +0200 maint commit 849496695b8e643899af04e3788e6c7889742ecb Author: Vladi Belperchinov-Shabanski Date: Wed Dec 21 03:18:49 2011 +0200 maint commit f9836db705dfa36df7932e6825712a3750fc192f Author: Vladi Belperchinov-Shabanski Date: Wed Dec 21 02:48:32 2011 +0200 maint commit fb3986557904584a0b85db474cb31028ab0fb541 Author: Vladi Belperchinov-Shabanski Date: Sun Oct 16 18:15:03 2011 +0300 maint commit 0b2bd9ddb10cece6e8305a48fef310e022f345c4 Author: Vladi Belperchinov-Shabanski Date: Tue Oct 11 20:22:05 2011 +0300 new-pcre commit dc8b1ae0cc8c84c295d34e9d1266eba1f6c6f360 Author: Vladi Belperchinov-Shabanski Date: Thu Aug 19 00:48:19 2010 +0300 maint commit 98d5acb17a62736e3108c146f8a3d2e5a8d3cf65 Author: Vladi Belperchinov-Shabanski Date: Thu Dec 17 00:53:23 2009 +0200 maint commit de0b0035bbc477d712610fc54394da9d9f24ca82 Author: Vladi Belperchinov-Shabanski Date: Wed Jul 29 22:21:06 2009 +0300 maint commit f97649984f996409f4b3a0a267a4ccd3b3d6e409 Author: Vladi Belperchinov-Shabanski Date: Tue Jul 21 00:34:55 2009 +0300 maint commit 8344896d8a74ea04923275d1a9f2918b0a312a81 Author: Vladi Belperchinov-Shabanski Date: Wed May 20 01:31:07 2009 +0300 maint commit 89073791f090f680c230f6d12a7e5530c668df47 Author: Vladi Belperchinov-Shabanski Date: Sun Apr 19 15:25:43 2009 +0300 wiki commit 5a6c5f302431f70d5f26ed5efa0348f4562ab046 Author: Vladi Belperchinov-Shabanski Date: Sun Apr 19 15:18:27 2009 +0300 maint commit 4f959317b04312b037d161290b3797eb8af25781 Author: Vladi Belperchinov-Shabanski Date: Sun Apr 19 15:04:33 2009 +0300 maint commit 73f91b80702f5ee4e138a808186632fa745ba332 Author: cade Date: Sat Aug 5 20:15:38 2006 +0000 *** empty log message *** commit e7ccef79ff348eb36f305345657745327ecbf0c0 Author: cade Date: Wed Oct 26 18:59:28 2005 +0000 *** empty log message *** commit 07a1611bd54f9a678d803a80a20dcb59fdae40db Author: cade Date: Wed Oct 26 00:17:02 2005 +0000 *** empty log message *** commit db4375e91bea9eb627655d4efa5d8df0d28b4de5 Author: cade Date: Sun Aug 28 15:03:08 2005 +0000 *** empty log message *** commit 8f6f42b9aac37235d7e4ebe7212c06fa1bc4cc35 Author: cade Date: Wed Jun 15 23:55:58 2005 +0000 *** empty log message *** commit b5900e5eaacb79247cbbb066f13fe85e2df1a08e Author: cade Date: Sun Jun 5 22:02:05 2005 +0000 *** empty log message *** commit 7deb35141c3e145c26f30356796c320878115637 Author: cade Date: Sun Jun 5 19:07:16 2005 +0000 *** empty log message *** commit c124d7ace6719081bfbf440b0b7c4369cdb24f92 Author: cade Date: Fri Jun 3 00:34:55 2005 +0000 *** empty log message *** commit 93f17efa1f79ffc197d4251e1773fb90245db06e Author: cade Date: Sun May 22 14:40:23 2005 +0000 *** empty log message *** commit d35463e92cdf925aa69e904f1c061207def806ba Author: cade Date: Sat Mar 26 21:00:09 2005 +0000 *** empty log message *** commit c3fb58331dc8bd605580b566b6d3ad04720e06c4 Author: cade Date: Wed Dec 29 02:49:46 2004 +0000 *** empty log message *** commit 0589c55b791848fe5bb1efcb0dbb938d73d352eb Author: cade Date: Sat Jul 5 22:37:58 2003 +0000 *** empty log message *** commit 45ac9a1360537830121fafd775170a66ecb9c313 Author: cade Date: Tue May 6 12:12:26 2003 +0000 *** empty log message *** commit 425664a67de673cf24c9cfde41ecf0e82c994a40 Author: cade Date: Mon Apr 28 17:32:00 2003 +0000 *** empty log message *** commit 4bac41298d43150c2d5e05b9d5058bec01f93401 Author: cade Date: Mon Apr 28 17:18:54 2003 +0000 *** empty log message *** commit c290d812ca702f1e630935074553f2a0a9afd7ef Author: cade Date: Sun Apr 27 11:53:10 2003 +0000 *** empty log message *** commit 8362dfac2c228feb03b720438354be3ab025ad35 Author: cade Date: Sun Feb 2 16:22:51 2003 +0000 *** empty log message *** commit 377648c01700008a390468d51b587a3ebddcdff2 Author: cade Date: Sun Dec 15 18:26:58 2002 +0000 *** empty log message *** commit 6cb18b26073b6c4f1e9de778f4058bdd05e3d15e Author: cade Date: Wed Nov 27 21:21:21 2002 +0000 *** empty log message *** commit 04ed87717ab3428b51c04e0e9086357a95896565 Author: cade Date: Thu Nov 7 23:51:09 2002 +0000 *** empty log message *** commit 2df73dd72d7484493f875cd50303f3544450ebbc Author: cade Date: Wed Nov 6 21:25:09 2002 +0000 *** empty log message *** commit 429b458521ea515593f9e94d7a393b3e4bea9888 Author: cade Date: Wed Oct 9 22:03:48 2002 +0000 *** empty log message *** commit 8d8bdaa64a4361312cc5ae09a7618ea4c317793e Author: cade Date: Wed Oct 9 08:46:00 2002 +0000 *** empty log message *** commit 86382a511e3d5a4220f0afd47aac051612c68b63 Author: cade Date: Wed Oct 2 22:37:20 2002 +0000 *** empty log message *** commit 24f2b6181a6d2af7d253fe40f6a8c7898e220125 Author: cade Date: Wed Sep 11 15:39:36 2002 +0000 *** empty log message *** commit c13ddc56408eeba9ce6b6abc0671d06c9906878b Author: cade Date: Sat Aug 17 11:57:04 2002 +0000 *** empty log message *** commit 8f38f26807032e16f59a4796ee223b0e7ce6cf03 Author: cade Date: Wed Jun 5 21:59:00 2002 +0000 *** empty log message *** commit 374f0ca357cc70d35e6fbb6675a1823d8312ff8e Author: cade Date: Fri May 24 17:04:07 2002 +0000 *** empty log message *** commit bb5c608ff97509b7e7a06bf99b62f6a9028de09d Author: cade Date: Fri May 24 16:52:49 2002 +0000 *** empty log message *** commit c3d9174f6a6847c0262b865c7d718ae6736e0bbc Author: cade Date: Fri Apr 26 07:39:37 2002 +0000 few docs fixups commit e257dda8ca9c2b256c45bb45ae6d3af1fbe68121 Author: cade Date: Fri Apr 26 07:27:52 2002 +0000 *** empty log message *** commit 1ffba67716e91a6456dcd605b24b2b28ec9db660 Author: cade Date: Tue Jan 1 16:33:59 2002 +0000 *** empty log message *** commit f510bab857f289ca04a9b0a74c7667c31f42a134 Author: cade Date: Tue Jan 1 16:33:59 2002 +0000 *** empty log message *** commit 078199c2ca07f4fbde2272bb9906d70c91a0d223 Author: cade Date: Tue Jan 1 15:48:47 2002 +0000 *** empty log message *** commit 9ebe8775a27c54297a55737359ce9565edf48a67 Author: cade Date: Mon Nov 26 10:12:21 2001 +0000 *** empty log message *** commit dbe05466d4a1e6cf1542f65e34bd03b237533417 Author: cade Date: Tue Nov 20 19:12:00 2001 +0000 *** empty log message *** commit 9a335be903e6c43430067fe4abbc95acbbd42efd Author: cade Date: Tue Nov 20 19:11:34 2001 +0000 *** empty log message *** commit f2015c6ce607739bb9323fe02b9d04a7f298b932 Author: cade Date: Sun Oct 28 14:12:15 2001 +0000 Initial revision