vfu-4.22/0000755000175000017500000000000014151772750010624 5ustar cadecadevfu-4.22/HISTORY0000644000175000017500000010047214151762146011712 0ustar cadecade ---------------------------------------------------------------------- VFU File Manager by Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg/vfu/ ---------------------------------------------------------------------- HISTORY (CHANGE.LOG) + feature add - feature changed x feature removed ! bugfix % note 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-4.22/vfu.pod0000644000175000017500000000642314151762146012133 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 Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg/ http://cade.datamax.bg/vfu vfu-4.22/vfu.10000644000175000017500000001753414151762146011516 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 "2021-12-01" "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 4 \& Vladi Belperchinov\-Shabanski "Cade" \& \& http://cade.datamax.bg/ \& http://cade.datamax.bg/vfu .Ve vfu-4.22/vfu.conf0000644000175000017500000002465714145574023012305 0ustar cadecade############################################################################################### # # vfu.conf # VFU File Manager config file # Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" # # http://cade.noxrun.com/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. 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 # %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-4.22/Makefile.in0000644000175000017500000007402314145574023012673 0ustar cadecade# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = \ $(top_srcdir)/build-aux/m4/ax_check_enable_debug.m4 \ $(top_srcdir)/build-aux/m4/ax_path_lib_pcre.m4 \ $(top_srcdir)/build-aux/m4/ax_require_defined.m4 \ $(top_srcdir)/build-aux/m4/ax_with_curses.m4 \ $(top_srcdir)/build-aux/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = depcomp = am__depfiles_maybe = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(packextrasdir)" \ "$(DESTDIR)$(sysconfdir)" NROFF = nroff MANS = $(dist_man_MANS) DATA = $(packextras_DATA) $(sysconf_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \ $(srcdir)/config.in $(top_srcdir)/build-aux/compile \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/missing COPYING INSTALL README TODO \ build-aux/compile build-aux/install-sh build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CURSES_CFLAGS = @CURSES_CFLAGS@ CURSES_LIBS = @CURSES_LIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE_CFLAGS = @PCRE_CFLAGS@ PCRE_LIBS = @PCRE_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__leading_dot = @am__leading_dot@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = yas-libs vslib vslib/t vfu packextrasdir = @docdir@ EXTRA_DIST = \ COPYING \ makefile \ README.MAC_OSX \ vfu.html \ build.sh \ build.docs \ THANKS.TO \ vfu.lsm \ build.netbsd \ FAQ \ mm.conf \ TODO \ vfu.pod \ COMPILE.NOTES \ HISTORY \ mm.proba.conf \ vfu.wiki \ CONFIG \ README \ INSTALL \ README.DOS \ XWINDOW.NOTES packextras_DATA = $(EXTRA_DIST) # Install the English man file and include it when `make dist` is used dist_man_MANS = vfu.1 # Install the configuration to $(sysconfdir) sysconf_DATA = vfu.conf ACLOCAL_AMFLAGS = --install -I build-aux/m4 all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign --ignore-deps'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign --ignore-deps \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign --ignore-deps Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 install-man1: $(dist_man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(dist_man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-packextrasDATA: $(packextras_DATA) @$(NORMAL_INSTALL) @list='$(packextras_DATA)'; test -n "$(packextrasdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(packextrasdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(packextrasdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(packextrasdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(packextrasdir)" || exit $$?; \ done uninstall-packextrasDATA: @$(NORMAL_UNINSTALL) @list='$(packextras_DATA)'; test -n "$(packextrasdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(packextrasdir)'; $(am__uninstall_files_from_dir) install-sysconfDATA: $(sysconf_DATA) @$(NORMAL_INSTALL) @list='$(sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sysconfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sysconfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfdir)" || exit $$?; \ done uninstall-sysconfDATA: @$(NORMAL_UNINSTALL) @list='$(sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(sysconfdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(MANS) $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(packextrasdir)" "$(DESTDIR)$(sysconfdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-man install-packextrasDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-sysconfDATA install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-man1 install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-man uninstall-packextrasDATA \ uninstall-sysconfDATA uninstall-man: uninstall-man1 .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-generic distclean-hdr \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-man1 \ install-packextrasDATA install-pdf install-pdf-am install-ps \ install-ps-am install-strip install-sysconfDATA installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-man uninstall-man1 uninstall-packextrasDATA \ uninstall-sysconfDATA .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: vfu-4.22/vstring/0000755000175000017500000000000014151762151012312 5ustar cadecadevfu-4.22/vstring/vstring.h0000644000175000017500000006756514145574056014212 0ustar cadecade/**************************************************************************** * * VSTRING Library * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * * http://cade.datamax.bg/ * Distributed under the GPL license, you should receive copy of GPL! * * 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* type, so there is no need to change function calls * nor the implementation when you change from char* to VString (and * vice versa). The main difference from other similar libs is that * the dynamic VString class has no visible methods (except operators) * so you will use it as a plain char* 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 string, which resizes automatically * * VSTRLIB part (vstrlib.h and vstrlib.cpp) provides string data * structures which mimic Perl's. There are several classes: * * VArray -- array of VString elements * VTrie -- associative array (hash) of VString elements * VRegexp -- regular expression helper class * * VString, VArray, VTrie use shallow copy and copy-on-write functionality, * so things like str1 = str2, varray1 = varray2 etc. are cheap and fast :) * ***************************************************************************/ #ifndef _VSTRING_H_ #define _VSTRING_H_ #include #include #include #include #ifndef ASSERT #define ASSERT assert #endif /*************************************************************************** ** ** GLOBALS ** ****************************************************************************/ #define VARRAY_BLOCK_SIZE 2048 class VTrie; /* forward */ class VArray; /* forward */ class VRegexp; /* forward */ class VString; /* forward */ #define VHash VTrie; /* using casual names... */ #define VRegExp VRegexp; /* using casual names... */ /*************************************************************************** ** ** 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 == 0 ) delete this; } int refs() { return _ref; } }; /**************************************************************************** ** ** VSTRING BOX ** ****************************************************************************/ class VStringBox: public VRef { public: int sl; // string buffer length int size; // internal buffer size char* s; // internal buffer int compact; VStringBox() { s = NULL; sl = size = compact = 0; resize_buf( 0 ); }; ~VStringBox() { undef(); if ( s ) free( s ); }; VStringBox* clone(); void resize_buf( int new_size ); void undef() { resize_buf( 0 ); sl = 0; }; }; /**************************************************************************** ** ** VSTRING ** ****************************************************************************/ #define STR_BLOCK_SIZE 256 class VString; VString& str_copy ( VString& target, const char* source, int pos = 0, int len = -1 ); // returns `len' chars from `pos' VString& str_pad ( VString& target, int len, char ch = ' ' ); VString& str_comma( VString& target, char delim = '\'' ); class VString { VStringBox* box; char retch; // used to return char& for off-range char index void detach(); public: VString( const VString& str ) { box = str.box; box->ref(); }; VString() { box = new VStringBox(); }; VString( const char* ps ) { box = new VStringBox(); set( ps); }; VString( const int n ) { box = new VStringBox(); i(n); }; VString( const long n ) { box = new VStringBox(); l(n); }; VString( const double n ) { box = new VStringBox(); f(n); }; ~VString() { box->unref(); }; void compact( int a_compact ) // set this != 0 for compact (memory preserving) behaviour { box->compact = a_compact; }; //FIXME: detach() first? void resize( int new_size ) { detach(); box->resize_buf( new_size ); }; void undef() { box->unref(); box = new VStringBox(); }; const VString& operator = ( const VString& str ) { box->unref(); box = str.box; box->ref(); return *this; }; const VString& operator = ( const char* ps ) { set(ps);return *this; }; const VString& operator = ( const int n ) { i(n); return *this; }; const VString& operator = ( const long n ) { l(n); return *this; }; const VString& operator = ( const double n ) { f(n); return *this; }; const VString& operator += ( const VString& str ) { cat( str.box->s ); return *this; }; const VString& operator += ( const char* ps ) { cat( ps ); return *this; }; const VString& operator += ( const int n ) { VString tmp = n; cat(tmp); return *this; }; const VString& operator += ( const long n ) { VString tmp = n; cat(tmp); return *this; }; const VString& operator += ( const double n ) { VString tmp = n; cat(tmp); return *this; }; const VString& operator *= ( const int n ) { return str_mul( *this, n ); }; friend VString operator + ( const VString& str1, const VString& str2 ) { VString res = str1; res += str2; return res; }; friend VString operator + ( const VString& str1, const char* ps ) { VString res = str1; res += ps; return res; }; friend VString operator + ( const char* ps, const VString& str2 ) { VString res = ps; res += str2; return res; }; friend VString operator + ( const VString& str1, const int n ) { VString res = str1; res += n; return res; }; friend VString operator + ( const int n, const VString& str2 ) { VString res = n; res += str2; return res; }; friend VString operator + ( const VString& str1, const long n ) { VString res = str1; res += n; return res; }; friend VString operator + ( const long n, const VString& str2 ) { VString res = n; res += str2; return res; }; friend VString operator + ( const VString& str1, const double n ) { VString res = str1; res += n; return res; }; friend VString operator + ( const double n, const VString& str2 ) { VString res = n; res += str2; return res; }; friend int operator == ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) == 0; }; friend int operator == ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) == 0; }; friend int operator == ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) == 0; }; friend int operator != ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) != 0; }; friend int operator != ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) != 0; }; friend int operator != ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) != 0; }; friend int operator > ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) > 0; }; friend int operator > ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) > 0; }; friend int operator > ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) > 0; }; friend int operator >= ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) >= 0; }; friend int operator >= ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) >= 0; }; friend int operator >= ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) >= 0; }; friend int operator < ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) < 0; }; friend int operator < ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) < 0; }; friend int operator < ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) < 0; }; friend int operator <= ( const VString& s1, const VString& s2 ) { return strcmp( s1, s2 ) <= 0; }; friend int operator <= ( const char* s1, const VString& s2 ) { return strcmp( s1, s2 ) <= 0; }; friend int operator <= ( const VString& s1, const char* s2 ) { return strcmp( s1, s2 ) <= 0; }; operator const char* ( ) const { return (const char*)box->s; } const char* data() { return box->s; } 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 = strlen(box->s); ASSERT( box->sl < box->size ); } void fix() { box->sl = strlen(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 f( const double d ); void fi( const double d ); // sets double as int (w/o frac) int i() { return atoi( box->s ); } long l() { return atol( box->s ); } double f() { return atof( box->s ); } double fi() { return atof( box->s ); } void set( const char* ps ); void cat( const char* ps ); void setn( const char* ps, int len ); void catn( const char* ps, int len ); /* for debugging only */ int check() { int len = strlen(box->s); return ((len == box->sl)&&(lensize)); } /**************************************************************************** ** VString Friend Functions (for class VString) ****************************************************************************/ inline friend int str_len( VString& target ) { return target.box->sl; }; inline friend VString& str_set( VString& target, const char* ps ) { target.set( ps ); return target; }; friend VString& str_mul ( VString& target, int n ); // multiplies the VString n times, i.e. "1"*5 = "11111" friend VString& str_del ( VString& target, int pos, int len ); // deletes `len' chars starting from `pos' friend VString& str_ins ( VString& target, int pos, const char* s ); // inserts `s' in position `pos' friend VString& str_ins_ch ( VString& target, int pos, char ch ); // inserts `ch' in position `pos' friend VString& str_replace( VString& target, const char* out, const char* in ); // replace `out' w. `in' friend VString& str_copy ( VString& target, const char* source, int pos, int len ); // returns `len' chars from `pos' friend VString& str_left ( VString& target, const char* source, int len ); // returns `len' chars from the left friend VString& str_right ( VString& target, const char* source, int len ); // returns `len' chars from the right friend VString& str_sleft ( VString& target, int len ); // self-left -- just as 'str_left()' but works on `target' friend VString& str_sright( VString& target, int len ); // self-right -- just as 'str_right()' but works on `target' friend VString& str_trim_left ( VString& target, int len ); // trims `len' chars from the beginning (left) friend VString& str_trim_right( VString& target, int len ); // trim `len' chars from the end (right) friend VString& str_cut_left ( VString& target, const char* charlist ); // remove all chars `charlist' from the beginning (i.e. from the left) friend VString& str_cut_right( VString& target, const char* charlist ); // remove all chars `charlist' from the end (i.e. from the right) friend VString& str_cut ( VString& target, const char* charlist ); // does `str_cut_right(charlist);str_cut_left(charlist);' friend VString& str_cut_spc ( VString& target ); // does `str_cut(" ");' friend VString& str_pad ( VString& target, int len, char ch ); friend VString& str_comma( VString& target, char delim ); // next 3 functions are safe! so if you get/set out f the VString range! friend void str_set_ch( VString& target, int pos, const char ch ); // sets `ch' char at position `pos' friend char str_get_ch( VString& target, int pos ); // return char at position `pos', -1 for the last char etc... friend void str_add_ch( VString& target, const char ch ); // adds `ch' at the end friend char* str_word( VString& target, const char* delimiters, char* result ); friend char* str_rword( VString& target, const char* delimiters, char* result ); // check VArray::split() instead of word() funtions... //FIXME: TODO: str_sprintf() should return VString! // this `sprintf'-like function works as follows: // 1. set `this.VString' 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, VString& target, const char *format, ... ); // this is equal to `printf( 1024, format, ... )', i.e. `init_size=1024' friend int sprintf( VString& target, const char *format, ... ); friend VString& str_tr ( VString& target, const char *from, const char *to ); friend VString& str_up ( VString& target ); friend VString& str_low( VString& target ); friend VString& str_flip_case( VString& target ); friend VString& str_reverse( VString& target ); // reverse the VString: `abcde' becomes `edcba' friend VString& str_squeeze( VString& target, const char* sq_chars ); // squeeze encountered repeating chars to one only }; /* end of VString class */ /**************************************************************************** ** ** VString Functions (for class VString) ** ****************************************************************************/ /**************************************************************************** ** ** VString Functions (for char*) ** ****************************************************************************/ inline int str_len( const char* ps ) { return strlen( ps ); } inline char* str_set( char* target, const char* ps ) { target[0] = 0; if (ps) strcpy( target, ps ); strcpy( target, ps ); return target; } char* str_mul( char* target, int n ); // multiplies the VString n times, i.e. "1"*5 = "11111" char* str_del ( char* target, int pos, int len ); // deletes `len' chars starting from `pos' char* str_ins ( char* target, int pos, const char* s ); // inserts `s' string in position `pos' char* str_ins_ch ( char* target, int pos, char ch ); // inserts `ch' char in position `pos' char* str_replace( char* target, const char* out, const char* in ); // replace `out' w. `in' int str_overlap( const char* target, const char* source, int len = -1 ); // check if source and target overlap, returns 1 if they do char* str_copy ( char* target, const char* source, int pos = 0, int len = -1 ); // returns `len' chars from `pos' char* str_left ( char* target, const char* source, int len ); // returns `len' chars from the left char* str_right ( char* target, const char* source, int len ); // returns `len' chars from the right char* str_sleft ( char* target, int len ); // "self-left" i.e. just as 'left' but works on `target' char* str_sright( char* target, int len ); // "self-right" i.e. just as 'right' but works on `target' char* str_trim_left ( char* target, int len ); // trims `len' chars from the beginning (left) char* str_trim_right( char* target, int len ); // trim `len' chars from the end (right) // next 2 functions are safe. so if you get/set out of the char* length range. // note: that `char*' funcs are slower because of initial strlen() check void str_set_ch( char* target, int pos, const char ch ); // sets `ch' char at position `pos' char str_get_ch( char* target, int pos ); // return char at position `pos', -1 for the last char etc... void str_add_ch( char* target, const char ch ); // adds `ch' at the end // return first `word' (will be stored to `resolt'), // i.e. from pos 0 to first found delimiter char // after that deletes this `word' from the `target' string. // returns NULL when no words left char* str_word ( char* target, const char* delimiters, char* result ); // ...same but `last' word reverse/rear char* str_rword( char* target, const char* delimiters, char* result ); char* str_cut_left ( char* target, const char* charlist ); // remove all chars `charlist' from the beginning (i.e. from the left) char* str_cut_right( char* target, const char* charlist ); // remove all chars `charlist' from the end (i.e. from the right) char* str_cut ( char* target, const char* charlist ); // does `CutR(charlist);CutL(charlist);' char* str_cut_spc ( char* target ); // does `str_cut(" ");' // expand align in a field, filled w. `ch', if len > 0 then right, else left char* str_pad( char* target, int len, char ch = ' ' ); // insert `commas' for 1000's delimiter or use another delimiter // VString supposed to be a integer or real w/o `e' format char* str_comma( char* target, char delim = '\'' ); // translate chars from `from' to `to' // length of `from' MUST be equal to length of `to' char* str_tr( char* target, const char *from, const char *to ); char* str_up ( char* target ); char* str_low( char* target ); char* str_flip_case( char* target ); char* str_reverse( char* target ); // reverse the VString: `abcde' becomes `edcba' char* str_squeeze( char* target, const char* sq_chars ); // squeeze repeating chars to one only /**************************************************************************** ** ** VString Functions (for const char*) ** ****************************************************************************/ VString str_up ( const char* src ); VString str_low( const char* src ); VString str_flip_case( const char* src ); /**************************************************************************** ** ** VString Functions -- common (VString class will pass transparently here) ** ****************************************************************************/ int str_find ( const char* target, int c, int startpos = 0 ); // returns first zero-based position of char, or -1 if not found int str_rfind( const char* target, int c ); // returns last zero-based position of char, or -1 if not found int str_find ( const char* target, const char* s, int startpos = 0 ); // returns first zero-based position of VString, or -1 if not found int str_rfind( const char* target, const char* s ); // returns last zero-based position of VString, or -1 if not found int str_count( const char* target, const char* charlist, int startpos = 0 ); // returns match count of all chars from `charlist' int str_str_count( const char* target, const char* s, int startpos = 0 ); // returns match count of `s' VString into target int str_is_int ( const char* target ); // check if VString is correct int value int str_is_double( const char* target ); // check if VString is correct double (w/o `e' format :( ) /*************************************************************************** ** ** VARRAYBOX ** ****************************************************************************/ class VArrayBox : public VRef { public: VString** _data; int _size; int _count; VArrayBox() { _data = NULL; _size = 0; _count = 0; }; ~VArrayBox() { undef(); }; VArrayBox* clone(); void resize( int new_size ); void undef() { resize( 0 ); }; }; /*************************************************************************** ** ** VARRAY ** ****************************************************************************/ class VArray { VArrayBox *box; int _fe; // foreach element index VString _ret_str; // return-container void detach(); void q_sort( int lo, int hi, int (*q_strcmp)(const char *, const char *) ); public: int compact; VArray(); VArray( const VArray& arr ); VArray( const VTrie& tr ); ~VArray(); int count() { return box->_count; } // return element count void ins( int n, const char* s ); // insert at position `n' void del( int n ); // delete at position `n' void set( int n, const char* s ); // set/replace at position `n' const char* get( int n ); // get at position `n' void undef() // clear the array (frees all elements) { box->unref(); box = new VArrayBox(); _ret_str = ""; } int push( const char* s ); // add to the end of the array int push( VTrie *tr ); // add to the end of the array int push( VArray *arr ); // add to the end of the array const char* pop(); // get and remove the last element int unshift( const char* s ); // add to the beginning of the array int unshift( VTrie *tr ); // add to the beginning of the array int unshift( VArray *arr ); // add to the beginning of the array const char* shift(); // get and remove the first element 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 void sort( int rev = 0, int (*q_strcmp)(const char *, const char *) = NULL ); // sort (optional reverse order) void reverse(); // reverse elements order void shuffle(); // randomize element order with Fisher-Yates shuffle VString& operator []( int n ) { if ( n < 0 ) { _ret_str = ""; return _ret_str; } if ( n >= box->_count ) set( n, "" ); else detach(); // I don't know if user will change returned VString?! return *box->_data[n]; } const VArray& operator = ( const VArray& arr ) { box->unref(); box = arr.box; box->ref(); return *this; }; const VArray& operator = ( const VTrie& tr ) { undef(); push( (VTrie*)&tr ); return *this; }; const VArray& operator = ( const VString& str ) { undef(); push( str ); return *this; }; const VArray& operator += ( const VArray& arr ) { push( (VArray*)&arr ); return *this; }; const VArray& operator += ( const VTrie& tr ) { push( (VTrie*)&tr ); return *this; }; const VArray& operator += ( const VString& str ) { push( str ); return *this; }; /* utilities */ /* implement `foreach'-like interface */ void reset() // reset position to beginning { _fe = -1; }; const char* next() // get next item or NULL for the end { _fe++; return _fe < box->_count ? box->_data[_fe]->data() : NULL; }; const 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 VTrieNode { public: VTrieNode(); ~VTrieNode(); VTrieNode *next; VTrieNode *down; char c; VString *data; void detach() { next = down = NULL; } void del_node( const char *key, int branch = 0 ); VTrieNode* find_node( const char* key, int create = 0 ); VTrieNode *clone(); void print(); }; /*************************************************************************** ** ** VTRIEBOX -- INTERNAL! ** ****************************************************************************/ class VTrieBox : public VRef { public: VTrieNode *root; VTrieBox() { root = new VTrieNode(); } ~VTrieBox() { ASSERT( root ); delete root; } VTrieBox* clone(); void undef() { ASSERT( root ); delete root; root = new VTrieNode(); }; }; /*************************************************************************** ** ** VTRIE ** ****************************************************************************/ class VTrie { VTrieBox *box; void detach(); void trace_node( VTrieNode *node, VArray* keys, VArray *vals ); VString temp_key; public: int compact; VTrie(); VTrie( const VArray& arr ); VTrie( const VTrie& tr ); ~VTrie(); void set( const char* key, const char* data ); // set data, same as []= void del( const char* key ); // remove data associated with `key' const char* get( const char* key ); // get data by `key', same as [] int exists( const char* key ); // return != 0 if key exist (i.e. is used) void undef() // delete all key+data pairs { box->unref(); box = new VTrieBox(); } void keys_and_values( VArray *keys, VArray *values ); VArray keys(); // returns VArray with keys currently used VArray values(); // returns VArray with keys' values void reverse(); // reverse keys <-> values void merge( VTrie *tr ); // adds keys+values (or modify existing keys) void merge( VArray *arr ); // adds keys+values (by VArray pair values) //void print_nodes() { print_node( root ); }; // for debug only void print(); // print trie 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 VString& operator []( const char* key ) { detach(); // I don't know if user will change returned VString?! VTrieNode *node = box->root->find_node( key, 1 ); ASSERT( node ); if ( ! node->data ) node->data = new VString(); return *(node->data); } const VTrie& operator = ( const VTrie& tr ) { box->unref(); box = tr.box; box->ref(); return *this; }; const VTrie& operator = ( const VArray& arr ) { undef(); merge( (VArray*)&arr ); return *this; }; const VTrie& operator += ( const VArray& arr ) { merge( (VArray*)&arr ); return *this; }; const VTrie& operator += ( const VTrie& tr ) { merge( (VTrie*)&tr ); return *this; }; }; /**************************************************************************** ** ** VString Utility functions ** ****************************************************************************/ // str_chop() removes last char from a VString (perl-like) inline char* str_chop( char* target ) { return str_trim_right( target, 1 ); } inline VString& str_chop( VString& target ) { return str_trim_right( target, 1 ); } /* reduces VString to the given width using dots: "this is long line" -> "this...ine" `s' can be NULL, then target will be reduced */ VString str_dot_reduce( const char* s, int width ); /**************************************************************************** ** ** VString file names utilities -- functions and classes ** NOTE: does not use any external (outside this library) function calls! ** ****************************************************************************/ // adds trailing '/' if not exist char* str_fix_path( char* s, int slashtype = '/' ); const char* str_fix_path( VString& s, int slashtype = '/' ); VString str_file_ext( const char *ps ); // `ext' VString str_file_name( const char *ps ); // `filename' VString str_file_name_ext( const char *ps ); // `filename.ext' VString str_file_path( const char *ps ); // `/path/' /* removes "/../"s, `path' can be NULL, then dest is fixed */ VString str_reduce_path( const char* path ); /**************************************************************************** ** ** VString Conversions ** ****************************************************************************/ long hex2long( const char* s ); // hex to long #endif /* _VSTRING_H_ */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-4.22/vstring/makefile0000644000175000017500000000763014145576076014034 0ustar cadecade ### MAKEMAKE STARTS HERE ####################################################### ### Created by makemake.pl on Fri Nov 19 03:21:26 2021 ######################### ### GLOBAL TARGETS ############################################################# default: mm_update all re: mm_update rebuild li: mm_update link all: mm_update modules vstring.a test clean: mm_update clean-modules clean-vstring.a clean-test rebuild: mm_update rebuild-modules rebuild-vstring.a rebuild-test link: mm_update link-modules link-vstring.a link-test ### GLOBAL (AND USER) DEFS ##################################################### AR ?= ar LD = $(CXX) MKDIR = mkdir -p MODULES = pcre2 RANLIB ?= ranlib RMDIR = rm -rf RMFILE = rm -f SRC = *.c *.cpp *.cc *.cxx ### TARGET 1: libvstring.a ##################################################### CC_1 = $(CXX) LD_1 = $(CXX) AR_1 = $(AR) rv RANLIB_1 = $(RANLIB) CCFLAGS_1 = -I. -Ipcre2 -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_1 = $(LDFLAGS) $(LDDEF) DEPFLAGS_1 = ARFLAGS_1 = TARGET_1 = libvstring.a ### SOURCES FOR TARGET 1: libvstring.a ######################################### SRC_1= \ vstring.cpp \ vstrlib.cpp \ #### OBJECTS FOR TARGET 1: libvstring.a ######################################## OBJ_1= \ .OBJ.vstring.a/vstring.o \ .OBJ.vstring.a/vstrlib.o \ ### TARGET DEFINITION FOR TARGET 1: libvstring.a ############################### .OBJ.vstring.a: $(MKDIR) .OBJ.vstring.a vstring.a: .OBJ.vstring.a $(OBJ_1) $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) clean-vstring.a: $(RMFILE) $(TARGET_1) $(RMDIR) .OBJ.vstring.a rebuild-vstring.a: clean-vstring.a vstring.a re-vstring.a: rebuild-vstring.a link-vstring.a: .OBJ.vstring.a $(OBJ_1) $(RMFILE) libvstring.a $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) ### TARGET OBJECTS FOR TARGET 1: libvstring.a ################################## .OBJ.vstring.a/vstring.o: vstring.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vstring.cpp -o .OBJ.vstring.a/vstring.o .OBJ.vstring.a/vstrlib.o: vstrlib.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vstrlib.cpp -o .OBJ.vstring.a/vstrlib.o ### TARGET 2: test ############################################################# CC_2 = $(CXX) LD_2 = $(CXX) AR_2 = $(AR) rv RANLIB_2 = $(RANLIB) CCFLAGS_2 = -I. -Ipcre2 -O2 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_2 = -Lpcre2 -lpcre2 $(LDFLAGS) $(LDDEF) DEPFLAGS_2 = ARFLAGS_2 = TARGET_2 = test ### SOURCES FOR TARGET 2: test ################################################# SRC_2= \ vstring.cpp \ vstrlib.cpp \ test.cpp \ #### OBJECTS FOR TARGET 2: test ################################################ OBJ_2= \ .OBJ.test/vstring.o \ .OBJ.test/vstrlib.o \ .OBJ.test/test.o \ ### TARGET DEFINITION FOR TARGET 2: test ####################################### .OBJ.test: $(MKDIR) .OBJ.test test: .OBJ.test $(OBJ_2) $(LD_2) $(OBJ_2) $(LDFLAGS_2) -o $(TARGET_2) clean-test: $(RMFILE) $(TARGET_2) $(RMDIR) .OBJ.test rebuild-test: clean-test test re-test: rebuild-test link-test: .OBJ.test $(OBJ_2) $(RMFILE) test $(LD_2) $(OBJ_2) $(LDFLAGS_2) -o $(TARGET_2) ### TARGET OBJECTS FOR TARGET 2: test ########################################## .OBJ.test/vstring.o: vstring.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vstring.cpp -o .OBJ.test/vstring.o .OBJ.test/vstrlib.o: vstrlib.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vstrlib.cpp -o .OBJ.test/vstrlib.o .OBJ.test/test.o: test.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c test.cpp -o .OBJ.test/test.o ### MODULES #################################################################### modules: $(MAKE) -C pcre2 clean-modules: $(MAKE) -C pcre2 clean rebuild-modules: $(MAKE) -C pcre2 rebuild link-modules: $(MAKE) -C pcre2 link mm_update: ### MAKEMAKE ENDS HERE ######################################################### vfu-4.22/vstring/TODO0000644000175000017500000000020314151762151012775 0ustar cadecade-- get_prematch() $` -- get_match() $& -- get_postmatch() $' -- replace() $1 $2 ... $` $& $' ... vfu-4.22/vstring/test.cpp0000644000175000017500000003120614151762151013777 0ustar cadecade/**************************************************************************** * * VSTRING Library * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * * http://cade.datamax.bg/ * Distributed under the GPL license, you should receive copy of GPL! * * 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* type, so there is no need to change function calls * nor the implementation when you change from char* to VString (and * vice versa). The main difference from other similar libs is that * the dynamic VString class has no visible methods (except operators) * so you will use it as a plain char* but it will expand/shrink as * needed. * * If you find bug or you have note about vstring lib, please feel * free to contact me. * ***************************************************************************/ #include #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"; // 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.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", str_len( set ) ); printf( "cat = %d\n", str_len( cat ) ); printf( "setn = %d\n", str_len( setn ) ); printf( "catn = %d\n", str_len( catn ) ); printf( "sete = %d\n", str_len( sete ) ); printf( "setp = %d\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: %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: [%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(); } int main( int argc, char* argv[] ) { /* 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 } /**/ 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-4.22/vstring/vstrlib.cpp0000644000175000017500000005724214151762151014515 0ustar cadecade /**************************************************************************** * * VSTRING Library * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * * http://cade.datamax.bg/ * Distributed under the GPL license, you should receive copy of GPL! * * 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* type, so there is no need to change function calls * nor the implementation when you change from char* to VString (and * vice versa). The main difference from other similar libs is that * the dynamic VString class has no visible methods (except operators) * so you will use it as a plain char* 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 string, which resizes automatically * * VSTRLIB part (vstrlib.h and vstrlib.cpp) provides string data * structures which mimic Perl's. There are several classes: * * VArray -- array of VString elements * VTrie -- associative array (hash) of VString elements * VRegexp -- regular expression helper class * * VString, VArray, VTrie use shallow copy and copy-on-write functionality, * so things like str1 = str2, varray1 = varray2 etc. are cheap and fast :) * ***************************************************************************/ #ifdef WIN32 #include "stdafx.h" #endif #include #include "vstrlib.h" /**************************************************************************** ** ** VString aditional functions ** ****************************************************************************/ char* time2str( const time_t tim ) { time_t t = tim; return ctime( &t ); } time_t str2time( const char* timstr ) { if (strlen( timstr ) < 24) return 0; char ts[32]; struct tm m; memset( &m, 0, sizeof(m) ); 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 = atoi( ts + 20 ) - 1900; ts[19] = 0; m.tm_sec = atoi( ts + 17 ); ts[16] = 0; m.tm_min = atoi( ts + 14 ); ts[13] = 0; m.tm_hour = atoi( ts + 11 ); ts[10] = 0; m.tm_mday = atoi( ts + 8 ); ts[ 7] = 0; m.tm_mon = str_find( "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC", ts+4 ) / 3; m.tm_yday = 0; m.tm_wday = 0; m.tm_isdst = -1; time_t tim = mktime( &m ); return tim; } int str_find_regexp( const char* target, const char* pattern, int startpos ) { VRegexp 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 char* target, const char* pattern ) { VRegexp 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( int ch ) { ch = 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 char *str, char* pattern ) { const 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; } /***************************************************************************** ** ** Knuth-Morris-Pratt search ** *****************************************************************************/ void __kmp_preprocess( const 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 char *p, int ps, const 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 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 char)p[i]] = ps - i; } int mem_quick_search( const char *p, int ps, const 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 char)d[j + ps]]; } return -1; } /* no-case version */ void __qs_preprocess_nc( const 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((unsigned char)p[i])] = ps - i; } int mem_quick_search_nc( const char *p, int ps, const 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((unsigned 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 char *p, int ps, const 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 char *p, int ps, FILE* f, const char* opt, int (*mem_search)( const char *p, int ps, const char *d, int ds ) ) { #define BUFSIZE (1024*1024) char* buff = new char[BUFSIZE]; int nocase = str_find( opt, 'i' ) > -1; char* np = new 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 char *p, int ps, const char* fn, const char* opt, int (*mem_search)( const char *p, int ps, const 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 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 char *re_string, FILE* f, int nocase, off_t spos ) { if ( strlen(re_string) >= (size_t)file_grep_max_line ) return -2; // just in case, and for now... char newpat[MAX_PATTERN+1]; strcpy( newpat, re_string ); if ( nocase ) str_up( newpat ); VRegexp re; if ( ! re.comp( newpat ) ) return -2; char *line = (char*)malloc( file_grep_max_line+1 ); 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; } /***************************************************************************** ** ** Search interface functions ** ** options are: ** ** i -- ignore case ** r -- regular expression (grep) ** h -- hex pattern search ** *****************************************************************************/ long file_string_search( const char *p, const char* fn, const 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 char *p, FILE *f, const char* opt ) { int ps = strlen(p); ASSERT( ps < MAX_PATTERN ); int nocase = str_find( opt, 'i' ) > -1; long pos = -1; if( str_find( opt, 'r' ) > -1 ) { pos = file_grep( p, f, 0, -1 ); } else if( str_find( opt, 'h' ) > -1 ) { 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 ? "i" : "" ); } else { pos = file_pattern_search( p, strlen(p), f, nocase ? "i" : "" ); } return pos; } int mem_string_search( const char *p, const char* d, const char* opt ) { int ps = strlen(p); ASSERT( ps < MAX_PATTERN ); int nocase = str_find( opt, 'i' ) > -1; long pos = -1; if( str_find( opt, 'r' ) > -1 ) { VRegexp re; if ( ! re.comp( p ) ) return -1; if ( ! re.m( d ) ) return -1; pos = re.sub_sp( 0 ); } else if( str_find( opt, 'h' ) > -1 ) { 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, strlen(d) ); else pos = mem_quick_search( new_p, pl, d, strlen(d) ); } } else { if ( nocase ) pos = mem_quick_search_nc( p, ps, d, strlen(d) ); else pos = mem_quick_search( p, ps, d, strlen(d) ); } return pos; } /*************************************************************************** ** ** VREGEXP ** ****************************************************************************/ VRegexp::VRegexp() { re = NULL; md = NULL; rc = 0; lp = NULL; pt = NULL; pl = 0; } VRegexp::VRegexp( const char* rs, const char* opt ) { re = NULL; md = NULL; rc = 0; lp = NULL; pt = NULL; pl = 0; opt_mode = MODE_REGEXP; comp( rs, opt ); } VRegexp::~VRegexp() { if ( re ) pcre2_code_free( re ); if ( md ) pcre2_match_data_free( md ); if ( pt ) delete pt; } int VRegexp::get_options( const char* opt ) { opt_mode = MODE_REGEXP; opt_nocase = 0; if ( ! opt ) return 0; if ( ! opt[0] ) return 0; int options = 0; int sl = strlen( opt ); int z; for( z = 0; z < sl; z++ ) { switch( opt[z] ) { case 'i': options |= PCRE2_CASELESS; opt_nocase = 1; break; case 'm': options |= PCRE2_MULTILINE; break; case 's': options |= PCRE2_DOTALL; break; case 'x': options |= PCRE2_EXTENDED; break; case 'f': opt_mode = MODE_FIND; break; case 'h': opt_mode = MODE_HEX; break; case 'r': opt_mode = MODE_REGEXP; break; default: errstr = "invalid option, expected are: imsxfhr"; return -1; } } return options; } int VRegexp::comp( const char* pattern, const 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 = "OK"; return 1; } else { char errbuf[256]; pcre2_get_error_message( errorcode, (PCRE2_UCHAR*)errbuf, sizeof(errbuf)/2 ); errstr = errbuf; errstr += ", at pos "; errstr += (int)erroffset; return 0; } } else { pl = strlen( pattern ); pt = new char[pl+1]; if ( opt_mode == MODE_HEX ) pl = hex_string_to_pattern( pattern, pt ); else strcpy( pt, pattern ); pt[pl] = 0; return pl; } } int VRegexp::study() { return 1; // not implemented } int VRegexp::ok() { if ( opt_mode == MODE_REGEXP ) return re != NULL; else return pt != NULL && pl > 0; } int VRegexp::m( const char* line ) { if ( ! ok() ) { errstr = "no pattern compiled"; return 0; } if ( ! line ) { errstr = "no data to search into"; return 0; } errstr = ""; 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; rc = pcre2_match( re, (PCRE2_SPTR)lp, strlen(lp), 0, options, md, NULL); if ( rc < 1 ) rc = 0; return rc; } else { if ( opt_nocase ) pos = mem_quick_search_nc( pt, pl, line, strlen(lp) ); else pos = mem_quick_search( pt, pl, line, strlen(lp) ); return pos >= 0; } } int VRegexp::m( const char* line, const char* pattern, const char *opt ) { comp( pattern, opt ); return m( line ); } VString VRegexp::sub( int n ) { VString 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 ); int s = ovector[n*2]; int e = ovector[n*2+1]; if ( s == PCRE2_UNSET || e == PCRE2_UNSET ) return substr; int l = e - s; substr.setn( lp + s, l ); } else { if ( n != 0 ) return substr; substr.setn( lp + pos, pl ); } return substr; } int VRegexp::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 VRegexp::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; } } /*************************************************************************** ** ** VCHARSET ** ****************************************************************************/ VCharSet::VCharSet() { _data = NULL; _size = 0; } VCharSet::~VCharSet() { _data = NULL; _size = 0; } void VCharSet::resize( int new_size ) { if ( new_size < 1 ) { if ( _data ) delete [] _data; _data = NULL; _size = 0; return; } // calc required mem size in unsigned chars (bytes?) new_size = new_size / sizeof(unsigned char) + (new_size % sizeof(unsigned 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 unsigned chars (bytes?) new_size *= VCHARSET_BLOCK_SIZE; unsigned char *new_data = new unsigned 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 VCharSet::push( int n, int val ) { if ( n < 0 ) return; if ( n >= _size * (int)sizeof(unsigned char) ) resize( n + 1 ); if ( val ) _data[ n / sizeof(unsigned char) ] |= 1 << (n % sizeof(unsigned char)); else _data[ n / sizeof(unsigned char) ] &= ~(1 << (n % sizeof(unsigned char))); } void VCharSet::undef( int n ) { push( n, 0 ); } void VCharSet::undef() { resize( 0 ); } int VCharSet::in( int n ) { if ( n < 0 || n >= _size * (int)sizeof(unsigned char) ) return 0; return ( _data[ n / sizeof(unsigned char) ] & ( 1 << ( n % sizeof(unsigned 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 = strlen( str ); for( int z = 0; z < sl; z++ ) set1( str[z] ); } void VCharSet::set_str0( const char* str ) { int sl = strlen( str ); for( int z = 0; z < sl; z++ ) set0( str[z] ); } int VCharSet::in( const char *str ) { int sl = strlen( 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; } */ /*************************************************************************** ** ** UTILITIES ** ****************************************************************************/ // split `source' with `regexp_str' regexp VArray str_split( const char* regexp_str, const char* source, int maxcount ) { VArray arr; VRegexp re; int z = re.comp( regexp_str ); ASSERT( z ); if ( ! z ) return arr; const char* ps = source; while( ps && ps[0] && re.m( ps ) ) { if ( maxcount != -1 ) { maxcount--; if ( maxcount == 0 ) break; } VString 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' VArray str_split_simple( const char* delimiter_str, const char* source, int maxcount ) { VArray arr; const char* ps = source; const char* fs; int rl = strlen( delimiter_str ); VString s; while( (fs = 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 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' VString str_join( VArray array, const char* glue ) { VString str; for( int z = 0; z < array.count()-1; z++ ) { str += array.get( z ); str += glue; } str += array.get( array.count()-1 ); return str; } /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-4.22/vstring/pcre2/0000755000175000017500000000000014145574056013335 5ustar cadecadevfu-4.22/vstring/pcre2/pcre2_auto_possess.c0000644000175000017500000011535614145574056017336 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains functions that scan a compiled pattern and change repeats into possessive repeats where possible. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Tables for auto-possessification * *************************************************/ /* This table is used to check whether auto-possessification is possible between adjacent character-type opcodes. The left-hand (repeated) opcode is used to select the row, and the right-hand opcode is use to select the column. A value of 1 means that auto-possessification is OK. For example, the second value in the first row means that \D+\d can be turned into \D++\d. The Unicode property types (\P and \p) have to be present to fill out the table because of what their opcode values are, but the table values should always be zero because property types are handled separately in the code. The last four columns apply to items that cannot be repeated, so there is no need to have rows for them. Note that OP_DIGIT etc. are generated only when PCRE_UCP is *not* set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */ #define APTROWS (LAST_AUTOTAB_LEFT_OP - FIRST_AUTOTAB_OP + 1) #define APTCOLS (LAST_AUTOTAB_RIGHT_OP - FIRST_AUTOTAB_OP + 1) static const uint8_t autoposstab[APTROWS][APTCOLS] = { /* \D \d \S \s \W \w . .+ \C \P \p \R \H \h \V \v \X \Z \z $ $M */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \D */ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \d */ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \S */ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \s */ { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \W */ { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \w */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* . */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* .+ */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \C */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \P */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \p */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \R */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \H */ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \h */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \V */ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* \v */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 } /* \X */ }; #ifdef SUPPORT_UNICODE /* This table is used to check whether auto-possessification is possible between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP). The left-hand (repeated) opcode is used to select the row, and the right-hand opcode is used to select the column. The values are as follows: 0 Always return FALSE (never auto-possessify) 1 Character groups are distinct (possessify if both are OP_PROP) 2 Check character categories in the same group (general or particular) 3 TRUE if the two opcodes are not the same (PROP vs NOTPROP) 4 Check left general category vs right particular category 5 Check right general category vs left particular category 6 Left alphanum vs right general category 7 Left space vs right general category 8 Left word vs right general category 9 Right alphanum vs left general category 10 Right space vs left general category 11 Right word vs left general category 12 Left alphanum vs right particular category 13 Left space vs right particular category 14 Left word vs right particular category 15 Right alphanum vs left particular category 16 Right space vs left particular category 17 Right word vs left particular category */ static const uint8_t propposstab[PT_TABSIZE][PT_TABSIZE] = { /* ANY LAMP GC PC SC ALNUM SPACE PXSPACE WORD CLIST UCNC */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_ANY */ { 0, 3, 0, 0, 0, 3, 1, 1, 0, 0, 0 }, /* PT_LAMP */ { 0, 0, 2, 4, 0, 9, 10, 10, 11, 0, 0 }, /* PT_GC */ { 0, 0, 5, 2, 0, 15, 16, 16, 17, 0, 0 }, /* PT_PC */ { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, /* PT_SC */ { 0, 3, 6, 12, 0, 3, 1, 1, 0, 0, 0 }, /* PT_ALNUM */ { 0, 1, 7, 13, 0, 1, 3, 3, 1, 0, 0 }, /* PT_SPACE */ { 0, 1, 7, 13, 0, 1, 3, 3, 1, 0, 0 }, /* PT_PXSPACE */ { 0, 0, 8, 14, 0, 0, 1, 1, 3, 0, 0 }, /* PT_WORD */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_CLIST */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 } /* PT_UCNC */ }; /* This table is used to check whether auto-possessification is possible between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP) when one specifies a general category and the other specifies a particular category. The row is selected by the general category and the column by the particular category. The value is 1 if the particular category is not part of the general category. */ static const uint8_t catposstab[7][30] = { /* Cc Cf Cn Co Cs Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc Pd Pe Pf Pi Po Ps Sc Sk Sm So Zl Zp Zs */ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* C */ { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* L */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* M */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* N */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 }, /* P */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }, /* S */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 } /* Z */ }; /* This table is used when checking ALNUM, (PX)SPACE, SPACE, and WORD against a general or particular category. The properties in each row are those that apply to the character set in question. Duplication means that a little unnecessary work is done when checking, but this keeps things much simpler because they can all use the same code. For more details see the comment where this table is used. Note: SPACE and PXSPACE used to be different because Perl excluded VT from "space", but from Perl 5.18 it's included, so both categories are treated the same here. */ static const uint8_t posspropstab[3][4] = { { ucp_L, ucp_N, ucp_N, ucp_Nl }, /* ALNUM, 3rd and 4th values redundant */ { ucp_Z, ucp_Z, ucp_C, ucp_Cc }, /* SPACE and PXSPACE, 2nd value redundant */ { ucp_L, ucp_N, ucp_P, ucp_Po } /* WORD */ }; #endif /* SUPPORT_UNICODE */ #ifdef SUPPORT_UNICODE /************************************************* * Check a character and a property * *************************************************/ /* This function is called by compare_opcodes() when a property item is adjacent to a fixed character. Arguments: c the character ptype the property type pdata the data for the type negated TRUE if it's a negated property (\P or \p{^) Returns: TRUE if auto-possessifying is OK */ static BOOL check_char_prop(uint32_t c, unsigned int ptype, unsigned int pdata, BOOL negated) { const uint32_t *p; const ucd_record *prop = GET_UCD(c); switch(ptype) { case PT_LAMP: return (prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt) == negated; case PT_GC: return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated; case PT_PC: return (pdata == prop->chartype) == negated; case PT_SC: return (pdata == prop->script) == negated; /* These are specials */ case PT_ALNUM: return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: return negated; default: return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == negated; } break; /* Control never reaches here */ case PT_WORD: return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE) == negated; case PT_CLIST: p = PRIV(ucd_caseless_sets) + prop->caseset; for (;;) { if (c < *p) return !negated; if (c == *p++) return negated; } break; /* Control never reaches here */ } return FALSE; } #endif /* SUPPORT_UNICODE */ /************************************************* * Base opcode of repeated opcodes * *************************************************/ /* Returns the base opcode for repeated single character type opcodes. If the opcode is not a repeated character type, it returns with the original value. Arguments: c opcode Returns: base opcode for the type */ static PCRE2_UCHAR get_repeat_base(PCRE2_UCHAR c) { return (c > OP_TYPEPOSUPTO)? c : (c >= OP_TYPESTAR)? OP_TYPESTAR : (c >= OP_NOTSTARI)? OP_NOTSTARI : (c >= OP_NOTSTAR)? OP_NOTSTAR : (c >= OP_STARI)? OP_STARI : OP_STAR; } /************************************************* * Fill the character property list * *************************************************/ /* Checks whether the code points to an opcode that can take part in auto- possessification, and if so, fills a list with its properties. Arguments: code points to start of expression utf TRUE if in UTF mode ucp TRUE if in UCP mode fcc points to the case-flipping table list points to output list list[0] will be filled with the opcode list[1] will be non-zero if this opcode can match an empty character string list[2..7] depends on the opcode Returns: points to the start of the next opcode if *code is accepted NULL if *code is not accepted */ static PCRE2_SPTR get_chr_property_list(PCRE2_SPTR code, BOOL utf, BOOL ucp, const uint8_t *fcc, uint32_t *list) { PCRE2_UCHAR c = *code; PCRE2_UCHAR base; PCRE2_SPTR end; uint32_t chr; #ifdef SUPPORT_UNICODE uint32_t *clist_dest; const uint32_t *clist_src; #else (void)utf; /* Suppress "unused parameter" compiler warnings */ (void)ucp; #endif list[0] = c; list[1] = FALSE; code++; if (c >= OP_STAR && c <= OP_TYPEPOSUPTO) { base = get_repeat_base(c); c -= (base - OP_STAR); if (c == OP_UPTO || c == OP_MINUPTO || c == OP_EXACT || c == OP_POSUPTO) code += IMM2_SIZE; list[1] = (c != OP_PLUS && c != OP_MINPLUS && c != OP_EXACT && c != OP_POSPLUS); switch(base) { case OP_STAR: list[0] = OP_CHAR; break; case OP_STARI: list[0] = OP_CHARI; break; case OP_NOTSTAR: list[0] = OP_NOT; break; case OP_NOTSTARI: list[0] = OP_NOTI; break; case OP_TYPESTAR: list[0] = *code; code++; break; } c = list[0]; } switch(c) { case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_ANYNL: case OP_NOT_HSPACE: case OP_HSPACE: case OP_NOT_VSPACE: case OP_VSPACE: case OP_EXTUNI: case OP_EODN: case OP_EOD: case OP_DOLL: case OP_DOLLM: return code; case OP_CHAR: case OP_NOT: GETCHARINCTEST(chr, code); list[2] = chr; list[3] = NOTACHAR; return code; case OP_CHARI: case OP_NOTI: list[0] = (c == OP_CHARI) ? OP_CHAR : OP_NOT; GETCHARINCTEST(chr, code); list[2] = chr; #ifdef SUPPORT_UNICODE if (chr < 128 || (chr < 256 && !utf && !ucp)) list[3] = fcc[chr]; else list[3] = UCD_OTHERCASE(chr); #elif defined SUPPORT_WIDE_CHARS list[3] = (chr < 256) ? fcc[chr] : chr; #else list[3] = fcc[chr]; #endif /* The othercase might be the same value. */ if (chr == list[3]) list[3] = NOTACHAR; else list[4] = NOTACHAR; return code; #ifdef SUPPORT_UNICODE case OP_PROP: case OP_NOTPROP: if (code[0] != PT_CLIST) { list[2] = code[0]; list[3] = code[1]; return code + 2; } /* Convert only if we have enough space. */ clist_src = PRIV(ucd_caseless_sets) + code[1]; clist_dest = list + 2; code += 2; do { if (clist_dest >= list + 8) { /* Early return if there is not enough space. This should never happen, since all clists are shorter than 5 character now. */ list[2] = code[0]; list[3] = code[1]; return code; } *clist_dest++ = *clist_src; } while(*clist_src++ != NOTACHAR); /* All characters are stored. The terminating NOTACHAR is copied from the clist itself. */ list[0] = (c == OP_PROP) ? OP_CHAR : OP_NOT; return code; #endif case OP_NCLASS: case OP_CLASS: #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: if (c == OP_XCLASS) end = code + GET(code, 0) - 1; else #endif end = code + 32 / sizeof(PCRE2_UCHAR); switch(*end) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSQUERY: list[1] = TRUE; end++; break; case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSPLUS: end++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: list[1] = (GET2(end, 1) == 0); end += 1 + 2 * IMM2_SIZE; break; } list[2] = (uint32_t)(end - code); return end; } return NULL; /* Opcode not accepted */ } /************************************************* * Scan further character sets for match * *************************************************/ /* Checks whether the base and the current opcode have a common character, in which case the base cannot be possessified. Arguments: code points to the byte code utf TRUE in UTF mode ucp TRUE in UCP mode cb compile data block base_list the data list of the base opcode base_end the end of the base opcode rec_limit points to recursion depth counter Returns: TRUE if the auto-possessification is possible */ static BOOL compare_opcodes(PCRE2_SPTR code, BOOL utf, BOOL ucp, const compile_block *cb, const uint32_t *base_list, PCRE2_SPTR base_end, int *rec_limit) { PCRE2_UCHAR c; uint32_t list[8]; const uint32_t *chr_ptr; const uint32_t *ochr_ptr; const uint32_t *list_ptr; PCRE2_SPTR next_code; #ifdef SUPPORT_WIDE_CHARS PCRE2_SPTR xclass_flags; #endif const uint8_t *class_bitset; const uint8_t *set1, *set2, *set_end; uint32_t chr; BOOL accepted, invert_bits; BOOL entered_a_group = FALSE; if (--(*rec_limit) <= 0) return FALSE; /* Recursion has gone too deep */ /* Note: the base_list[1] contains whether the current opcode has a greedy (represented by a non-zero value) quantifier. This is a different from other character type lists, which store here that the character iterator matches to an empty string (also represented by a non-zero value). */ for(;;) { /* All operations move the code pointer forward. Therefore infinite recursions are not possible. */ c = *code; /* Skip over callouts */ if (c == OP_CALLOUT) { code += PRIV(OP_lengths)[c]; continue; } if (c == OP_CALLOUT_STR) { code += GET(code, 1 + 2*LINK_SIZE); continue; } /* At the end of a branch, skip to the end of the group. */ if (c == OP_ALT) { do code += GET(code, 1); while (*code == OP_ALT); c = *code; } /* Inspect the next opcode. */ switch(c) { /* We can always possessify a greedy iterator at the end of the pattern, which is reached after skipping over the final OP_KET. A non-greedy iterator must never be possessified. */ case OP_END: return base_list[1] != 0; /* When an iterator is at the end of certain kinds of group we can inspect what follows the group by skipping over the closing ket. Note that this does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given iteration is variable (could be another iteration or could be the next item). As these two opcodes are not listed in the next switch, they will end up as the next code to inspect, and return FALSE by virtue of being unsupported. */ case OP_KET: case OP_KETRPOS: /* The non-greedy case cannot be converted to a possessive form. */ if (base_list[1] == 0) return FALSE; /* If the bracket is capturing it might be referenced by an OP_RECURSE so its last iterator can never be possessified if the pattern contains recursions. (This could be improved by keeping a list of group numbers that are called by recursion.) */ switch(*(code - GET(code, 1))) { case OP_CBRA: case OP_SCBRA: case OP_CBRAPOS: case OP_SCBRAPOS: if (cb->had_recurse) return FALSE; break; /* A script run might have to backtrack if the iterated item can match characters from more than one script. So give up unless repeating an explicit character. */ case OP_SCRIPT_RUN: if (base_list[0] != OP_CHAR && base_list[0] != OP_CHARI) return FALSE; break; /* Atomic sub-patterns and assertions can always auto-possessify their last iterator. However, if the group was entered as a result of checking a previous iterator, this is not possible. */ case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: return !entered_a_group; /* Non-atomic assertions - don't possessify last iterator. This needs more thought. */ case OP_ASSERT_NA: case OP_ASSERTBACK_NA: return FALSE; } /* Skip over the bracket and inspect what comes next. */ code += PRIV(OP_lengths)[c]; continue; /* Handle cases where the next item is a group. */ case OP_ONCE: case OP_BRA: case OP_CBRA: next_code = code + GET(code, 1); code += PRIV(OP_lengths)[c]; /* Check each branch. We have to recurse a level for all but the last branch. */ while (*next_code == OP_ALT) { if (!compare_opcodes(code, utf, ucp, cb, base_list, base_end, rec_limit)) return FALSE; code = next_code + 1 + LINK_SIZE; next_code += GET(next_code, 1); } entered_a_group = TRUE; continue; case OP_BRAZERO: case OP_BRAMINZERO: next_code = code + 1; if (*next_code != OP_BRA && *next_code != OP_CBRA && *next_code != OP_ONCE) return FALSE; do next_code += GET(next_code, 1); while (*next_code == OP_ALT); /* The bracket content will be checked by the OP_BRA/OP_CBRA case above. */ next_code += 1 + LINK_SIZE; if (!compare_opcodes(next_code, utf, ucp, cb, base_list, base_end, rec_limit)) return FALSE; code += PRIV(OP_lengths)[c]; continue; /* The next opcode does not need special handling; fall through and use it to see if the base can be possessified. */ default: break; } /* We now have the next appropriate opcode to compare with the base. Check for a supported opcode, and load its properties. */ code = get_chr_property_list(code, utf, ucp, cb->fcc, list); if (code == NULL) return FALSE; /* Unsupported */ /* If either opcode is a small character list, set pointers for comparing characters from that list with another list, or with a property. */ if (base_list[0] == OP_CHAR) { chr_ptr = base_list + 2; list_ptr = list; } else if (list[0] == OP_CHAR) { chr_ptr = list + 2; list_ptr = base_list; } /* Character bitsets can also be compared to certain opcodes. */ else if (base_list[0] == OP_CLASS || list[0] == OP_CLASS #if PCRE2_CODE_UNIT_WIDTH == 8 /* In 8 bit, non-UTF mode, OP_CLASS and OP_NCLASS are the same. */ || (!utf && (base_list[0] == OP_NCLASS || list[0] == OP_NCLASS)) #endif ) { #if PCRE2_CODE_UNIT_WIDTH == 8 if (base_list[0] == OP_CLASS || (!utf && base_list[0] == OP_NCLASS)) #else if (base_list[0] == OP_CLASS) #endif { set1 = (uint8_t *)(base_end - base_list[2]); list_ptr = list; } else { set1 = (uint8_t *)(code - list[2]); list_ptr = base_list; } invert_bits = FALSE; switch(list_ptr[0]) { case OP_CLASS: case OP_NCLASS: set2 = (uint8_t *) ((list_ptr == list ? code : base_end) - list_ptr[2]); break; #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: xclass_flags = (list_ptr == list ? code : base_end) - list_ptr[2] + LINK_SIZE; if ((*xclass_flags & XCL_HASPROP) != 0) return FALSE; if ((*xclass_flags & XCL_MAP) == 0) { /* No bits are set for characters < 256. */ if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0; /* Might be an empty repeat. */ continue; } set2 = (uint8_t *)(xclass_flags + 1); break; #endif case OP_NOT_DIGIT: invert_bits = TRUE; /* Fall through */ case OP_DIGIT: set2 = (uint8_t *)(cb->cbits + cbit_digit); break; case OP_NOT_WHITESPACE: invert_bits = TRUE; /* Fall through */ case OP_WHITESPACE: set2 = (uint8_t *)(cb->cbits + cbit_space); break; case OP_NOT_WORDCHAR: invert_bits = TRUE; /* Fall through */ case OP_WORDCHAR: set2 = (uint8_t *)(cb->cbits + cbit_word); break; default: return FALSE; } /* Because the bit sets are unaligned bytes, we need to perform byte comparison here. */ set_end = set1 + 32; if (invert_bits) { do { if ((*set1++ & ~(*set2++)) != 0) return FALSE; } while (set1 < set_end); } else { do { if ((*set1++ & *set2++) != 0) return FALSE; } while (set1 < set_end); } if (list[1] == 0) return TRUE; /* Might be an empty repeat. */ continue; } /* Some property combinations also acceptable. Unicode property opcodes are processed specially; the rest can be handled with a lookup table. */ else { uint32_t leftop, rightop; leftop = base_list[0]; rightop = list[0]; #ifdef SUPPORT_UNICODE accepted = FALSE; /* Always set in non-unicode case. */ if (leftop == OP_PROP || leftop == OP_NOTPROP) { if (rightop == OP_EOD) accepted = TRUE; else if (rightop == OP_PROP || rightop == OP_NOTPROP) { int n; const uint8_t *p; BOOL same = leftop == rightop; BOOL lisprop = leftop == OP_PROP; BOOL risprop = rightop == OP_PROP; BOOL bothprop = lisprop && risprop; /* There's a table that specifies how each combination is to be processed: 0 Always return FALSE (never auto-possessify) 1 Character groups are distinct (possessify if both are OP_PROP) 2 Check character categories in the same group (general or particular) 3 Return TRUE if the two opcodes are not the same ... see comments below */ n = propposstab[base_list[2]][list[2]]; switch(n) { case 0: break; case 1: accepted = bothprop; break; case 2: accepted = (base_list[3] == list[3]) != same; break; case 3: accepted = !same; break; case 4: /* Left general category, right particular category */ accepted = risprop && catposstab[base_list[3]][list[3]] == same; break; case 5: /* Right general category, left particular category */ accepted = lisprop && catposstab[list[3]][base_list[3]] == same; break; /* This code is logically tricky. Think hard before fiddling with it. The posspropstab table has four entries per row. Each row relates to one of PCRE's special properties such as ALNUM or SPACE or WORD. Only WORD actually needs all four entries, but using repeats for the others means they can all use the same code below. The first two entries in each row are Unicode general categories, and apply always, because all the characters they include are part of the PCRE character set. The third and fourth entries are a general and a particular category, respectively, that include one or more relevant characters. One or the other is used, depending on whether the check is for a general or a particular category. However, in both cases the category contains more characters than the specials that are defined for the property being tested against. Therefore, it cannot be used in a NOTPROP case. Example: the row for WORD contains ucp_L, ucp_N, ucp_P, ucp_Po. Underscore is covered by ucp_P or ucp_Po. */ case 6: /* Left alphanum vs right general category */ case 7: /* Left space vs right general category */ case 8: /* Left word vs right general category */ p = posspropstab[n-6]; accepted = risprop && lisprop == (list[3] != p[0] && list[3] != p[1] && (list[3] != p[2] || !lisprop)); break; case 9: /* Right alphanum vs left general category */ case 10: /* Right space vs left general category */ case 11: /* Right word vs left general category */ p = posspropstab[n-9]; accepted = lisprop && risprop == (base_list[3] != p[0] && base_list[3] != p[1] && (base_list[3] != p[2] || !risprop)); break; case 12: /* Left alphanum vs right particular category */ case 13: /* Left space vs right particular category */ case 14: /* Left word vs right particular category */ p = posspropstab[n-12]; accepted = risprop && lisprop == (catposstab[p[0]][list[3]] && catposstab[p[1]][list[3]] && (list[3] != p[3] || !lisprop)); break; case 15: /* Right alphanum vs left particular category */ case 16: /* Right space vs left particular category */ case 17: /* Right word vs left particular category */ p = posspropstab[n-15]; accepted = lisprop && risprop == (catposstab[p[0]][base_list[3]] && catposstab[p[1]][base_list[3]] && (base_list[3] != p[3] || !risprop)); break; } } } else #endif /* SUPPORT_UNICODE */ accepted = leftop >= FIRST_AUTOTAB_OP && leftop <= LAST_AUTOTAB_LEFT_OP && rightop >= FIRST_AUTOTAB_OP && rightop <= LAST_AUTOTAB_RIGHT_OP && autoposstab[leftop - FIRST_AUTOTAB_OP][rightop - FIRST_AUTOTAB_OP]; if (!accepted) return FALSE; if (list[1] == 0) return TRUE; /* Might be an empty repeat. */ continue; } /* Control reaches here only if one of the items is a small character list. All characters are checked against the other side. */ do { chr = *chr_ptr; switch(list_ptr[0]) { case OP_CHAR: ochr_ptr = list_ptr + 2; do { if (chr == *ochr_ptr) return FALSE; ochr_ptr++; } while(*ochr_ptr != NOTACHAR); break; case OP_NOT: ochr_ptr = list_ptr + 2; do { if (chr == *ochr_ptr) break; ochr_ptr++; } while(*ochr_ptr != NOTACHAR); if (*ochr_ptr == NOTACHAR) return FALSE; /* Not found */ break; /* Note that OP_DIGIT etc. are generated only when PCRE2_UCP is *not* set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */ case OP_DIGIT: if (chr < 256 && (cb->ctypes[chr] & ctype_digit) != 0) return FALSE; break; case OP_NOT_DIGIT: if (chr > 255 || (cb->ctypes[chr] & ctype_digit) == 0) return FALSE; break; case OP_WHITESPACE: if (chr < 256 && (cb->ctypes[chr] & ctype_space) != 0) return FALSE; break; case OP_NOT_WHITESPACE: if (chr > 255 || (cb->ctypes[chr] & ctype_space) == 0) return FALSE; break; case OP_WORDCHAR: if (chr < 255 && (cb->ctypes[chr] & ctype_word) != 0) return FALSE; break; case OP_NOT_WORDCHAR: if (chr > 255 || (cb->ctypes[chr] & ctype_word) == 0) return FALSE; break; case OP_HSPACE: switch(chr) { HSPACE_CASES: return FALSE; default: break; } break; case OP_NOT_HSPACE: switch(chr) { HSPACE_CASES: break; default: return FALSE; } break; case OP_ANYNL: case OP_VSPACE: switch(chr) { VSPACE_CASES: return FALSE; default: break; } break; case OP_NOT_VSPACE: switch(chr) { VSPACE_CASES: break; default: return FALSE; } break; case OP_DOLL: case OP_EODN: switch (chr) { case CHAR_CR: case CHAR_LF: case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ return FALSE; } break; case OP_EOD: /* Can always possessify before \z */ break; #ifdef SUPPORT_UNICODE case OP_PROP: case OP_NOTPROP: if (!check_char_prop(chr, list_ptr[2], list_ptr[3], list_ptr[0] == OP_NOTPROP)) return FALSE; break; #endif case OP_NCLASS: if (chr > 255) return FALSE; /* Fall through */ case OP_CLASS: if (chr > 255) break; class_bitset = (uint8_t *) ((list_ptr == list ? code : base_end) - list_ptr[2]); if ((class_bitset[chr >> 3] & (1u << (chr & 7))) != 0) return FALSE; break; #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: if (PRIV(xclass)(chr, (list_ptr == list ? code : base_end) - list_ptr[2] + LINK_SIZE, utf)) return FALSE; break; #endif default: return FALSE; } chr_ptr++; } while(*chr_ptr != NOTACHAR); /* At least one character must be matched from this opcode. */ if (list[1] == 0) return TRUE; } /* Control never reaches here. There used to be a fail-save return FALSE; here, but some compilers complain about an unreachable statement. */ } /************************************************* * Scan compiled regex for auto-possession * *************************************************/ /* Replaces single character iterations with their possessive alternatives if appropriate. This function modifies the compiled opcode! Hitting a non-existent opcode may indicate a bug in PCRE2, but it can also be caused if a bad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches overly complicated or large patterns. In these cases, the check just stops, leaving the remainder of the pattern unpossessified. Arguments: code points to start of the byte code cb compile data block Returns: 0 for success -1 if a non-existant opcode is encountered */ int PRIV(auto_possessify)(PCRE2_UCHAR *code, const compile_block *cb) { PCRE2_UCHAR c; PCRE2_SPTR end; PCRE2_UCHAR *repeat_opcode; uint32_t list[8]; int rec_limit = 1000; /* Was 10,000 but clang+ASAN uses a lot of stack. */ BOOL utf = (cb->external_options & PCRE2_UTF) != 0; BOOL ucp = (cb->external_options & PCRE2_UCP) != 0; for (;;) { c = *code; if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */ if (c >= OP_STAR && c <= OP_TYPEPOSUPTO) { c -= get_repeat_base(c) - OP_STAR; end = (c <= OP_MINUPTO) ? get_chr_property_list(code, utf, ucp, cb->fcc, list) : NULL; list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO; if (end != NULL && compare_opcodes(end, utf, ucp, cb, list, end, &rec_limit)) { switch(c) { case OP_STAR: *code += OP_POSSTAR - OP_STAR; break; case OP_MINSTAR: *code += OP_POSSTAR - OP_MINSTAR; break; case OP_PLUS: *code += OP_POSPLUS - OP_PLUS; break; case OP_MINPLUS: *code += OP_POSPLUS - OP_MINPLUS; break; case OP_QUERY: *code += OP_POSQUERY - OP_QUERY; break; case OP_MINQUERY: *code += OP_POSQUERY - OP_MINQUERY; break; case OP_UPTO: *code += OP_POSUPTO - OP_UPTO; break; case OP_MINUPTO: *code += OP_POSUPTO - OP_MINUPTO; break; } } c = *code; } else if (c == OP_CLASS || c == OP_NCLASS || c == OP_XCLASS) { #ifdef SUPPORT_WIDE_CHARS if (c == OP_XCLASS) repeat_opcode = code + GET(code, 1); else #endif repeat_opcode = code + 1 + (32 / sizeof(PCRE2_UCHAR)); c = *repeat_opcode; if (c >= OP_CRSTAR && c <= OP_CRMINRANGE) { /* The return from get_chr_property_list() will never be NULL when *code (aka c) is one of the three class opcodes. However, gcc with -fanalyzer notes that a NULL return is possible, and grumbles. Hence we put in a check. */ end = get_chr_property_list(code, utf, ucp, cb->fcc, list); list[1] = (c & 1) == 0; if (end != NULL && compare_opcodes(end, utf, ucp, cb, list, end, &rec_limit)) { switch (c) { case OP_CRSTAR: case OP_CRMINSTAR: *repeat_opcode = OP_CRPOSSTAR; break; case OP_CRPLUS: case OP_CRMINPLUS: *repeat_opcode = OP_CRPOSPLUS; break; case OP_CRQUERY: case OP_CRMINQUERY: *repeat_opcode = OP_CRPOSQUERY; break; case OP_CRRANGE: case OP_CRMINRANGE: *repeat_opcode = OP_CRPOSRANGE; break; } } } c = *code; } switch(c) { case OP_END: return 0; case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; break; case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSUPTO: if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; break; case OP_CALLOUT_STR: code += GET(code, 1 + 2*LINK_SIZE); break; #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: code += GET(code, 1); break; #endif case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: code += code[1]; break; } /* Add in the fixed length from the table */ code += PRIV(OP_lengths)[c]; /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be followed by a multi-byte character. The length in the table is a minimum, so we have to arrange to skip the extra code units. */ #ifdef MAYBE_UTF_MULTI if (utf) switch(c) { case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_QUERY: case OP_MINQUERY: case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_QUERYI: case OP_MINQUERYI: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); break; } #else (void)(utf); /* Keep compiler happy by referencing function argument */ #endif /* SUPPORT_WIDE_CHARS */ } } /* End of pcre2_auto_possess.c */ vfu-4.22/vstring/pcre2/pcre2_tables.c0000644000175000017500000010623314145574056016053 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2019 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains some fixed tables that are used by more than one of the PCRE2 code modules. The tables are also #included by the pcre2test program, which uses macros to change their names from _pcre2_xxx to xxxx, thereby avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is defined. */ #ifndef PCRE2_PCRE2TEST /* We're compiling the library */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #endif /* PCRE2_PCRE2TEST */ /* Table of sizes for the fixed-length opcodes. It's defined in a macro so that the definition is next to the definition of the opcodes in pcre2_internal.h. This is mode-dependent, so is skipped when this file is included by pcre2test. */ #ifndef PCRE2_PCRE2TEST const uint8_t PRIV(OP_lengths)[] = { OP_LENGTHS }; #endif /* Tables of horizontal and vertical whitespace characters, suitable for adding to classes. */ const uint32_t PRIV(hspace_list)[] = { HSPACE_LIST }; const uint32_t PRIV(vspace_list)[] = { VSPACE_LIST }; /* These tables are the pairs of delimiters that are valid for callout string arguments. For each starting delimiter there must be a matching ending delimiter, which in fact is different only for bracket-like delimiters. */ const uint32_t PRIV(callout_start_delims)[] = { CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK, CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN, CHAR_DOLLAR_SIGN, CHAR_LEFT_CURLY_BRACKET, 0 }; const uint32_t PRIV(callout_end_delims[]) = { CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK, CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN, CHAR_DOLLAR_SIGN, CHAR_RIGHT_CURLY_BRACKET, 0 }; /************************************************* * Tables for UTF-8 support * *************************************************/ /* These tables are required by pcre2test in 16- or 32-bit mode, as well as for the library in 8-bit mode, because pcre2test uses UTF-8 internally for handling wide characters. */ #if defined PCRE2_PCRE2TEST || \ (defined SUPPORT_UNICODE && \ defined PCRE2_CODE_UNIT_WIDTH && \ PCRE2_CODE_UNIT_WIDTH == 8) /* These are the breakpoints for different numbers of bytes in a UTF-8 character. */ const int PRIV(utf8_table1)[] = { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; const int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int); /* These are the indicator bits and the mask for the data bits to set in the first byte of a character, indexed by the number of additional bytes. */ const int PRIV(utf8_table2)[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; const int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; /* Table of the number of extra bytes, indexed by the first byte masked with 0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */ const uint8_t PRIV(utf8_table4)[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; #endif /* UTF-8 support needed */ #ifdef SUPPORT_UNICODE /* Table to translate from particular type value to the general value. */ const uint32_t PRIV(ucp_gentype)[] = { ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */ ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */ ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */ ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */ ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */ ucp_P, ucp_P, /* Ps, Po */ ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */ ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */ }; /* This table encodes the rules for finding the end of an extended grapheme cluster. Every code point has a grapheme break property which is one of the ucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions 10 and 11. The 2-dimensional table is indexed by the properties of two adjacent code points. The left property selects a word from the table, and the right property selects a bit from that word like this: PRIV(ucp_gbtable)[left-property] & (1u << right-property) The value is non-zero if a grapheme break is NOT permitted between the relevant two code points. The breaking rules are as follows: 1. Break at the start and end of text (pretty obviously). 2. Do not break between a CR and LF; otherwise, break before and after controls. 3. Do not break Hangul syllable sequences, the rules for which are: L may be followed by L, V, LV or LVT LV or V may be followed by V or T LVT or T may be followed by T 4. Do not break before extending characters or zero-width-joiner (ZWJ). The following rules are only for extended grapheme clusters (but that's what we are implementing). 5. Do not break before SpacingMarks. 6. Do not break after Prepend characters. 7. Do not break within emoji modifier sequences or emoji zwj sequences. That is, do not break between characters with the Extended_Pictographic property. Extend and ZWJ characters are allowed between the characters; this cannot be represented in this table, the code has to deal with it. 8. Do not break within emoji flag sequences. That is, do not break between regional indicator (RI) symbols if there are an odd number of RI characters before the break point. This table encodes "join RI characters"; the code has to deal with checking for previous adjoining RIs. 9. Otherwise, break everywhere. */ #define ESZ (1< 0 when length requested < 0 on error or unset value */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_pattern_info(const pcre2_code *code, uint32_t what, void *where) { const pcre2_real_code *re = (pcre2_real_code *)code; if (where == NULL) /* Requests field length */ { switch(what) { case PCRE2_INFO_ALLOPTIONS: case PCRE2_INFO_ARGOPTIONS: case PCRE2_INFO_BACKREFMAX: case PCRE2_INFO_BSR: case PCRE2_INFO_CAPTURECOUNT: case PCRE2_INFO_DEPTHLIMIT: case PCRE2_INFO_EXTRAOPTIONS: case PCRE2_INFO_FIRSTCODETYPE: case PCRE2_INFO_FIRSTCODEUNIT: case PCRE2_INFO_HASBACKSLASHC: case PCRE2_INFO_HASCRORLF: case PCRE2_INFO_HEAPLIMIT: case PCRE2_INFO_JCHANGED: case PCRE2_INFO_LASTCODETYPE: case PCRE2_INFO_LASTCODEUNIT: case PCRE2_INFO_MATCHEMPTY: case PCRE2_INFO_MATCHLIMIT: case PCRE2_INFO_MAXLOOKBEHIND: case PCRE2_INFO_MINLENGTH: case PCRE2_INFO_NAMEENTRYSIZE: case PCRE2_INFO_NAMECOUNT: case PCRE2_INFO_NEWLINE: return sizeof(uint32_t); case PCRE2_INFO_FIRSTBITMAP: return sizeof(const uint8_t *); case PCRE2_INFO_JITSIZE: case PCRE2_INFO_SIZE: case PCRE2_INFO_FRAMESIZE: return sizeof(size_t); case PCRE2_INFO_NAMETABLE: return sizeof(PCRE2_SPTR); } } if (re == NULL) return PCRE2_ERROR_NULL; /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; /* Check that this pattern was compiled in the correct bit mode */ if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE; switch(what) { case PCRE2_INFO_ALLOPTIONS: *((uint32_t *)where) = re->overall_options; break; case PCRE2_INFO_ARGOPTIONS: *((uint32_t *)where) = re->compile_options; break; case PCRE2_INFO_BACKREFMAX: *((uint32_t *)where) = re->top_backref; break; case PCRE2_INFO_BSR: *((uint32_t *)where) = re->bsr_convention; break; case PCRE2_INFO_CAPTURECOUNT: *((uint32_t *)where) = re->top_bracket; break; case PCRE2_INFO_DEPTHLIMIT: *((uint32_t *)where) = re->limit_depth; if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET; break; case PCRE2_INFO_EXTRAOPTIONS: *((uint32_t *)where) = re->extra_options; break; case PCRE2_INFO_FIRSTCODETYPE: *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 : ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0; break; case PCRE2_INFO_FIRSTCODEUNIT: *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? re->first_codeunit : 0; break; case PCRE2_INFO_FIRSTBITMAP: *((const uint8_t **)where) = ((re->flags & PCRE2_FIRSTMAPSET) != 0)? &(re->start_bitmap[0]) : NULL; break; case PCRE2_INFO_FRAMESIZE: *((size_t *)where) = offsetof(heapframe, ovector) + re->top_bracket * 2 * sizeof(PCRE2_SIZE); break; case PCRE2_INFO_HASBACKSLASHC: *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0; break; case PCRE2_INFO_HASCRORLF: *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0; break; case PCRE2_INFO_HEAPLIMIT: *((uint32_t *)where) = re->limit_heap; if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET; break; case PCRE2_INFO_JCHANGED: *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0; break; case PCRE2_INFO_JITSIZE: #ifdef SUPPORT_JIT *((size_t *)where) = (re->executable_jit != NULL)? PRIV(jit_get_size)(re->executable_jit) : 0; #else *((size_t *)where) = 0; #endif break; case PCRE2_INFO_LASTCODETYPE: *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)? 1 : 0; break; case PCRE2_INFO_LASTCODEUNIT: *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)? re->last_codeunit : 0; break; case PCRE2_INFO_MATCHEMPTY: *((uint32_t *)where) = (re->flags & PCRE2_MATCH_EMPTY) != 0; break; case PCRE2_INFO_MATCHLIMIT: *((uint32_t *)where) = re->limit_match; if (re->limit_match == UINT32_MAX) return PCRE2_ERROR_UNSET; break; case PCRE2_INFO_MAXLOOKBEHIND: *((uint32_t *)where) = re->max_lookbehind; break; case PCRE2_INFO_MINLENGTH: *((uint32_t *)where) = re->minlength; break; case PCRE2_INFO_NAMEENTRYSIZE: *((uint32_t *)where) = re->name_entry_size; break; case PCRE2_INFO_NAMECOUNT: *((uint32_t *)where) = re->name_count; break; case PCRE2_INFO_NAMETABLE: *((PCRE2_SPTR *)where) = (PCRE2_SPTR)((char *)re + sizeof(pcre2_real_code)); break; case PCRE2_INFO_NEWLINE: *((uint32_t *)where) = re->newline_convention; break; case PCRE2_INFO_SIZE: *((size_t *)where) = re->blocksize; break; default: return PCRE2_ERROR_BADOPTION; } return 0; } /************************************************* * Callout enumerator * *************************************************/ /* Arguments: code points to compiled code callback function called for each callout block callout_data user data passed to the callback Returns: 0 when successfully completed < 0 on local error != 0 for callback error */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_callout_enumerate(const pcre2_code *code, int (*callback)(pcre2_callout_enumerate_block *, void *), void *callout_data) { pcre2_real_code *re = (pcre2_real_code *)code; pcre2_callout_enumerate_block cb; PCRE2_SPTR cc; #ifdef SUPPORT_UNICODE BOOL utf; #endif if (re == NULL) return PCRE2_ERROR_NULL; #ifdef SUPPORT_UNICODE utf = (re->overall_options & PCRE2_UTF) != 0; #endif /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; /* Check that this pattern was compiled in the correct bit mode */ if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE; cb.version = 0; cc = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; while (TRUE) { int rc; switch (*cc) { case OP_END: return 0; case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_QUERY: case OP_MINQUERY: case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_QUERYI: case OP_MINQUERYI: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: cc += PRIV(OP_lengths)[*cc]; #ifdef SUPPORT_UNICODE if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: case OP_TYPEPOSUPTO: cc += PRIV(OP_lengths)[*cc]; #ifdef SUPPORT_UNICODE if (cc[-1] == OP_PROP || cc[-1] == OP_NOTPROP) cc += 2; #endif break; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: cc += GET(cc, 1); break; #endif case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: cc += PRIV(OP_lengths)[*cc] + cc[1]; break; case OP_CALLOUT: cb.pattern_position = GET(cc, 1); cb.next_item_length = GET(cc, 1 + LINK_SIZE); cb.callout_number = cc[1 + 2*LINK_SIZE]; cb.callout_string_offset = 0; cb.callout_string_length = 0; cb.callout_string = NULL; rc = callback(&cb, callout_data); if (rc != 0) return rc; cc += PRIV(OP_lengths)[*cc]; break; case OP_CALLOUT_STR: cb.pattern_position = GET(cc, 1); cb.next_item_length = GET(cc, 1 + LINK_SIZE); cb.callout_number = 0; cb.callout_string_offset = GET(cc, 1 + 3*LINK_SIZE); cb.callout_string_length = GET(cc, 1 + 2*LINK_SIZE) - (1 + 4*LINK_SIZE) - 2; cb.callout_string = cc + (1 + 4*LINK_SIZE) + 1; rc = callback(&cb, callout_data); if (rc != 0) return rc; cc += GET(cc, 1 + 2*LINK_SIZE); break; default: cc += PRIV(OP_lengths)[*cc]; break; } } } /* End of pcre2_pattern_info.c */ vfu-4.22/vstring/pcre2/LICENCE0000644000175000017500000000662514145574056014333 0ustar cadecadePCRE2 LICENCE ------------- PCRE2 is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD" licence, as specified below, with one exemption for certain binary redistributions. The documentation for PCRE2, supplied in the "doc" directory, is distributed under the same terms as the software itself. The data in the testdata directory is not copyrighted and is in the public domain. The basic library functions are written in C and are freestanding. Also included in the distribution is a just-in-time compiler that can be used to optimize pattern matching. This is an optional feature that can be omitted when the library is built. THE BASIC LIBRARY FUNCTIONS --------------------------- Written by: Philip Hazel Email local part: Philip.Hazel Email domain: gmail.com Retired from University of Cambridge Computing Service, Cambridge, England. Copyright (c) 1997-2021 University of Cambridge All rights reserved. PCRE2 JUST-IN-TIME COMPILATION SUPPORT -------------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Email domain: freemail.hu Copyright(c) 2010-2021 Zoltan Herczeg All rights reserved. STACK-LESS JUST-IN-TIME COMPILER -------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Email domain: freemail.hu Copyright(c) 2009-2021 Zoltan Herczeg All rights reserved. THE "BSD" LICENCE ----------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notices, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notices, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES ------------------------------------------ The second condition in the BSD licence (covering binary redistributions) does not apply all the way down a chain of software. If binary package A includes PCRE2, it must respect the condition, but if package B is software that includes package A, the condition is not imposed on package B unless it uses PCRE2 independently. End vfu-4.22/vstring/pcre2/pcre2_intmodedep.h0000644000175000017500000011063314145574056016735 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains mode-dependent macro and structure definitions. The file is #included by pcre2_internal.h if PCRE2_CODE_UNIT_WIDTH is defined. These mode-dependent items are kept in a separate file so that they can also be #included multiple times for different code unit widths by pcre2test in order to have access to the hidden structures at all supported widths. Some of the mode-dependent macros are required at different widths for different parts of the pcre2test code (in particular, the included pcre_printint.c file). We undefine them here so that they can be re-defined for multiple inclusions. Not all of these are used in pcre2test, but it's easier just to undefine them all. */ #undef ACROSSCHAR #undef BACKCHAR #undef BYTES2CU #undef CHMAX_255 #undef CU2BYTES #undef FORWARDCHAR #undef FORWARDCHARTEST #undef GET #undef GET2 #undef GETCHAR #undef GETCHARINC #undef GETCHARINCTEST #undef GETCHARLEN #undef GETCHARLENTEST #undef GETCHARTEST #undef GET_EXTRALEN #undef HAS_EXTRALEN #undef IMM2_SIZE #undef MAX_255 #undef MAX_MARK #undef MAX_PATTERN_SIZE #undef MAX_UTF_SINGLE_CU #undef NOT_FIRSTCU #undef PUT #undef PUT2 #undef PUT2INC #undef PUTCHAR #undef PUTINC #undef TABLE_GET /* -------------------------- MACROS ----------------------------- */ /* PCRE keeps offsets in its compiled code as at least 16-bit quantities (always stored in big-endian order in 8-bit mode) by default. These are used, for example, to link from the start of a subpattern to its alternatives and its end. The use of 16 bits per offset limits the size of an 8-bit compiled regex to around 64K, which is big enough for almost everybody. However, I received a request for an even bigger limit. For this reason, and also to make the code easier to maintain, the storing and loading of offsets from the compiled code unit string is now handled by the macros that are defined here. The macros are controlled by the value of LINK_SIZE. This defaults to 2, but values of 3 or 4 are also supported. */ /* ------------------- 8-bit support ------------------ */ #if PCRE2_CODE_UNIT_WIDTH == 8 #if LINK_SIZE == 2 #define PUT(a,n,d) \ (a[n] = (PCRE2_UCHAR)((d) >> 8)), \ (a[(n)+1] = (PCRE2_UCHAR)((d) & 255)) #define GET(a,n) \ (unsigned int)(((a)[n] << 8) | (a)[(n)+1]) #define MAX_PATTERN_SIZE (1 << 16) #elif LINK_SIZE == 3 #define PUT(a,n,d) \ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 8)), \ (a[(n)+2] = (PCRE2_UCHAR)((d) & 255)) #define GET(a,n) \ (unsigned int)(((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2]) #define MAX_PATTERN_SIZE (1 << 24) #elif LINK_SIZE == 4 #define PUT(a,n,d) \ (a[n] = (PCRE2_UCHAR)((d) >> 24)), \ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 16)), \ (a[(n)+2] = (PCRE2_UCHAR)((d) >> 8)), \ (a[(n)+3] = (PCRE2_UCHAR)((d) & 255)) #define GET(a,n) \ (unsigned int)(((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) #define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ #else #error LINK_SIZE must be 2, 3, or 4 #endif /* ------------------- 16-bit support ------------------ */ #elif PCRE2_CODE_UNIT_WIDTH == 16 #if LINK_SIZE == 2 #undef LINK_SIZE #define LINK_SIZE 1 #define PUT(a,n,d) \ (a[n] = (PCRE2_UCHAR)(d)) #define GET(a,n) \ (a[n]) #define MAX_PATTERN_SIZE (1 << 16) #elif LINK_SIZE == 3 || LINK_SIZE == 4 #undef LINK_SIZE #define LINK_SIZE 2 #define PUT(a,n,d) \ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \ (a[(n)+1] = (PCRE2_UCHAR)((d) & 65535)) #define GET(a,n) \ (unsigned int)(((a)[n] << 16) | (a)[(n)+1]) #define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ #else #error LINK_SIZE must be 2, 3, or 4 #endif /* ------------------- 32-bit support ------------------ */ #elif PCRE2_CODE_UNIT_WIDTH == 32 #undef LINK_SIZE #define LINK_SIZE 1 #define PUT(a,n,d) \ (a[n] = (d)) #define GET(a,n) \ (a[n]) #define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ #else #error Unsupported compiling mode #endif /* --------------- Other mode-specific macros ----------------- */ /* PCRE uses some other (at least) 16-bit quantities that do not change when the size of offsets changes. There are used for repeat counts and for other things such as capturing parenthesis numbers in back references. Define the number of code units required to hold a 16-bit count/offset, and macros to load and store such a value. For reasons that I do not understand, the expression in the 8-bit GET2 macro is treated by gcc as a signed expression, even when a is declared as unsigned. It seems that any kind of arithmetic results in a signed value. Hence the cast. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define IMM2_SIZE 2 #define GET2(a,n) (unsigned int)(((a)[n] << 8) | (a)[(n)+1]) #define PUT2(a,n,d) a[n] = (d) >> 8, a[(n)+1] = (d) & 255 #else /* Code units are 16 or 32 bits */ #define IMM2_SIZE 1 #define GET2(a,n) a[n] #define PUT2(a,n,d) a[n] = d #endif /* Other macros that are different for 8-bit mode. The MAX_255 macro checks whether its argument, which is assumed to be one code unit, is less than 256. The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK name must fit in one code unit; currently it is set to 255 or 65535. The TABLE_GET macro is used to access elements of tables containing exactly 256 items. Its argument is a code unit. When code points can be greater than 255, a check is needed before accessing these tables. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MAX_255(c) TRUE #define MAX_MARK ((1u << 8) - 1) #define TABLE_GET(c, table, default) ((table)[c]) #ifdef SUPPORT_UNICODE #define SUPPORT_WIDE_CHARS #define CHMAX_255(c) ((c) <= 255u) #else #define CHMAX_255(c) TRUE #endif /* SUPPORT_UNICODE */ #else /* Code units are 16 or 32 bits */ #define CHMAX_255(c) ((c) <= 255u) #define MAX_255(c) ((c) <= 255u) #define MAX_MARK ((1u << 16) - 1) #define SUPPORT_WIDE_CHARS #define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default)) #endif /* ----------------- Character-handling macros ----------------- */ /* There is a proposed future special "UTF-21" mode, in which only the lowest 21 bits of a 32-bit character are interpreted as UTF, with the remaining 11 high-order bits available to the application for other uses. In preparation for the future implementation of this mode, there are macros that load a data item and, if in this special mode, mask it to 21 bits. These macros all have names starting with UCHAR21. In all other modes, including the normal 32-bit library, the macros all have the same simple definitions. When the new mode is implemented, it is expected that these definitions will be varied appropriately using #ifdef when compiling the library that supports the special mode. */ #define UCHAR21(eptr) (*(eptr)) #define UCHAR21TEST(eptr) (*(eptr)) #define UCHAR21INC(eptr) (*(eptr)++) #define UCHAR21INCTEST(eptr) (*(eptr)++) /* When UTF encoding is being used, a character is no longer just a single byte in 8-bit mode or a single short in 16-bit mode. The macros for character handling generate simple sequences when used in the basic mode, and more complicated ones for UTF characters. GETCHARLENTEST and other macros are not used when UTF is not supported. To make sure they can never even appear when UTF support is omitted, we don't even define them. */ #ifndef SUPPORT_UNICODE /* #define MAX_UTF_SINGLE_CU */ /* #define HAS_EXTRALEN(c) */ /* #define GET_EXTRALEN(c) */ /* #define NOT_FIRSTCU(c) */ #define GETCHAR(c, eptr) c = *eptr; #define GETCHARTEST(c, eptr) c = *eptr; #define GETCHARINC(c, eptr) c = *eptr++; #define GETCHARINCTEST(c, eptr) c = *eptr++; #define GETCHARLEN(c, eptr, len) c = *eptr; #define PUTCHAR(c, p) (*p = c, 1) /* #define GETCHARLENTEST(c, eptr, len) */ /* #define BACKCHAR(eptr) */ /* #define FORWARDCHAR(eptr) */ /* #define FORWARCCHARTEST(eptr,end) */ /* #define ACROSSCHAR(condition, eptr, action) */ #else /* SUPPORT_UNICODE */ /* ------------------- 8-bit support ------------------ */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */ /* The largest UTF code point that can be encoded as a single code unit. */ #define MAX_UTF_SINGLE_CU 127 /* Tests whether the code point needs extra characters to decode. */ #define HAS_EXTRALEN(c) HASUTF8EXTRALEN(c) /* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. Otherwise it has an undefined behaviour. */ #define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3fu]) /* Returns TRUE, if the given value is not the first code unit of a UTF sequence. */ #define NOT_FIRSTCU(c) (((c) & 0xc0u) == 0x80u) /* Get the next UTF-8 character, not advancing the pointer. This is called when we know we are in UTF-8 mode. */ #define GETCHAR(c, eptr) \ c = *eptr; \ if (c >= 0xc0u) GETUTF8(c, eptr); /* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the pointer. */ #define GETCHARTEST(c, eptr) \ c = *eptr; \ if (utf && c >= 0xc0u) GETUTF8(c, eptr); /* Get the next UTF-8 character, advancing the pointer. This is called when we know we are in UTF-8 mode. */ #define GETCHARINC(c, eptr) \ c = *eptr++; \ if (c >= 0xc0u) GETUTF8INC(c, eptr); /* Get the next character, testing for UTF-8 mode, and advancing the pointer. This is called when we don't know if we are in UTF-8 mode. */ #define GETCHARINCTEST(c, eptr) \ c = *eptr++; \ if (utf && c >= 0xc0u) GETUTF8INC(c, eptr); /* Get the next UTF-8 character, not advancing the pointer, incrementing length if there are extra bytes. This is called when we know we are in UTF-8 mode. */ #define GETCHARLEN(c, eptr, len) \ c = *eptr; \ if (c >= 0xc0u) GETUTF8LEN(c, eptr, len); /* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the pointer, incrementing length if there are extra bytes. This is called when we do not know if we are in UTF-8 mode. */ #define GETCHARLENTEST(c, eptr, len) \ c = *eptr; \ if (utf && c >= 0xc0u) GETUTF8LEN(c, eptr, len); /* If the pointer is not at the start of a character, move it back until it is. This is called only in UTF-8 mode - we don't put a test within the macro because almost all calls are already within a block of UTF-8 only code. */ #define BACKCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr-- /* Same as above, just in the other direction. */ #define FORWARDCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr++ #define FORWARDCHARTEST(eptr,end) while(eptr < end && (*eptr & 0xc0u) == 0x80u) eptr++ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ while((condition) && ((*eptr) & 0xc0u) == 0x80u) action /* Deposit a character into memory, returning the number of code units. */ #define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \ PRIV(ord2utf)(c,p) : (*p = c, 1)) /* ------------------- 16-bit support ------------------ */ #elif PCRE2_CODE_UNIT_WIDTH == 16 #define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */ /* The largest UTF code point that can be encoded as a single code unit. */ #define MAX_UTF_SINGLE_CU 65535 /* Tests whether the code point needs extra characters to decode. */ #define HAS_EXTRALEN(c) (((c) & 0xfc00u) == 0xd800u) /* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. Otherwise it has an undefined behaviour. */ #define GET_EXTRALEN(c) 1 /* Returns TRUE, if the given value is not the first code unit of a UTF sequence. */ #define NOT_FIRSTCU(c) (((c) & 0xfc00u) == 0xdc00u) /* Base macro to pick up the low surrogate of a UTF-16 character, not advancing the pointer. */ #define GETUTF16(c, eptr) \ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; } /* Get the next UTF-16 character, not advancing the pointer. This is called when we know we are in UTF-16 mode. */ #define GETCHAR(c, eptr) \ c = *eptr; \ if ((c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr); /* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the pointer. */ #define GETCHARTEST(c, eptr) \ c = *eptr; \ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr); /* Base macro to pick up the low surrogate of a UTF-16 character, advancing the pointer. */ #define GETUTF16INC(c, eptr) \ { c = (((c & 0x3ffu) << 10) | (*eptr++ & 0x3ffu)) + 0x10000u; } /* Get the next UTF-16 character, advancing the pointer. This is called when we know we are in UTF-16 mode. */ #define GETCHARINC(c, eptr) \ c = *eptr++; \ if ((c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr); /* Get the next character, testing for UTF-16 mode, and advancing the pointer. This is called when we don't know if we are in UTF-16 mode. */ #define GETCHARINCTEST(c, eptr) \ c = *eptr++; \ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr); /* Base macro to pick up the low surrogate of a UTF-16 character, not advancing the pointer, incrementing the length. */ #define GETUTF16LEN(c, eptr, len) \ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; len++; } /* Get the next UTF-16 character, not advancing the pointer, incrementing length if there is a low surrogate. This is called when we know we are in UTF-16 mode. */ #define GETCHARLEN(c, eptr, len) \ c = *eptr; \ if ((c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len); /* Get the next UTF-816character, testing for UTF-16 mode, not advancing the pointer, incrementing length if there is a low surrogate. This is called when we do not know if we are in UTF-16 mode. */ #define GETCHARLENTEST(c, eptr, len) \ c = *eptr; \ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len); /* If the pointer is not at the start of a character, move it back until it is. This is called only in UTF-16 mode - we don't put a test within the macro because almost all calls are already within a block of UTF-16 only code. */ #define BACKCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr-- /* Same as above, just in the other direction. */ #define FORWARDCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr++ #define FORWARDCHARTEST(eptr,end) if (eptr < end && (*eptr & 0xfc00u) == 0xdc00u) eptr++ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action /* Deposit a character into memory, returning the number of code units. */ #define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \ PRIV(ord2utf)(c,p) : (*p = c, 1)) /* ------------------- 32-bit support ------------------ */ #else /* These are trivial for the 32-bit library, since all UTF-32 characters fit into one PCRE2_UCHAR unit. */ #define MAX_UTF_SINGLE_CU (0x10ffffu) #define HAS_EXTRALEN(c) (0) #define GET_EXTRALEN(c) (0) #define NOT_FIRSTCU(c) (0) /* Get the next UTF-32 character, not advancing the pointer. This is called when we know we are in UTF-32 mode. */ #define GETCHAR(c, eptr) \ c = *(eptr); /* Get the next UTF-32 character, testing for UTF-32 mode, and not advancing the pointer. */ #define GETCHARTEST(c, eptr) \ c = *(eptr); /* Get the next UTF-32 character, advancing the pointer. This is called when we know we are in UTF-32 mode. */ #define GETCHARINC(c, eptr) \ c = *((eptr)++); /* Get the next character, testing for UTF-32 mode, and advancing the pointer. This is called when we don't know if we are in UTF-32 mode. */ #define GETCHARINCTEST(c, eptr) \ c = *((eptr)++); /* Get the next UTF-32 character, not advancing the pointer, not incrementing length (since all UTF-32 is of length 1). This is called when we know we are in UTF-32 mode. */ #define GETCHARLEN(c, eptr, len) \ GETCHAR(c, eptr) /* Get the next UTF-32character, testing for UTF-32 mode, not advancing the pointer, not incrementing the length (since all UTF-32 is of length 1). This is called when we do not know if we are in UTF-32 mode. */ #define GETCHARLENTEST(c, eptr, len) \ GETCHARTEST(c, eptr) /* If the pointer is not at the start of a character, move it back until it is. This is called only in UTF-32 mode - we don't put a test within the macro because almost all calls are already within a block of UTF-32 only code. These are all no-ops since all UTF-32 characters fit into one pcre_uchar. */ #define BACKCHAR(eptr) do { } while (0) /* Same as above, just in the other direction. */ #define FORWARDCHAR(eptr) do { } while (0) #define FORWARDCHARTEST(eptr,end) do { } while (0) /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) do { } while (0) /* Deposit a character into memory, returning the number of code units. */ #define PUTCHAR(c, p) (*p = c, 1) #endif /* UTF-32 character handling */ #endif /* SUPPORT_UNICODE */ /* Mode-dependent macros that have the same definition in all modes. */ #define CU2BYTES(x) ((x)*((PCRE2_CODE_UNIT_WIDTH/8))) #define BYTES2CU(x) ((x)/((PCRE2_CODE_UNIT_WIDTH/8))) #define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE #define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE /* ----------------------- HIDDEN STRUCTURES ----------------------------- */ /* NOTE: All these structures *must* start with a pcre2_memctl structure. The code that uses them is simpler because it assumes this. */ /* The real general context structure. At present it holds only data for custom memory control. */ typedef struct pcre2_real_general_context { pcre2_memctl memctl; } pcre2_real_general_context; /* The real compile context structure */ typedef struct pcre2_real_compile_context { pcre2_memctl memctl; int (*stack_guard)(uint32_t, void *); void *stack_guard_data; const uint8_t *tables; PCRE2_SIZE max_pattern_length; uint16_t bsr_convention; uint16_t newline_convention; uint32_t parens_nest_limit; uint32_t extra_options; } pcre2_real_compile_context; /* The real match context structure. */ typedef struct pcre2_real_match_context { pcre2_memctl memctl; #ifdef SUPPORT_JIT pcre2_jit_callback jit_callback; void *jit_callback_data; #endif int (*callout)(pcre2_callout_block *, void *); void *callout_data; int (*substitute_callout)(pcre2_substitute_callout_block *, void *); void *substitute_callout_data; PCRE2_SIZE offset_limit; uint32_t heap_limit; uint32_t match_limit; uint32_t depth_limit; } pcre2_real_match_context; /* The real convert context structure. */ typedef struct pcre2_real_convert_context { pcre2_memctl memctl; uint32_t glob_separator; uint32_t glob_escape; } pcre2_real_convert_context; /* The real compiled code structure. The type for the blocksize field is defined specially because it is required in pcre2_serialize_decode() when copying the size from possibly unaligned memory into a variable of the same type. Use a macro rather than a typedef to avoid compiler warnings when this file is included multiple times by pcre2test. LOOKBEHIND_MAX specifies the largest lookbehind that is supported. (OP_REVERSE in a pattern has a 16-bit argument in 8-bit and 16-bit modes, so we need no more than a 16-bit field here.) */ #undef CODE_BLOCKSIZE_TYPE #define CODE_BLOCKSIZE_TYPE size_t #undef LOOKBEHIND_MAX #define LOOKBEHIND_MAX UINT16_MAX typedef struct pcre2_real_code { pcre2_memctl memctl; /* Memory control fields */ const uint8_t *tables; /* The character tables */ void *executable_jit; /* Pointer to JIT code */ uint8_t start_bitmap[32]; /* Bitmap for starting code unit < 256 */ CODE_BLOCKSIZE_TYPE blocksize; /* Total (bytes) that was malloc-ed */ uint32_t magic_number; /* Paranoid and endianness check */ uint32_t compile_options; /* Options passed to pcre2_compile() */ uint32_t overall_options; /* Options after processing the pattern */ uint32_t extra_options; /* Taken from compile_context */ uint32_t flags; /* Various state flags */ uint32_t limit_heap; /* Limit set in the pattern */ uint32_t limit_match; /* Limit set in the pattern */ uint32_t limit_depth; /* Limit set in the pattern */ uint32_t first_codeunit; /* Starting code unit */ uint32_t last_codeunit; /* This codeunit must be seen */ uint16_t bsr_convention; /* What \R matches */ uint16_t newline_convention; /* What is a newline? */ uint16_t max_lookbehind; /* Longest lookbehind (characters) */ uint16_t minlength; /* Minimum length of match */ uint16_t top_bracket; /* Highest numbered group */ uint16_t top_backref; /* Highest numbered back reference */ uint16_t name_entry_size; /* Size (code units) of table entries */ uint16_t name_count; /* Number of name entries in the table */ } pcre2_real_code; /* The real match data structure. Define ovector as large as it can ever actually be so that array bound checkers don't grumble. Memory for this structure is obtained by calling pcre2_match_data_create(), which sets the size as the offset of ovector plus a pair of elements for each capturable string, so the size varies from call to call. As the maximum number of capturing subpatterns is 65535 we must allow for 65536 strings to include the overall match. (See also the heapframe structure below.) */ typedef struct pcre2_real_match_data { pcre2_memctl memctl; const pcre2_real_code *code; /* The pattern used for the match */ PCRE2_SPTR subject; /* The subject that was matched */ PCRE2_SPTR mark; /* Pointer to last mark */ PCRE2_SIZE leftchar; /* Offset to leftmost code unit */ PCRE2_SIZE rightchar; /* Offset to rightmost code unit */ PCRE2_SIZE startchar; /* Offset to starting code unit */ uint8_t matchedby; /* Type of match (normal, JIT, DFA) */ uint8_t flags; /* Various flags */ uint16_t oveccount; /* Number of pairs */ int rc; /* The return code from the match */ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ } pcre2_real_match_data; /* ----------------------- PRIVATE STRUCTURES ----------------------------- */ /* These structures are not needed for pcre2test. */ #ifndef PCRE2_PCRE2TEST /* Structures for checking for mutual recursion when scanning compiled or parsed code. */ typedef struct recurse_check { struct recurse_check *prev; PCRE2_SPTR group; } recurse_check; typedef struct parsed_recurse_check { struct parsed_recurse_check *prev; uint32_t *groupptr; } parsed_recurse_check; /* Structure for building a cache when filling in recursion offsets. */ typedef struct recurse_cache { PCRE2_SPTR group; int groupnumber; } recurse_cache; /* Structure for maintaining a chain of pointers to the currently incomplete branches, for testing for left recursion while compiling. */ typedef struct branch_chain { struct branch_chain *outer; PCRE2_UCHAR *current_branch; } branch_chain; /* Structure for building a list of named groups during the first pass of compiling. */ typedef struct named_group { PCRE2_SPTR name; /* Points to the name in the pattern */ uint32_t number; /* Group number */ uint16_t length; /* Length of the name */ uint16_t isdup; /* TRUE if a duplicate */ } named_group; /* Structure for passing "static" information around between the functions doing the compiling, so that they are thread-safe. */ typedef struct compile_block { pcre2_real_compile_context *cx; /* Points to the compile context */ const uint8_t *lcc; /* Points to lower casing table */ const uint8_t *fcc; /* Points to case-flipping table */ const uint8_t *cbits; /* Points to character type table */ const uint8_t *ctypes; /* Points to table of type maps */ PCRE2_SPTR start_workspace; /* The start of working space */ PCRE2_SPTR start_code; /* The start of the compiled code */ PCRE2_SPTR start_pattern; /* The start of the pattern */ PCRE2_SPTR end_pattern; /* The end of the pattern */ PCRE2_UCHAR *name_table; /* The name/number table */ PCRE2_SIZE workspace_size; /* Size of workspace */ PCRE2_SIZE small_ref_offset[10]; /* Offsets for \1 to \9 */ PCRE2_SIZE erroroffset; /* Offset of error in pattern */ uint16_t names_found; /* Number of entries so far */ uint16_t name_entry_size; /* Size of each entry */ uint16_t parens_depth; /* Depth of nested parentheses */ uint16_t assert_depth; /* Depth of nested assertions */ open_capitem *open_caps; /* Chain of open capture items */ named_group *named_groups; /* Points to vector in pre-compile */ uint32_t named_group_list_size; /* Number of entries in the list */ uint32_t external_options; /* External (initial) options */ uint32_t external_flags; /* External flag bits to be set */ uint32_t bracount; /* Count of capturing parentheses */ uint32_t lastcapture; /* Last capture encountered */ uint32_t *parsed_pattern; /* Parsed pattern buffer */ uint32_t *parsed_pattern_end; /* Parsed pattern should not get here */ uint32_t *groupinfo; /* Group info vector */ uint32_t top_backref; /* Maximum back reference */ uint32_t backref_map; /* Bitmap of low back refs */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ uint32_t class_range_start; /* Overall class range start */ uint32_t class_range_end; /* Overall class range end */ PCRE2_UCHAR nl[4]; /* Newline string when fixed length */ int max_lookbehind; /* Maximum lookbehind (characters) */ int req_varyopt; /* "After variable item" flag for reqbyte */ BOOL had_accept; /* (*ACCEPT) encountered */ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */ BOOL had_recurse; /* Had a recursion or subroutine call */ BOOL dupnames; /* Duplicate names exist */ } compile_block; /* Structure for keeping the properties of the in-memory stack used by the JIT matcher. */ typedef struct pcre2_real_jit_stack { pcre2_memctl memctl; void* stack; } pcre2_real_jit_stack; /* Structure for items in a linked list that represents an explicit recursive call within the pattern when running pcre_dfa_match(). */ typedef struct dfa_recursion_info { struct dfa_recursion_info *prevrec; PCRE2_SPTR subject_position; uint32_t group_num; } dfa_recursion_info; /* Structure for "stack" frames that are used for remembering backtracking positions during matching. As these are used in a vector, with the ovector item being extended, the size of the structure must be a multiple of PCRE2_SIZE. The only way to check this at compile time is to force an error by generating an array with a negative size. By putting this in a typedef (which is never used), we don't generate any code when all is well. */ typedef struct heapframe { /* The first set of fields are variables that have to be preserved over calls to RRMATCH(), but which do not need to be copied to new frames. */ PCRE2_SPTR ecode; /* The current position in the pattern */ PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */ PCRE2_SIZE length; /* Used for character, string, or code lengths */ PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */ PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */ uint32_t rdepth; /* "Recursion" depth */ uint32_t group_frame_type; /* Type information for group frames */ uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */ uint8_t return_id; /* Where to go on in internal "return" */ uint8_t op; /* Processing opcode */ /* At this point, the structure is 16-bit aligned. On most architectures the alignment requirement for a pointer will ensure that the eptr field below is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer that is 16-bit aligned. We must therefore ensure that what comes between here and eptr is an odd multiple of 16 bits so as to get back into 32-bit alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs fudges in the other cases. In the 32-bit case the padding comes first so that the occu field itself is 32-bit aligned. Without the padding, this structure is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */ #if PCRE2_CODE_UNIT_WIDTH == 8 PCRE2_UCHAR occu[6]; /* Used for other case code units */ #elif PCRE2_CODE_UNIT_WIDTH == 16 PCRE2_UCHAR occu[2]; /* Used for other case code units */ uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ #else uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ PCRE2_UCHAR occu[1]; /* Used for other case code units */ #endif /* The rest have to be copied from the previous frame whenever a new frame becomes current. The final field is specified as a large vector so that runtime array bound checks don't catch references to it. However, for any specific call to pcre2_match() the memory allocated for each frame structure allows for exactly the right size ovector for the number of capturing parentheses. (See also the comment for pcre2_real_match_data above.) */ PCRE2_SPTR eptr; /* MUST BE FIRST */ PCRE2_SPTR start_match; /* Can be adjusted by \K */ PCRE2_SPTR mark; /* Most recent mark on the success path */ uint32_t current_recurse; /* Current (deepest) recursion number */ uint32_t capture_last; /* Most recent capture */ PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */ PCRE2_SIZE offset_top; /* Offset after highest capture */ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ } heapframe; /* This typedef is a check that the size of the heapframe structure is a multiple of PCRE2_SIZE. See various comments above. */ typedef char check_heapframe_size[ ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)]; /* Structure for passing "static" information around between the functions doing traditional NFA matching (pcre2_match() and friends). */ typedef struct match_block { pcre2_memctl memctl; /* For general use */ PCRE2_SIZE frame_vector_size; /* Size of a backtracking frame */ heapframe *match_frames; /* Points to vector of frames */ heapframe *match_frames_top; /* Points after the end of the vector */ heapframe *stack_frames; /* The original vector on the stack */ PCRE2_SIZE heap_limit; /* As it says */ uint32_t match_limit; /* As it says */ uint32_t match_limit_depth; /* As it says */ uint32_t match_call_count; /* Number of times a new frame is created */ BOOL hitend; /* Hit the end of the subject at some point */ BOOL hasthen; /* Pattern contains (*THEN) */ BOOL allowemptypartial; /* Allow empty hard partial */ const uint8_t *lcc; /* Points to lower casing table */ const uint8_t *fcc; /* Points to case-flipping table */ const uint8_t *ctypes; /* Points to table of type maps */ PCRE2_SIZE start_offset; /* The start offset value */ PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */ uint16_t partial; /* PARTIAL options */ uint16_t bsr_convention; /* \R interpretation */ uint16_t name_count; /* Number of names in name table */ uint16_t name_entry_size; /* Size of entry in names table */ PCRE2_SPTR name_table; /* Table of group names */ PCRE2_SPTR start_code; /* For use when recursing */ PCRE2_SPTR start_subject; /* Start of the subject string */ PCRE2_SPTR check_subject; /* Where UTF-checked from */ PCRE2_SPTR end_subject; /* End of the subject string */ PCRE2_SPTR end_match_ptr; /* Subject position at end match */ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */ PCRE2_SPTR last_used_ptr; /* Latest consulted character */ PCRE2_SPTR mark; /* Mark pointer to pass back on success */ PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */ PCRE2_SPTR verb_ecode_ptr; /* For passing back info */ PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */ uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ uint32_t skip_arg_count; /* For counting SKIP_ARGs */ uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ } match_block; /* A similar structure is used for the same purpose by the DFA matching functions. */ typedef struct dfa_match_block { pcre2_memctl memctl; /* For general use */ PCRE2_SPTR start_code; /* Start of the compiled pattern */ PCRE2_SPTR start_subject ; /* Start of the subject string */ PCRE2_SPTR end_subject; /* End of subject string */ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */ PCRE2_SPTR last_used_ptr; /* Latest consulted character */ const uint8_t *tables; /* Character tables */ PCRE2_SIZE start_offset; /* The start offset value */ PCRE2_SIZE heap_limit; /* As it says */ PCRE2_SIZE heap_used; /* As it says */ uint32_t match_limit; /* As it says */ uint32_t match_limit_depth; /* As it says */ uint32_t match_call_count; /* Number of calls of internal function */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ BOOL allowemptypartial; /* Allow empty hard partial */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ uint16_t bsr_convention; /* \R interpretation */ pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ dfa_recursion_info *recursive; /* Linked list of recursion data */ } dfa_match_block; #endif /* PCRE2_PCRE2TEST */ /* End of pcre2_intmodedep.h */ vfu-4.22/vstring/pcre2/pcre2_match_data.c0000644000175000017500000001251014145574056016660 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2019 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Create a match data block given ovector size * *************************************************/ /* A minimum of 1 is imposed on the number of ovector pairs. */ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) { pcre2_match_data *yield; if (oveccount < 1) oveccount = 1; yield = PRIV(memctl_malloc)( offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE), (pcre2_memctl *)gcontext); if (yield == NULL) return NULL; yield->oveccount = oveccount; yield->flags = 0; return yield; } /************************************************* * Create a match data block using pattern data * *************************************************/ /* If no context is supplied, use the memory allocator from the code. */ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION pcre2_match_data_create_from_pattern(const pcre2_code *code, pcre2_general_context *gcontext) { if (gcontext == NULL) gcontext = (pcre2_general_context *)code; return pcre2_match_data_create(((pcre2_real_code *)code)->top_bracket + 1, gcontext); } /************************************************* * Free a match data block * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_match_data_free(pcre2_match_data *match_data) { if (match_data != NULL) { if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0) match_data->memctl.free((void *)match_data->subject, match_data->memctl.memory_data); match_data->memctl.free(match_data, match_data->memctl.memory_data); } } /************************************************* * Get last mark in match * *************************************************/ PCRE2_EXP_DEFN PCRE2_SPTR PCRE2_CALL_CONVENTION pcre2_get_mark(pcre2_match_data *match_data) { return match_data->mark; } /************************************************* * Get pointer to ovector * *************************************************/ PCRE2_EXP_DEFN PCRE2_SIZE * PCRE2_CALL_CONVENTION pcre2_get_ovector_pointer(pcre2_match_data *match_data) { return match_data->ovector; } /************************************************* * Get number of ovector slots * *************************************************/ PCRE2_EXP_DEFN uint32_t PCRE2_CALL_CONVENTION pcre2_get_ovector_count(pcre2_match_data *match_data) { return match_data->oveccount; } /************************************************* * Get starting code unit in match * *************************************************/ PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION pcre2_get_startchar(pcre2_match_data *match_data) { return match_data->startchar; } /************************************************* * Get size of match data block * *************************************************/ PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION pcre2_get_match_data_size(pcre2_match_data *match_data) { return offsetof(pcre2_match_data, ovector) + 2 * (match_data->oveccount) * sizeof(PCRE2_SIZE); } /* End of pcre2_match_data.c */ vfu-4.22/vstring/pcre2/pcre2_dfa_match.c0000644000175000017500000042063214145574056016511 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains the external function pcre2_dfa_match(), which is an alternative matching function that uses a sort of DFA algorithm (not a true FSM). This is NOT Perl-compatible, but it has advantages in certain applications. */ /* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved the performance of his patterns greatly. I could not use it as it stood, as it was not thread safe, and made assumptions about pattern sizes. Also, it caused test 7 to loop, and test 9 to crash with a segfault. The issue is the check for duplicate states, which is done by a simple linear search up the state list. (Grep for "duplicate" below to find the code.) For many patterns, there will never be many states active at one time, so a simple linear search is fine. In patterns that have many active states, it might be a bottleneck. The suggested code used an indexing scheme to remember which states had previously been used for each character, and avoided the linear search when it knew there was no chance of a duplicate. This was implemented when adding states to the state lists. I wrote some thread-safe, not-limited code to try something similar at the time of checking for duplicates (instead of when adding states), using index vectors on the stack. It did give a 13% improvement with one specially constructed pattern for certain subject strings, but on other strings and on many of the simpler patterns in the test suite it did worse. The major problem, I think, was the extra time to initialize the index. This had to be done for each call of internal_dfa_match(). (The supplied patch used a static vector, initialized only once - I suspect this was the cause of the problems with the tests.) Overall, I concluded that the gains in some cases did not outweigh the losses in others, so I abandoned this code. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define NLBLOCK mb /* Block containing newline information */ #define PSSTART start_subject /* Field containing processed string start */ #define PSEND end_subject /* Field containing processed string end */ #include "pcre2_internal.h" #define PUBLIC_DFA_MATCH_OPTIONS \ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART| \ PCRE2_COPY_MATCHED_SUBJECT) /************************************************* * Code parameters and static tables * *************************************************/ /* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes into others, under special conditions. A gap of 20 between the blocks should be enough. The resulting opcodes don't have to be less than 256 because they are never stored, so we push them well clear of the normal opcodes. */ #define OP_PROP_EXTRA 300 #define OP_EXTUNI_EXTRA 320 #define OP_ANYNL_EXTRA 340 #define OP_HSPACE_EXTRA 360 #define OP_VSPACE_EXTRA 380 /* This table identifies those opcodes that are followed immediately by a character that is to be tested in some way. This makes it possible to centralize the loading of these characters. In the case of Type * etc, the "character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a small value. Non-zero values in the table are the offsets from the opcode where the character is to be found. ***NOTE*** If the start of this table is modified, the three tables that follow must also be modified. */ static const uint8_t coptable[] = { 0, /* End */ 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */ 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */ 0, 0, 0, /* Any, AllAny, Anybyte */ 0, 0, /* \P, \p */ 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */ 0, /* \X */ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */ 1, /* Char */ 1, /* Chari */ 1, /* not */ 1, /* noti */ /* Positive single-char repeats */ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto, minupto */ 1+IMM2_SIZE, /* exact */ 1, 1, 1, 1+IMM2_SIZE, /* *+, ++, ?+, upto+ */ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto I, minupto I */ 1+IMM2_SIZE, /* exact I */ 1, 1, 1, 1+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ /* Negative single-char repeats - only for chars < 256 */ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto, minupto */ 1+IMM2_SIZE, /* NOT exact */ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+, ++, ?+, upto+ */ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto I, minupto I */ 1+IMM2_SIZE, /* NOT exact I */ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+I, ++I, ?+I, upto+I */ /* Positive type repeats */ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ 1+IMM2_SIZE, 1+IMM2_SIZE, /* Type upto, minupto */ 1+IMM2_SIZE, /* Type exact */ 1, 1, 1, 1+IMM2_SIZE, /* Type *+, ++, ?+, upto+ */ /* Character class & ref repeats */ 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */ 0, 0, /* CRRANGE, CRMINRANGE */ 0, 0, 0, 0, /* Possessive *+, ++, ?+, CRPOSRANGE */ 0, /* CLASS */ 0, /* NCLASS */ 0, /* XCLASS - variable length */ 0, /* REF */ 0, /* REFI */ 0, /* DNREF */ 0, /* DNREFI */ 0, /* RECURSE */ 0, /* CALLOUT */ 0, /* CALLOUT_STR */ 0, /* Alt */ 0, /* Ket */ 0, /* KetRmax */ 0, /* KetRmin */ 0, /* KetRpos */ 0, /* Reverse */ 0, /* Assert */ 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ 0, /* NA assert */ 0, /* NA assert behind */ 0, /* ONCE */ 0, /* SCRIPT_RUN */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ 0, 0, /* RREF, DNRREF */ 0, 0, /* FALSE, TRUE */ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ 0, 0, /* COMMIT, COMMIT_ARG */ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; /* This table identifies those opcodes that inspect a character. It is used to remember the fact that a character could have been inspected when the end of the subject is reached. ***NOTE*** If the start of this table is modified, the two tables that follow must also be modified. */ static const uint8_t poptable[] = { 0, /* End */ 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ 1, 1, 1, /* Any, AllAny, Anybyte */ 1, 1, /* \P, \p */ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ 1, /* \X */ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */ 1, /* Char */ 1, /* Chari */ 1, /* not */ 1, /* noti */ /* Positive single-char repeats */ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ 1, 1, 1, /* upto, minupto, exact */ 1, 1, 1, 1, /* *+, ++, ?+, upto+ */ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ 1, 1, 1, /* upto I, minupto I, exact I */ 1, 1, 1, 1, /* *+I, ++I, ?+I, upto+I */ /* Negative single-char repeats - only for chars < 256 */ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ 1, 1, 1, /* NOT upto, minupto, exact */ 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ 1, 1, 1, /* NOT upto I, minupto I, exact I */ 1, 1, 1, 1, /* NOT *+I, ++I, ?+I, upto+I */ /* Positive type repeats */ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ 1, 1, 1, /* Type upto, minupto, exact */ 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */ /* Character class & ref repeats */ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ 1, 1, /* CRRANGE, CRMINRANGE */ 1, 1, 1, 1, /* Possessive *+, ++, ?+, CRPOSRANGE */ 1, /* CLASS */ 1, /* NCLASS */ 1, /* XCLASS - variable length */ 0, /* REF */ 0, /* REFI */ 0, /* DNREF */ 0, /* DNREFI */ 0, /* RECURSE */ 0, /* CALLOUT */ 0, /* CALLOUT_STR */ 0, /* Alt */ 0, /* Ket */ 0, /* KetRmax */ 0, /* KetRmin */ 0, /* KetRpos */ 0, /* Reverse */ 0, /* Assert */ 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ 0, /* NA assert */ 0, /* NA assert behind */ 0, /* ONCE */ 0, /* SCRIPT_RUN */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ 0, 0, /* RREF, DNRREF */ 0, 0, /* FALSE, TRUE */ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ 0, 0, /* COMMIT, COMMIT_ARG */ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; /* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W, and \w */ static const uint8_t toptable1[] = { 0, 0, 0, 0, 0, 0, ctype_digit, ctype_digit, ctype_space, ctype_space, ctype_word, ctype_word, 0, 0 /* OP_ANY, OP_ALLANY */ }; static const uint8_t toptable2[] = { 0, 0, 0, 0, 0, 0, ctype_digit, 0, ctype_space, 0, ctype_word, 0, 1, 1 /* OP_ANY, OP_ALLANY */ }; /* Structure for holding data about a particular state, which is in effect the current data for an active path through the match tree. It must consist entirely of ints because the working vector we are passed, and which we put these structures in, is a vector of ints. */ typedef struct stateblock { int offset; /* Offset to opcode (-ve has meaning) */ int count; /* Count for repeats */ int data; /* Some use extra data */ } stateblock; #define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int)) /* Before version 10.32 the recursive calls of internal_dfa_match() were passed local working space and output vectors that were created on the stack. This has caused issues for some patterns, especially in small-stack environments such as Windows. A new scheme is now in use which sets up a vector on the stack, but if this is too small, heap memory is used, up to the heap_limit. The main parameters are all numbers of ints because the workspace is a vector of ints. The size of the starting stack vector, DFA_START_RWS_SIZE, is in bytes, and is defined in pcre2_internal.h so as to be available to pcre2test when it is finding the minimum heap requirement for a match. */ #define OVEC_UNIT (sizeof(PCRE2_SIZE)/sizeof(int)) #define RWS_BASE_SIZE (DFA_START_RWS_SIZE/sizeof(int)) /* Stack vector */ #define RWS_RSIZE 1000 /* Work size for recursion */ #define RWS_OVEC_RSIZE (1000*OVEC_UNIT) /* Ovector for recursion */ #define RWS_OVEC_OSIZE (2*OVEC_UNIT) /* Ovector in other cases */ /* This structure is at the start of each workspace block. */ typedef struct RWS_anchor { struct RWS_anchor *next; uint32_t size; /* Number of ints */ uint32_t free; /* Number of ints */ } RWS_anchor; #define RWS_ANCHOR_SIZE (sizeof(RWS_anchor)/sizeof(int)) /************************************************* * Process a callout * *************************************************/ /* This function is called to perform a callout. Arguments: code current code pointer offsets points to current capture offsets current_subject start of current subject match ptr current position in subject mb the match block extracode extra code offset when called from condition lengthptr where to return the callout length Returns: the return from the callout */ static int do_callout(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject, PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode, PCRE2_SIZE *lengthptr) { pcre2_callout_block *cb = mb->cb; *lengthptr = (code[extracode] == OP_CALLOUT)? (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] : (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode); if (mb->callout == NULL) return 0; /* No callout provided */ /* Fixed fields in the callout block are set once and for all at the start of matching. */ cb->offset_vector = offsets; cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject); cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject); cb->pattern_position = GET(code, 1 + extracode); cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode); if (code[extracode] == OP_CALLOUT) { cb->callout_number = code[1 + 2*LINK_SIZE + extracode]; cb->callout_string_offset = 0; cb->callout_string = NULL; cb->callout_string_length = 0; } else { cb->callout_number = 0; cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode); cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1; cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2; } return (mb->callout)(cb, mb->callout_data); } /************************************************* * Expand local workspace memory * *************************************************/ /* This function is called when internal_dfa_match() is about to be called recursively and there is insufficient working space left in the current workspace block. If there's an existing next block, use it; otherwise get a new block unless the heap limit is reached. Arguments: rwsptr pointer to block pointer (updated) ovecsize space needed for an ovector mb the match block Returns: 0 rwsptr has been updated !0 an error code */ static int more_workspace(RWS_anchor **rwsptr, unsigned int ovecsize, dfa_match_block *mb) { RWS_anchor *rws = *rwsptr; RWS_anchor *new; if (rws->next != NULL) { new = rws->next; } /* Sizes in the RWS_anchor blocks are in units of sizeof(int), but mb->heap_limit and mb->heap_used are in kibibytes. Play carefully, to avoid overflow. */ else { uint32_t newsize = (rws->size >= UINT32_MAX/2)? UINT32_MAX/2 : rws->size * 2; uint32_t newsizeK = newsize/(1024/sizeof(int)); if (newsizeK + mb->heap_used > mb->heap_limit) newsizeK = (uint32_t)(mb->heap_limit - mb->heap_used); newsize = newsizeK*(1024/sizeof(int)); if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE) return PCRE2_ERROR_HEAPLIMIT; new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data); if (new == NULL) return PCRE2_ERROR_NOMEMORY; mb->heap_used += newsizeK; new->next = NULL; new->size = newsize; rws->next = new; } new->free = new->size - RWS_ANCHOR_SIZE; *rwsptr = new; return 0; } /************************************************* * Match a Regular Expression - DFA engine * *************************************************/ /* This internal function applies a compiled pattern to a subject string, starting at a given point, using a DFA engine. This function is called from the external one, possibly multiple times if the pattern is not anchored. The function calls itself recursively for some kinds of subpattern. Arguments: mb the match_data block with fixed information this_start_code the opening bracket of this subexpression's code current_subject where we currently are in the subject string start_offset start offset in the subject string offsets vector to contain the matching string offsets offsetcount size of same workspace vector of workspace wscount size of same rlevel function call recursion level Returns: > 0 => number of match offset pairs placed in offsets = 0 => offsets overflowed; longest matches are present -1 => failed to match < -1 => some kind of unexpected problem The following macros are used for adding states to the two state vectors (one for the current character, one for the following character). */ #define ADD_ACTIVE(x,y) \ if (active_count++ < wscount) \ { \ next_active_state->offset = (x); \ next_active_state->count = (y); \ next_active_state++; \ } \ else return PCRE2_ERROR_DFA_WSSIZE #define ADD_ACTIVE_DATA(x,y,z) \ if (active_count++ < wscount) \ { \ next_active_state->offset = (x); \ next_active_state->count = (y); \ next_active_state->data = (z); \ next_active_state++; \ } \ else return PCRE2_ERROR_DFA_WSSIZE #define ADD_NEW(x,y) \ if (new_count++ < wscount) \ { \ next_new_state->offset = (x); \ next_new_state->count = (y); \ next_new_state++; \ } \ else return PCRE2_ERROR_DFA_WSSIZE #define ADD_NEW_DATA(x,y,z) \ if (new_count++ < wscount) \ { \ next_new_state->offset = (x); \ next_new_state->count = (y); \ next_new_state->data = (z); \ next_new_state++; \ } \ else return PCRE2_ERROR_DFA_WSSIZE /* And now, here is the code */ static int internal_dfa_match( dfa_match_block *mb, PCRE2_SPTR this_start_code, PCRE2_SPTR current_subject, PCRE2_SIZE start_offset, PCRE2_SIZE *offsets, uint32_t offsetcount, int *workspace, int wscount, uint32_t rlevel, int *RWS) { stateblock *active_states, *new_states, *temp_states; stateblock *next_active_state, *next_new_state; const uint8_t *ctypes, *lcc, *fcc; PCRE2_SPTR ptr; PCRE2_SPTR end_code; dfa_recursion_info new_recursive; int active_count, new_count, match_count; /* Some fields in the mb block are frequently referenced, so we load them into independent variables in the hope that this will perform better. */ PCRE2_SPTR start_subject = mb->start_subject; PCRE2_SPTR end_subject = mb->end_subject; PCRE2_SPTR start_code = mb->start_code; #ifdef SUPPORT_UNICODE BOOL utf = (mb->poptions & PCRE2_UTF) != 0; BOOL utf_or_ucp = utf || (mb->poptions & PCRE2_UCP) != 0; #else BOOL utf = FALSE; #endif BOOL reset_could_continue = FALSE; if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; offsetcount &= (uint32_t)(-2); /* Round down */ wscount -= 2; wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) / (2 * INTS_PER_STATEBLOCK); ctypes = mb->tables + ctypes_offset; lcc = mb->tables + lcc_offset; fcc = mb->tables + fcc_offset; match_count = PCRE2_ERROR_NOMATCH; /* A negative number */ active_states = (stateblock *)(workspace + 2); next_new_state = new_states = active_states + wscount; new_count = 0; /* The first thing in any (sub) pattern is a bracket of some sort. Push all the alternative states onto the list, and find out where the end is. This makes is possible to use this function recursively, when we want to stop at a matching internal ket rather than at the end. If we are dealing with a backward assertion we have to find out the maximum amount to move back, and set up each alternative appropriately. */ if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT) { size_t max_back = 0; size_t gone_back; end_code = this_start_code; do { size_t back = (size_t)GET(end_code, 2+LINK_SIZE); if (back > max_back) max_back = back; end_code += GET(end_code, 1); } while (*end_code == OP_ALT); /* If we can't go back the amount required for the longest lookbehind pattern, go back as far as we can; some alternatives may still be viable. */ #ifdef SUPPORT_UNICODE /* In character mode we have to step back character by character */ if (utf) { for (gone_back = 0; gone_back < max_back; gone_back++) { if (current_subject <= start_subject) break; current_subject--; ACROSSCHAR(current_subject > start_subject, current_subject, current_subject--); } } else #endif /* In byte-mode we can do this quickly. */ { size_t current_offset = (size_t)(current_subject - start_subject); gone_back = (current_offset < max_back)? current_offset : max_back; current_subject -= gone_back; } /* Save the earliest consulted character */ if (current_subject < mb->start_used_ptr) mb->start_used_ptr = current_subject; /* Now we can process the individual branches. There will be an OP_REVERSE at the start of each branch, except when the length of the branch is zero. */ end_code = this_start_code; do { uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0; size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE); if (back <= gone_back) { int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen); ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back)); } end_code += GET(end_code, 1); } while (*end_code == OP_ALT); } /* This is the code for a "normal" subpattern (not a backward assertion). The start of a whole pattern is always one of these. If we are at the top level, we may be asked to restart matching from the same point that we reached for a previous partial match. We still have to scan through the top-level branches to find the end state. */ else { end_code = this_start_code; /* Restarting */ if (rlevel == 1 && (mb->moptions & PCRE2_DFA_RESTART) != 0) { do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT); new_count = workspace[1]; if (!workspace[0]) memcpy(new_states, active_states, (size_t)new_count * sizeof(stateblock)); } /* Not restarting */ else { int length = 1 + LINK_SIZE + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) ? IMM2_SIZE:0); do { ADD_NEW((int)(end_code - start_code + length), 0); end_code += GET(end_code, 1); length = 1 + LINK_SIZE; } while (*end_code == OP_ALT); } } workspace[0] = 0; /* Bit indicating which vector is current */ /* Loop for scanning the subject */ ptr = current_subject; for (;;) { int i, j; int clen, dlen; uint32_t c, d; int forced_fail = 0; BOOL partial_newline = FALSE; BOOL could_continue = reset_could_continue; reset_could_continue = FALSE; if (ptr > mb->last_used_ptr) mb->last_used_ptr = ptr; /* Make the new state list into the active state list and empty the new state list. */ temp_states = active_states; active_states = new_states; new_states = temp_states; active_count = new_count; new_count = 0; workspace[0] ^= 1; /* Remember for the restarting feature */ workspace[1] = active_count; /* Set the pointers for adding new states */ next_active_state = active_states + active_count; next_new_state = new_states; /* Load the current character from the subject outside the loop, as many different states may want to look at it, and we assume that at least one will. */ if (ptr < end_subject) { clen = 1; /* Number of data items in the character */ #ifdef SUPPORT_UNICODE GETCHARLENTEST(c, ptr, clen); #else c = *ptr; #endif /* SUPPORT_UNICODE */ } else { clen = 0; /* This indicates the end of the subject */ c = NOTACHAR; /* This value should never actually be used */ } /* Scan up the active states and act on each one. The result of an action may be to add more states to the currently active list (e.g. on hitting a parenthesis) or it may be to put states on the new list, for considering when we move the character pointer on. */ for (i = 0; i < active_count; i++) { stateblock *current_state = active_states + i; BOOL caseless = FALSE; PCRE2_SPTR code; uint32_t codevalue; int state_offset = current_state->offset; int rrc; int count; /* A negative offset is a special case meaning "hold off going to this (negated) state until the number of characters in the data field have been skipped". If the could_continue flag was passed over from a previous state, arrange for it to passed on. */ if (state_offset < 0) { if (current_state->data > 0) { ADD_NEW_DATA(state_offset, current_state->count, current_state->data - 1); if (could_continue) reset_could_continue = TRUE; continue; } else { current_state->offset = state_offset = -state_offset; } } /* Check for a duplicate state with the same count, and skip if found. See the note at the head of this module about the possibility of improving performance here. */ for (j = 0; j < i; j++) { if (active_states[j].offset == state_offset && active_states[j].count == current_state->count) goto NEXT_ACTIVE_STATE; } /* The state offset is the offset to the opcode */ code = start_code + state_offset; codevalue = *code; /* If this opcode inspects a character, but we are at the end of the subject, remember the fact for use when testing for a partial match. */ if (clen == 0 && poptable[codevalue] != 0) could_continue = TRUE; /* If this opcode is followed by an inline character, load it. It is tempting to test for the presence of a subject character here, but that is wrong, because sometimes zero repetitions of the subject are permitted. We also use this mechanism for opcodes such as OP_TYPEPLUS that take an argument that is not a data character - but is always one byte long because the values are small. We have to take special action to deal with \P, \p, \H, \h, \V, \v and \X in this case. To keep the other cases fast, convert these ones to new opcodes. */ if (coptable[codevalue] > 0) { dlen = 1; #ifdef SUPPORT_UNICODE if (utf) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else #endif /* SUPPORT_UNICODE */ d = code[coptable[codevalue]]; if (codevalue >= OP_TYPESTAR) { switch(d) { case OP_ANYBYTE: return PCRE2_ERROR_DFA_UITEM; case OP_NOTPROP: case OP_PROP: codevalue += OP_PROP_EXTRA; break; case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break; case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break; case OP_NOT_HSPACE: case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break; case OP_NOT_VSPACE: case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break; default: break; } } } else { dlen = 0; /* Not strictly necessary, but compilers moan */ d = NOTACHAR; /* if these variables are not set. */ } /* Now process the individual opcodes */ switch (codevalue) { /* ========================================================================== */ /* These cases are never obeyed. This is a fudge that causes a compile- time error if the vectors coptable or poptable, which are indexed by opcode, are not the correct length. It seems to be the only way to do such a check at compile time, as the sizeof() operator does not work in the C preprocessor. */ case OP_TABLE_LENGTH: case OP_TABLE_LENGTH + ((sizeof(coptable) == OP_TABLE_LENGTH) && (sizeof(poptable) == OP_TABLE_LENGTH)): return 0; /* ========================================================================== */ /* Reached a closing bracket. If not at the end of the pattern, carry on with the next opcode. For repeating opcodes, also add the repeat state. Note that KETRPOS will always be encountered at the end of the subpattern, because the possessive subpattern repeats are always handled using recursive calls. Thus, it never adds any new states. At the end of the (sub)pattern, unless we have an empty string and PCRE2_NOTEMPTY is set, or PCRE2_NOTEMPTY_ATSTART is set and we are at the start of the subject, save the match data, shifting up all previous matches so we always have the longest first. */ case OP_KET: case OP_KETRMIN: case OP_KETRMAX: case OP_KETRPOS: if (code != end_code) { ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0); if (codevalue != OP_KET) { ADD_ACTIVE(state_offset - (int)GET(code, 1), 0); } } else { if (ptr > current_subject || ((mb->moptions & PCRE2_NOTEMPTY) == 0 && ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) == 0 || current_subject > start_subject + mb->start_offset))) { if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0; else if (match_count > 0 && ++match_count * 2 > (int)offsetcount) match_count = 0; count = ((match_count == 0)? (int)offsetcount : match_count * 2) - 2; if (count > 0) (void)memmove(offsets + 2, offsets, (size_t)count * sizeof(PCRE2_SIZE)); if (offsetcount >= 2) { offsets[0] = (PCRE2_SIZE)(current_subject - start_subject); offsets[1] = (PCRE2_SIZE)(ptr - start_subject); } if ((mb->moptions & PCRE2_DFA_SHORTEST) != 0) return match_count; } } break; /* ========================================================================== */ /* These opcodes add to the current list of states without looking at the current character. */ /*-----------------------------------------------------------------*/ case OP_ALT: do { code += GET(code, 1); } while (*code == OP_ALT); ADD_ACTIVE((int)(code - start_code), 0); break; /*-----------------------------------------------------------------*/ case OP_BRA: case OP_SBRA: do { ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); code += GET(code, 1); } while (*code == OP_ALT); break; /*-----------------------------------------------------------------*/ case OP_CBRA: case OP_SCBRA: ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE + IMM2_SIZE), 0); code += GET(code, 1); while (*code == OP_ALT) { ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); code += GET(code, 1); } break; /*-----------------------------------------------------------------*/ case OP_BRAZERO: case OP_BRAMINZERO: ADD_ACTIVE(state_offset + 1, 0); code += 1 + GET(code, 2); while (*code == OP_ALT) code += GET(code, 1); ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); break; /*-----------------------------------------------------------------*/ case OP_SKIPZERO: code += 1 + GET(code, 2); while (*code == OP_ALT) code += GET(code, 1); ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); break; /*-----------------------------------------------------------------*/ case OP_CIRC: if (ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0) { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_CIRCM: if ((ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0) || ((ptr != end_subject || (mb->poptions & PCRE2_ALT_CIRCUMFLEX) != 0 ) && WAS_NEWLINE(ptr))) { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_EOD: if (ptr >= end_subject) { if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0) return PCRE2_ERROR_PARTIAL; else { ADD_ACTIVE(state_offset + 1, 0); } } break; /*-----------------------------------------------------------------*/ case OP_SOD: if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_SOM: if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); } break; /* ========================================================================== */ /* These opcodes inspect the next subject character, and sometimes the previous one as well, but do not have an argument. The variable clen contains the length of the current character and is zero if we are at the end of the subject. */ /*-----------------------------------------------------------------*/ case OP_ANY: if (clen > 0 && !IS_NEWLINE(ptr)) { if (ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else { ADD_NEW(state_offset + 1, 0); } } break; /*-----------------------------------------------------------------*/ case OP_ALLANY: if (clen > 0) { ADD_NEW(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_EODN: if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - mb->nllen)) { if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0) return PCRE2_ERROR_PARTIAL; ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_DOLL: if ((mb->moptions & PCRE2_NOTEOL) == 0) { if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) could_continue = TRUE; else if (clen == 0 || ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) && (ptr == end_subject - mb->nllen) )) { ADD_ACTIVE(state_offset + 1, 0); } else if (ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0) { reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, 1); } else could_continue = partial_newline = TRUE; } } break; /*-----------------------------------------------------------------*/ case OP_DOLLM: if ((mb->moptions & PCRE2_NOTEOL) == 0) { if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) could_continue = TRUE; else if (clen == 0 || ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr))) { ADD_ACTIVE(state_offset + 1, 0); } else if (ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0) { reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, 1); } else could_continue = partial_newline = TRUE; } } else if (IS_NEWLINE(ptr)) { ADD_ACTIVE(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_DIGIT: case OP_WHITESPACE: case OP_WORDCHAR: if (clen > 0 && c < 256 && ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0) { ADD_NEW(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_NOT_DIGIT: case OP_NOT_WHITESPACE: case OP_NOT_WORDCHAR: if (clen > 0 && (c >= 256 || ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)) { ADD_NEW(state_offset + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_WORD_BOUNDARY: case OP_NOT_WORD_BOUNDARY: { int left_word, right_word; if (ptr > start_subject) { PCRE2_SPTR temp = ptr - 1; if (temp < mb->start_used_ptr) mb->start_used_ptr = temp; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (utf) { BACKCHAR(temp); } #endif GETCHARTEST(d, temp); #ifdef SUPPORT_UNICODE if ((mb->poptions & PCRE2_UCP) != 0) { if (d == '_') left_word = TRUE; else { uint32_t cat = UCD_CATEGORY(d); left_word = (cat == ucp_L || cat == ucp_N); } } else #endif left_word = d < 256 && (ctypes[d] & ctype_word) != 0; } else left_word = FALSE; if (clen > 0) { if (ptr >= mb->last_used_ptr) { PCRE2_SPTR temp = ptr + 1; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (utf) { FORWARDCHARTEST(temp, mb->end_subject); } #endif mb->last_used_ptr = temp; } #ifdef SUPPORT_UNICODE if ((mb->poptions & PCRE2_UCP) != 0) { if (c == '_') right_word = TRUE; else { uint32_t cat = UCD_CATEGORY(c); right_word = (cat == ucp_L || cat == ucp_N); } } else #endif right_word = c < 256 && (ctypes[c] & ctype_word) != 0; } else right_word = FALSE; if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY)) { ADD_ACTIVE(state_offset + 1, 0); } } break; /*-----------------------------------------------------------------*/ /* Check the next character by Unicode property. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ #ifdef SUPPORT_UNICODE case OP_PROP: case OP_NOTPROP: if (clen > 0) { BOOL OK; const uint32_t *cp; const ucd_record * prop = GET_UCD(c); switch(code[1]) { case PT_ANY: OK = TRUE; break; case PT_LAMP: OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt; break; case PT_GC: OK = PRIV(ucp_gentype)[prop->chartype] == code[2]; break; case PT_PC: OK = prop->chartype == code[2]; break; case PT_SC: OK = prop->script == code[2]; break; /* These are specials for combination cases. */ case PT_ALNUM: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N; break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: OK = TRUE; break; default: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z; break; } break; case PT_WORD: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE; break; case PT_CLIST: cp = PRIV(ucd_caseless_sets) + code[2]; for (;;) { if (c < *cp) { OK = FALSE; break; } if (c == *cp++) { OK = TRUE; break; } } break; case PT_UCNC: OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ default: OK = codevalue != OP_PROP; break; } if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); } } break; #endif /* ========================================================================== */ /* These opcodes likewise inspect the subject character, but have an argument that is not a data character. It is one of these opcodes: OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE, OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */ case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { if (d == OP_ANY && ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (count > 0 && codevalue == OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW(state_offset, count); } } break; /*-----------------------------------------------------------------*/ case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSQUERY: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { if (d == OP_ANY && ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset + 2, 0); } } break; /*-----------------------------------------------------------------*/ case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPOSSTAR: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { if (d == OP_ANY && ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSSTAR) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset, 0); } } break; /*-----------------------------------------------------------------*/ case OP_TYPEEXACT: count = current_state->count; /* Number already matched */ if (clen > 0) { if (d == OP_ANY && ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (++count >= (int)GET2(code, 1)) { ADD_NEW(state_offset + 1 + IMM2_SIZE + 1, 0); } else { ADD_NEW(state_offset, count); } } } break; /*-----------------------------------------------------------------*/ case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); count = current_state->count; /* Number already matched */ if (clen > 0) { if (d == OP_ANY && ptr + 1 >= mb->end_subject && (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && c == NLBLOCK->nl[0]) { could_continue = partial_newline = TRUE; } else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || (c < 256 && (d != OP_ANY || !IS_NEWLINE(ptr)) && ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) { if (codevalue == OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW(state_offset + 2 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } } break; /* ========================================================================== */ /* These are virtual opcodes that are used when something like OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its argument. It keeps the code above fast for the other cases. The argument is in the d variable. */ #ifdef SUPPORT_UNICODE case OP_PROP_EXTRA + OP_TYPEPLUS: case OP_PROP_EXTRA + OP_TYPEMINPLUS: case OP_PROP_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); } if (clen > 0) { BOOL OK; const uint32_t *cp; const ucd_record * prop = GET_UCD(c); switch(code[2]) { case PT_ANY: OK = TRUE; break; case PT_LAMP: OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt; break; case PT_GC: OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; break; case PT_PC: OK = prop->chartype == code[3]; break; case PT_SC: OK = prop->script == code[3]; break; /* These are specials for combination cases. */ case PT_ALNUM: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N; break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: OK = TRUE; break; default: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z; break; } break; case PT_WORD: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE; break; case PT_CLIST: cp = PRIV(ucd_caseless_sets) + code[3]; for (;;) { if (c < *cp) { OK = FALSE; break; } if (c == *cp++) { OK = TRUE; break; } } break; case PT_UCNC: OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ default: OK = codevalue != OP_PROP; break; } if (OK == (d == OP_PROP)) { if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW(state_offset, count); } } break; /*-----------------------------------------------------------------*/ case OP_EXTUNI_EXTRA + OP_TYPEPLUS: case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS: case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { int ncount = 0; if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, &ncount); count++; ADD_NEW_DATA(-state_offset, count, ncount); } break; #endif /*-----------------------------------------------------------------*/ case OP_ANYNL_EXTRA + OP_TYPEPLUS: case OP_ANYNL_EXTRA + OP_TYPEMINPLUS: case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { int ncount = 0; switch (c) { case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; goto ANYNL01; case CHAR_CR: if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL01: case CHAR_LF: if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW_DATA(-state_offset, count, ncount); break; default: break; } } break; /*-----------------------------------------------------------------*/ case OP_VSPACE_EXTRA + OP_TYPEPLUS: case OP_VSPACE_EXTRA + OP_TYPEMINPLUS: case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { BOOL OK; switch (c) { VSPACE_CASES: OK = TRUE; break; default: OK = FALSE; break; } if (OK == (d == OP_VSPACE)) { if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW_DATA(-state_offset, count, 0); } } break; /*-----------------------------------------------------------------*/ case OP_HSPACE_EXTRA + OP_TYPEPLUS: case OP_HSPACE_EXTRA + OP_TYPEMINPLUS: case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { BOOL OK; switch (c) { HSPACE_CASES: OK = TRUE; break; default: OK = FALSE; break; } if (OK == (d == OP_HSPACE)) { if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW_DATA(-state_offset, count, 0); } } break; /*-----------------------------------------------------------------*/ #ifdef SUPPORT_UNICODE case OP_PROP_EXTRA + OP_TYPEQUERY: case OP_PROP_EXTRA + OP_TYPEMINQUERY: case OP_PROP_EXTRA + OP_TYPEPOSQUERY: count = 4; goto QS1; case OP_PROP_EXTRA + OP_TYPESTAR: case OP_PROP_EXTRA + OP_TYPEMINSTAR: case OP_PROP_EXTRA + OP_TYPEPOSSTAR: count = 0; QS1: ADD_ACTIVE(state_offset + 4, 0); if (clen > 0) { BOOL OK; const uint32_t *cp; const ucd_record * prop = GET_UCD(c); switch(code[2]) { case PT_ANY: OK = TRUE; break; case PT_LAMP: OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt; break; case PT_GC: OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; break; case PT_PC: OK = prop->chartype == code[3]; break; case PT_SC: OK = prop->script == code[3]; break; /* These are specials for combination cases. */ case PT_ALNUM: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N; break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: OK = TRUE; break; default: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z; break; } break; case PT_WORD: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE; break; case PT_CLIST: cp = PRIV(ucd_caseless_sets) + code[3]; for (;;) { if (c < *cp) { OK = FALSE; break; } if (c == *cp++) { OK = TRUE; break; } } break; case PT_UCNC: OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ default: OK = codevalue != OP_PROP; break; } if (OK == (d == OP_PROP)) { if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset + count, 0); } } break; /*-----------------------------------------------------------------*/ case OP_EXTUNI_EXTRA + OP_TYPEQUERY: case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY: case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY: count = 2; goto QS2; case OP_EXTUNI_EXTRA + OP_TYPESTAR: case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR: case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR: count = 0; QS2: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, &ncount); ADD_NEW_DATA(-(state_offset + count), 0, ncount); } break; #endif /*-----------------------------------------------------------------*/ case OP_ANYNL_EXTRA + OP_TYPEQUERY: case OP_ANYNL_EXTRA + OP_TYPEMINQUERY: case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY: count = 2; goto QS3; case OP_ANYNL_EXTRA + OP_TYPESTAR: case OP_ANYNL_EXTRA + OP_TYPEMINSTAR: case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR: count = 0; QS3: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { int ncount = 0; switch (c) { case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; goto ANYNL02; case CHAR_CR: if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL02: case CHAR_LF: if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW_DATA(-(state_offset + (int)count), 0, ncount); break; default: break; } } break; /*-----------------------------------------------------------------*/ case OP_VSPACE_EXTRA + OP_TYPEQUERY: case OP_VSPACE_EXTRA + OP_TYPEMINQUERY: case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY: count = 2; goto QS4; case OP_VSPACE_EXTRA + OP_TYPESTAR: case OP_VSPACE_EXTRA + OP_TYPEMINSTAR: case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR: count = 0; QS4: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { BOOL OK; switch (c) { VSPACE_CASES: OK = TRUE; break; default: OK = FALSE; break; } if (OK == (d == OP_VSPACE)) { if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW_DATA(-(state_offset + (int)count), 0, 0); } } break; /*-----------------------------------------------------------------*/ case OP_HSPACE_EXTRA + OP_TYPEQUERY: case OP_HSPACE_EXTRA + OP_TYPEMINQUERY: case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY: count = 2; goto QS5; case OP_HSPACE_EXTRA + OP_TYPESTAR: case OP_HSPACE_EXTRA + OP_TYPEMINSTAR: case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR: count = 0; QS5: ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { BOOL OK; switch (c) { HSPACE_CASES: OK = TRUE; break; default: OK = FALSE; break; } if (OK == (d == OP_HSPACE)) { if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW_DATA(-(state_offset + (int)count), 0, 0); } } break; /*-----------------------------------------------------------------*/ #ifdef SUPPORT_UNICODE case OP_PROP_EXTRA + OP_TYPEEXACT: case OP_PROP_EXTRA + OP_TYPEUPTO: case OP_PROP_EXTRA + OP_TYPEMINUPTO: case OP_PROP_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT) { ADD_ACTIVE(state_offset + 1 + IMM2_SIZE + 3, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; const uint32_t *cp; const ucd_record * prop = GET_UCD(c); switch(code[1 + IMM2_SIZE + 1]) { case PT_ANY: OK = TRUE; break; case PT_LAMP: OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt; break; case PT_GC: OK = PRIV(ucp_gentype)[prop->chartype] == code[1 + IMM2_SIZE + 2]; break; case PT_PC: OK = prop->chartype == code[1 + IMM2_SIZE + 2]; break; case PT_SC: OK = prop->script == code[1 + IMM2_SIZE + 2]; break; /* These are specials for combination cases. */ case PT_ALNUM: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N; break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: OK = TRUE; break; default: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z; break; } break; case PT_WORD: OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE; break; case PT_CLIST: cp = PRIV(ucd_caseless_sets) + code[1 + IMM2_SIZE + 2]; for (;;) { if (c < *cp) { OK = FALSE; break; } if (c == *cp++) { OK = TRUE; break; } } break; case PT_UCNC: OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || c >= 0xe000; break; /* Should never occur, but keep compilers from grumbling. */ default: OK = codevalue != OP_PROP; break; } if (OK == (d == OP_PROP)) { if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW(state_offset + 1 + IMM2_SIZE + 3, 0); } else { ADD_NEW(state_offset, count); } } } break; /*-----------------------------------------------------------------*/ case OP_EXTUNI_EXTRA + OP_TYPEEXACT: case OP_EXTUNI_EXTRA + OP_TYPEUPTO: case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO: case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT) { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { PCRE2_SPTR nptr; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; if (++count >= (int)GET2(code, 1)) { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } else { ADD_NEW_DATA(-state_offset, count, ncount); } } break; #endif /*-----------------------------------------------------------------*/ case OP_ANYNL_EXTRA + OP_TYPEEXACT: case OP_ANYNL_EXTRA + OP_TYPEUPTO: case OP_ANYNL_EXTRA + OP_TYPEMINUPTO: case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT) { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { int ncount = 0; switch (c) { case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; goto ANYNL03; case CHAR_CR: if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1; /* Fall through */ ANYNL03: case CHAR_LF: if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } else { ADD_NEW_DATA(-state_offset, count, ncount); } break; default: break; } } break; /*-----------------------------------------------------------------*/ case OP_VSPACE_EXTRA + OP_TYPEEXACT: case OP_VSPACE_EXTRA + OP_TYPEUPTO: case OP_VSPACE_EXTRA + OP_TYPEMINUPTO: case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT) { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; switch (c) { VSPACE_CASES: OK = TRUE; break; default: OK = FALSE; } if (OK == (d == OP_VSPACE)) { if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } else { ADD_NEW_DATA(-state_offset, count, 0); } } } break; /*-----------------------------------------------------------------*/ case OP_HSPACE_EXTRA + OP_TYPEEXACT: case OP_HSPACE_EXTRA + OP_TYPEUPTO: case OP_HSPACE_EXTRA + OP_TYPEMINUPTO: case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO: if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT) { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } count = current_state->count; /* Number already matched */ if (clen > 0) { BOOL OK; switch (c) { HSPACE_CASES: OK = TRUE; break; default: OK = FALSE; break; } if (OK == (d == OP_HSPACE)) { if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } else { ADD_NEW_DATA(-state_offset, count, 0); } } } break; /* ========================================================================== */ /* These opcodes are followed by a character that is usually compared to the current subject character; it is loaded into d. We still get here even if there is no subject character, because in some cases zero repetitions are permitted. */ /*-----------------------------------------------------------------*/ case OP_CHAR: if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); } break; /*-----------------------------------------------------------------*/ case OP_CHARI: if (clen == 0) break; #ifdef SUPPORT_UNICODE if (utf_or_ucp) { if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else { unsigned int othercase; if (c < 128) othercase = fcc[c]; else othercase = UCD_OTHERCASE(c); if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); } } } else #endif /* SUPPORT_UNICODE */ /* Not UTF or UCP mode */ { if (TABLE_GET(c, lcc, c) == TABLE_GET(d, lcc, d)) { ADD_NEW(state_offset + 2, 0); } } break; #ifdef SUPPORT_UNICODE /*-----------------------------------------------------------------*/ /* This is a tricky one because it can match more than one character. Find out how many characters to skip, and then set up a negative state to wait for them to pass before continuing. */ case OP_EXTUNI: if (clen > 0) { int ncount = 0; PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, ncount); } break; #endif /*-----------------------------------------------------------------*/ /* This is a tricky like EXTUNI because it too can match more than one character (when CR is followed by LF). In this case, set up a negative state to wait for one character to pass before continuing. */ case OP_ANYNL: if (clen > 0) switch(c) { case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; /* Fall through */ case CHAR_LF: ADD_NEW(state_offset + 1, 0); break; case CHAR_CR: if (ptr + 1 >= end_subject) { ADD_NEW(state_offset + 1, 0); if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; } else if (UCHAR21TEST(ptr + 1) == CHAR_LF) { ADD_NEW_DATA(-(state_offset + 1), 0, 1); } else { ADD_NEW(state_offset + 1, 0); } break; } break; /*-----------------------------------------------------------------*/ case OP_NOT_VSPACE: if (clen > 0) switch(c) { VSPACE_CASES: break; default: ADD_NEW(state_offset + 1, 0); break; } break; /*-----------------------------------------------------------------*/ case OP_VSPACE: if (clen > 0) switch(c) { VSPACE_CASES: ADD_NEW(state_offset + 1, 0); break; default: break; } break; /*-----------------------------------------------------------------*/ case OP_NOT_HSPACE: if (clen > 0) switch(c) { HSPACE_CASES: break; default: ADD_NEW(state_offset + 1, 0); break; } break; /*-----------------------------------------------------------------*/ case OP_HSPACE: if (clen > 0) switch(c) { HSPACE_CASES: ADD_NEW(state_offset + 1, 0); break; default: break; } break; /*-----------------------------------------------------------------*/ /* Match a negated single character casefully. */ case OP_NOT: if (clen > 0 && c != d) { ADD_NEW(state_offset + dlen + 1, 0); } break; /*-----------------------------------------------------------------*/ /* Match a negated single character caselessly. */ case OP_NOTI: if (clen > 0) { uint32_t otherd; #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); if (c != d && c != otherd) { ADD_NEW(state_offset + dlen + 1, 0); } } break; /*-----------------------------------------------------------------*/ case OP_PLUSI: case OP_MINPLUSI: case OP_POSPLUSI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTPOSPLUSI: caseless = TRUE; codevalue -= OP_STARI - OP_STAR; /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); } if (clen > 0) { uint32_t otherd = NOTACHAR; if (caseless) { #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { if (count > 0 && (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS)) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW(state_offset, count); } } break; /*-----------------------------------------------------------------*/ case OP_QUERYI: case OP_MINQUERYI: case OP_POSQUERYI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTPOSQUERYI: caseless = TRUE; codevalue -= OP_STARI - OP_STAR; /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTPOSQUERY: ADD_ACTIVE(state_offset + dlen + 1, 0); if (clen > 0) { uint32_t otherd = NOTACHAR; if (caseless) { #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset + dlen + 1, 0); } } break; /*-----------------------------------------------------------------*/ case OP_STARI: case OP_MINSTARI: case OP_POSSTARI: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPOSSTARI: caseless = TRUE; codevalue -= OP_STARI - OP_STAR; /* Fall through */ case OP_STAR: case OP_MINSTAR: case OP_POSSTAR: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPOSSTAR: ADD_ACTIVE(state_offset + dlen + 1, 0); if (clen > 0) { uint32_t otherd = NOTACHAR; if (caseless) { #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset, 0); } } break; /*-----------------------------------------------------------------*/ case OP_EXACTI: case OP_NOTEXACTI: caseless = TRUE; codevalue -= OP_STARI - OP_STAR; /* Fall through */ case OP_EXACT: case OP_NOTEXACT: count = current_state->count; /* Number already matched */ if (clen > 0) { uint32_t otherd = NOTACHAR; if (caseless) { #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { if (++count >= (int)GET2(code, 1)) { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } } break; /*-----------------------------------------------------------------*/ case OP_UPTOI: case OP_MINUPTOI: case OP_POSUPTOI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTPOSUPTOI: caseless = TRUE; codevalue -= OP_STARI - OP_STAR; /* Fall through */ case OP_UPTO: case OP_MINUPTO: case OP_POSUPTO: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTPOSUPTO: ADD_ACTIVE(state_offset + dlen + 1 + IMM2_SIZE, 0); count = current_state->count; /* Number already matched */ if (clen > 0) { uint32_t otherd = NOTACHAR; if (caseless) { #ifdef SUPPORT_UNICODE if (utf_or_ucp && d >= 128) otherd = UCD_OTHERCASE(d); else #endif /* SUPPORT_UNICODE */ otherd = TABLE_GET(d, fcc, d); } if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) { if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= (int)GET2(code, 1)) { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } } break; /* ========================================================================== */ /* These are the class-handling opcodes */ case OP_CLASS: case OP_NCLASS: case OP_XCLASS: { BOOL isinclass = FALSE; int next_state_offset; PCRE2_SPTR ecode; /* For a simple class, there is always just a 32-byte table, and we can set isinclass from it. */ if (codevalue != OP_XCLASS) { ecode = code + 1 + (32 / sizeof(PCRE2_UCHAR)); if (clen > 0) { isinclass = (c > 255)? (codevalue == OP_NCLASS) : ((((uint8_t *)(code + 1))[c/8] & (1u << (c&7))) != 0); } } /* An extended class may have a table or a list of single characters, ranges, or both, and it may be positive or negative. There's a function that sorts all this out. */ else { ecode = code + GET(code, 1); if (clen > 0) isinclass = PRIV(xclass)(c, code + 1 + LINK_SIZE, utf); } /* At this point, isinclass is set for all kinds of class, and ecode points to the byte after the end of the class. If there is a quantifier, this is where it will be. */ next_state_offset = (int)(ecode - start_code); switch (*ecode) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPOSSTAR: ADD_ACTIVE(next_state_offset + 1, 0); if (isinclass) { if (*ecode == OP_CRPOSSTAR) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(state_offset, 0); } break; case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSPLUS: count = current_state->count; /* Already matched */ if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); } if (isinclass) { if (count > 0 && *ecode == OP_CRPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } count++; ADD_NEW(state_offset, count); } break; case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSQUERY: ADD_ACTIVE(next_state_offset + 1, 0); if (isinclass) { if (*ecode == OP_CRPOSQUERY) { active_count--; /* Remove non-match possibility */ next_active_state--; } ADD_NEW(next_state_offset + 1, 0); } break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: count = current_state->count; /* Already matched */ if (count >= (int)GET2(ecode, 1)) { ADD_ACTIVE(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } if (isinclass) { int max = (int)GET2(ecode, 1 + IMM2_SIZE); if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1)) { active_count--; /* Remove non-match possibility */ next_active_state--; } if (++count >= max && max != 0) /* Max 0 => no limit */ { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } else { ADD_NEW(state_offset, count); } } break; default: if (isinclass) { ADD_NEW(next_state_offset, 0); } break; } } break; /* ========================================================================== */ /* These are the opcodes for fancy brackets of various kinds. We have to use recursion in order to handle them. The "always failing" assertion (?!) is optimised to OP_FAIL when compiling, so we have to support that, though the other "backtracking verbs" are not supported. */ case OP_FAIL: forced_fail++; /* Count FAILs for multiple states */ break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: { int rc; int *local_workspace; PCRE2_SIZE *local_offsets; PCRE2_SPTR endasscode = code + GET(code, 1); RWS_anchor *rws = (RWS_anchor *)RWS; if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) { rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); if (rc != 0) return rc; RWS = (int *)rws; } local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); rc = internal_dfa_match( mb, /* static match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ RWS_RSIZE, /* size of same */ rlevel, /* function recursion level */ RWS); /* recursion workspace */ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } } break; /*-----------------------------------------------------------------*/ case OP_COND: case OP_SCOND: { int codelink = (int)GET(code, 1); PCRE2_UCHAR condcode; /* Because of the way auto-callout works during compile, a callout item is inserted between OP_COND and an assertion condition. This does not happen for the other conditions. */ if (code[LINK_SIZE + 1] == OP_CALLOUT || code[LINK_SIZE + 1] == OP_CALLOUT_STR) { PCRE2_SIZE callout_length; rrc = do_callout(code, offsets, current_subject, ptr, mb, 1 + LINK_SIZE, &callout_length); if (rrc < 0) return rrc; /* Abandon */ if (rrc > 0) break; /* Fail this thread */ code += callout_length; /* Skip callout data */ } condcode = code[LINK_SIZE+1]; /* Back reference conditions and duplicate named recursion conditions are not supported */ if (condcode == OP_CREF || condcode == OP_DNCREF || condcode == OP_DNRREF) return PCRE2_ERROR_DFA_UCOND; /* The DEFINE condition is always false, and the assertion (?!) is converted to OP_FAIL. */ if (condcode == OP_FALSE || condcode == OP_FAIL) { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } /* There is also an always-true condition */ else if (condcode == OP_TRUE) { ADD_ACTIVE(state_offset + LINK_SIZE + 2, 0); } /* The only supported version of OP_RREF is for the value RREF_ANY, which means "test if in any recursion". We can't test for specifically recursed groups. */ else if (condcode == OP_RREF) { unsigned int value = GET2(code, LINK_SIZE + 2); if (value != RREF_ANY) return PCRE2_ERROR_DFA_UCOND; if (mb->recursive != NULL) { ADD_ACTIVE(state_offset + LINK_SIZE + 2 + IMM2_SIZE, 0); } else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } } /* Otherwise, the condition is an assertion */ else { int rc; int *local_workspace; PCRE2_SIZE *local_offsets; PCRE2_SPTR asscode = code + LINK_SIZE + 1; PCRE2_SPTR endasscode = asscode + GET(asscode, 1); RWS_anchor *rws = (RWS_anchor *)RWS; if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) { rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); if (rc != 0) return rc; RWS = (int *)rws; } local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); rc = internal_dfa_match( mb, /* fixed match data */ asscode, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ RWS_RSIZE, /* size of same */ rlevel, /* function recursion level */ RWS); /* recursion workspace */ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } } } break; /*-----------------------------------------------------------------*/ case OP_RECURSE: { int rc; int *local_workspace; PCRE2_SIZE *local_offsets; RWS_anchor *rws = (RWS_anchor *)RWS; dfa_recursion_info *ri; PCRE2_SPTR callpat = start_code + GET(code, 1); uint32_t recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); if (rws->free < RWS_RSIZE + RWS_OVEC_RSIZE) { rc = more_workspace(&rws, RWS_OVEC_RSIZE, mb); if (rc != 0) return rc; RWS = (int *)rws; } local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE; rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE; /* Check for repeating a recursion without advancing the subject pointer. This should catch convoluted mutual recursions. (Some simple cases are caught at compile time.) */ for (ri = mb->recursive; ri != NULL; ri = ri->prevrec) if (recno == ri->group_num && ptr == ri->subject_position) return PCRE2_ERROR_RECURSELOOP; /* Remember this recursion and where we started it so as to catch infinite loops. */ new_recursive.group_num = recno; new_recursive.subject_position = ptr; new_recursive.prevrec = mb->recursive; mb->recursive = &new_recursive; rc = internal_dfa_match( mb, /* fixed match data */ callpat, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ RWS_OVEC_RSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ RWS_RSIZE, /* size of same */ rlevel, /* function recursion level */ RWS); /* recursion workspace */ rws->free += RWS_RSIZE + RWS_OVEC_RSIZE; mb->recursive = new_recursive.prevrec; /* Done this recursion */ /* Ran out of internal offsets */ if (rc == 0) return PCRE2_ERROR_DFA_RECURSE; /* For each successful matched substring, set up the next state with a count of characters to skip before trying it. Note that the count is in characters, not bytes. */ if (rc > 0) { for (rc = rc*2 - 2; rc >= 0; rc -= 2) { PCRE2_SIZE charcount = local_offsets[rc+1] - local_offsets[rc]; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (utf) { PCRE2_SPTR p = start_subject + local_offsets[rc]; PCRE2_SPTR pp = start_subject + local_offsets[rc+1]; while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; } #endif if (charcount > 0) { ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0, (int)(charcount - 1)); } else { ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0); } } } else if (rc != PCRE2_ERROR_NOMATCH) return rc; } break; /*-----------------------------------------------------------------*/ case OP_BRAPOS: case OP_SBRAPOS: case OP_CBRAPOS: case OP_SCBRAPOS: case OP_BRAPOSZERO: { int rc; int *local_workspace; PCRE2_SIZE *local_offsets; PCRE2_SIZE charcount, matched_count; PCRE2_SPTR local_ptr = ptr; RWS_anchor *rws = (RWS_anchor *)RWS; BOOL allow_zero; if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) { rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); if (rc != 0) return rc; RWS = (int *)rws; } local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; if (codevalue == OP_BRAPOSZERO) { allow_zero = TRUE; codevalue = *(++code); /* Codevalue will be one of above BRAs */ } else allow_zero = FALSE; /* Loop to match the subpattern as many times as possible as if it were a complete pattern. */ for (matched_count = 0;; matched_count++) { rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ local_ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ RWS_RSIZE, /* size of same */ rlevel, /* function recursion level */ RWS); /* recursion workspace */ /* Failed to match */ if (rc < 0) { if (rc != PCRE2_ERROR_NOMATCH) return rc; break; } /* Matched: break the loop if zero characters matched. */ charcount = local_offsets[1] - local_offsets[0]; if (charcount == 0) break; local_ptr += charcount; /* Advance temporary position ptr */ } rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; /* At this point we have matched the subpattern matched_count times, and local_ptr is pointing to the character after the end of the last match. */ if (matched_count > 0 || allow_zero) { PCRE2_SPTR end_subpattern = code; int next_state_offset; do { end_subpattern += GET(end_subpattern, 1); } while (*end_subpattern == OP_ALT); next_state_offset = (int)(end_subpattern - start_code + LINK_SIZE + 1); /* Optimization: if there are no more active states, and there are no new states yet set up, then skip over the subject string right here, to save looping. Otherwise, set up the new state to swing into action when the end of the matched substring is reached. */ if (i + 1 >= active_count && new_count == 0) { ptr = local_ptr; clen = 0; ADD_NEW(next_state_offset, 0); } else { PCRE2_SPTR p = ptr; PCRE2_SPTR pp = local_ptr; charcount = (PCRE2_SIZE)(pp - p); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (utf) while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; #endif ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1)); } } } break; /*-----------------------------------------------------------------*/ case OP_ONCE: { int rc; int *local_workspace; PCRE2_SIZE *local_offsets; RWS_anchor *rws = (RWS_anchor *)RWS; if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) { rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); if (rc != 0) return rc; RWS = (int *)rws; } local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ RWS_RSIZE, /* size of same */ rlevel, /* function recursion level */ RWS); /* recursion workspace */ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; if (rc >= 0) { PCRE2_SPTR end_subpattern = code; PCRE2_SIZE charcount = local_offsets[1] - local_offsets[0]; int next_state_offset, repeat_state_offset; do { end_subpattern += GET(end_subpattern, 1); } while (*end_subpattern == OP_ALT); next_state_offset = (int)(end_subpattern - start_code + LINK_SIZE + 1); /* If the end of this subpattern is KETRMAX or KETRMIN, we must arrange for the repeat state also to be added to the relevant list. Calculate the offset, or set -1 for no repeat. */ repeat_state_offset = (*end_subpattern == OP_KETRMAX || *end_subpattern == OP_KETRMIN)? (int)(end_subpattern - start_code - GET(end_subpattern, 1)) : -1; /* If we have matched an empty string, add the next state at the current character pointer. This is important so that the duplicate checking kicks in, which is what breaks infinite loops that match an empty string. */ if (charcount == 0) { ADD_ACTIVE(next_state_offset, 0); } /* Optimization: if there are no more active states, and there are no new states yet set up, then skip over the subject string right here, to save looping. Otherwise, set up the new state to swing into action when the end of the matched substring is reached. */ else if (i + 1 >= active_count && new_count == 0) { ptr += charcount; clen = 0; ADD_NEW(next_state_offset, 0); /* If we are adding a repeat state at the new character position, we must fudge things so that it is the only current state. Otherwise, it might be a duplicate of one we processed before, and that would cause it to be skipped. */ if (repeat_state_offset >= 0) { next_active_state = active_states; active_count = 0; i = -1; ADD_ACTIVE(repeat_state_offset, 0); } } else { #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (utf) { PCRE2_SPTR p = start_subject + local_offsets[0]; PCRE2_SPTR pp = start_subject + local_offsets[1]; while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--; } #endif ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1)); if (repeat_state_offset >= 0) { ADD_NEW_DATA(-repeat_state_offset, 0, (int)(charcount - 1)); } } } else if (rc != PCRE2_ERROR_NOMATCH) return rc; } break; /* ========================================================================== */ /* Handle callouts */ case OP_CALLOUT: case OP_CALLOUT_STR: { PCRE2_SIZE callout_length; rrc = do_callout(code, offsets, current_subject, ptr, mb, 0, &callout_length); if (rrc < 0) return rrc; /* Abandon */ if (rrc == 0) { ADD_ACTIVE(state_offset + (int)callout_length, 0); } } break; /* ========================================================================== */ default: /* Unsupported opcode */ return PCRE2_ERROR_DFA_UITEM; } NEXT_ACTIVE_STATE: continue; } /* End of loop scanning active states */ /* We have finished the processing at the current subject character. If no new states have been set for the next character, we have found all the matches that we are going to find. If partial matching has been requested, check for appropriate conditions. The "forced_ fail" variable counts the number of (*F) encountered for the character. If it is equal to the original active_count (saved in workspace[1]) it means that (*F) was found on every active state. In this case we don't want to give a partial match. The "could_continue" variable is true if a state could have continued but for the fact that the end of the subject was reached. */ if (new_count <= 0) { if (could_continue && /* Some could go on, and */ forced_fail != workspace[1] && /* Not all forced fail & */ ( /* either... */ (mb->moptions & PCRE2_PARTIAL_HARD) != 0 /* Hard partial */ || /* or... */ ((mb->moptions & PCRE2_PARTIAL_SOFT) != 0 && /* Soft partial and */ match_count < 0) /* no matches */ ) && /* And... */ ( partial_newline || /* Either partial NL */ ( /* or ... */ ptr >= end_subject && /* End of subject and */ ( /* either */ ptr > mb->start_used_ptr || /* Inspected non-empty string */ mb->allowemptypartial /* or pattern has lookbehind */ ) /* or could match empty */ ) )) match_count = PCRE2_ERROR_PARTIAL; break; /* Exit from loop along the subject string */ } /* One or more states are active for the next character. */ ptr += clen; /* Advance to next subject character */ } /* Loop to move along the subject string */ /* Control gets here from "break" a few lines above. If we have a match and PCRE2_ENDANCHORED is set, the match fails. */ if (match_count >= 0 && ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 && ptr < end_subject) match_count = PCRE2_ERROR_NOMATCH; return match_count; } /************************************************* * Match a pattern using the DFA algorithm * *************************************************/ /* This function matches a compiled pattern to a subject string, using the alternate matching algorithm that finds all matches at once. Arguments: code points to the compiled pattern subject subject string length length of subject string startoffset where to start matching in the subject options option bits match_data points to a match data structure gcontext points to a match context workspace pointer to workspace wscount size of workspace Returns: > 0 => number of match offset pairs placed in offsets = 0 => offsets overflowed; longest matches are present -1 => failed to match < -1 => some kind of unexpected problem */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_dfa_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount) { int rc; int was_zero_terminated = 0; const pcre2_real_code *re = (const pcre2_real_code *)code; PCRE2_SPTR start_match; PCRE2_SPTR end_subject; PCRE2_SPTR bumpalong_limit; PCRE2_SPTR req_cu_ptr; BOOL utf, anchored, startline, firstline; BOOL has_first_cu = FALSE; BOOL has_req_cu = FALSE; #if PCRE2_CODE_UNIT_WIDTH == 8 PCRE2_SPTR memchr_found_first_cu = NULL; PCRE2_SPTR memchr_found_first_cu2 = NULL; #endif PCRE2_UCHAR first_cu = 0; PCRE2_UCHAR first_cu2 = 0; PCRE2_UCHAR req_cu = 0; PCRE2_UCHAR req_cu2 = 0; const uint8_t *start_bits = NULL; /* We need to have mb pointing to a match block, because the IS_NEWLINE macro is used below, and it expects NLBLOCK to be defined as a pointer. */ pcre2_callout_block cb; dfa_match_block actual_match_block; dfa_match_block *mb = &actual_match_block; /* Set up a starting block of memory for use during recursive calls to internal_dfa_match(). By putting this on the stack, it minimizes resource use in the case when it is not needed. If this is too small, more memory is obtained from the heap. At the start of each block is an anchor structure.*/ int base_recursion_workspace[RWS_BASE_SIZE]; RWS_anchor *rws = (RWS_anchor *)base_recursion_workspace; rws->next = NULL; rws->size = RWS_BASE_SIZE; rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE; /* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated subject string. */ if (length == PCRE2_ZERO_TERMINATED) { length = PRIV(strlen)(subject); was_zero_terminated = 1; } /* Plausibility checks */ if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION; if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL) return PCRE2_ERROR_NULL; if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE; if (start_offset > length) return PCRE2_ERROR_BADOFFSET; /* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same time. */ if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 && ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) return PCRE2_ERROR_BADOPTION; /* Invalid UTF support is not available for DFA matching. */ if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0) return PCRE2_ERROR_DFA_UINVALID_UTF; /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; /* Check the code unit width. */ if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8) return PCRE2_ERROR_BADMODE; /* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the options variable for this function. Users of PCRE2 who are not calling the function directly would like to have a way of setting these flags, in the same way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and (*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be transferred to the options for this function. The bits are guaranteed to be adjacent, but do not have the same values. This bit of Boolean trickery assumes that the match-time bits are not more significant than the flag bits. If by accident this is not the case, a compile-time division by zero error will occur. */ #define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) #define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO /* If restarting after a partial match, do some sanity checks on the contents of the workspace. */ if ((options & PCRE2_DFA_RESTART) != 0) { if ((workspace[0] & (-2)) != 0 || workspace[1] < 1 || workspace[1] > (int)((wscount - 2)/INTS_PER_STATEBLOCK)) return PCRE2_ERROR_DFA_BADRESTART; } /* Set some local values */ utf = (re->overall_options & PCRE2_UTF) != 0; start_match = subject + start_offset; end_subject = subject + length; req_cu_ptr = start_match - 1; anchored = (options & (PCRE2_ANCHORED|PCRE2_DFA_RESTART)) != 0 || (re->overall_options & PCRE2_ANCHORED) != 0; /* The "must be at the start of a line" flags are used in a loop when finding where to start. */ startline = (re->flags & PCRE2_STARTLINE) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; bumpalong_limit = end_subject; /* Initialize and set up the fixed fields in the callout block, with a pointer in the match block. */ mb->cb = &cb; cb.version = 2; cb.subject = subject; cb.subject_length = (PCRE2_SIZE)(end_subject - subject); cb.callout_flags = 0; cb.capture_top = 1; /* No capture support */ cb.capture_last = 0; cb.mark = NULL; /* No (*MARK) support */ /* Get data from the match context, if present, and fill in the remaining fields in the match block. It is an error to set an offset limit without setting the flag at compile time. */ if (mcontext == NULL) { mb->callout = NULL; mb->memctl = re->memctl; mb->match_limit = PRIV(default_match_context).match_limit; mb->match_limit_depth = PRIV(default_match_context).depth_limit; mb->heap_limit = PRIV(default_match_context).heap_limit; } else { if (mcontext->offset_limit != PCRE2_UNSET) { if ((re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0) return PCRE2_ERROR_BADOFFSETLIMIT; bumpalong_limit = subject + mcontext->offset_limit; } mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; mb->match_limit = mcontext->match_limit; mb->match_limit_depth = mcontext->depth_limit; mb->heap_limit = mcontext->heap_limit; } if (mb->match_limit > re->limit_match) mb->match_limit = re->limit_match; if (mb->match_limit_depth > re->limit_depth) mb->match_limit_depth = re->limit_depth; if (mb->heap_limit > re->limit_heap) mb->heap_limit = re->limit_heap; mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; mb->tables = re->tables; mb->start_subject = subject; mb->end_subject = end_subject; mb->start_offset = start_offset; mb->allowemptypartial = (re->max_lookbehind > 0) || (re->flags & PCRE2_MATCH_EMPTY) != 0; mb->moptions = options; mb->poptions = re->overall_options; mb->match_call_count = 0; mb->heap_used = 0; /* Process the \R and newline settings. */ mb->bsr_convention = re->bsr_convention; mb->nltype = NLTYPE_FIXED; switch(re->newline_convention) { case PCRE2_NEWLINE_CR: mb->nllen = 1; mb->nl[0] = CHAR_CR; break; case PCRE2_NEWLINE_LF: mb->nllen = 1; mb->nl[0] = CHAR_NL; break; case PCRE2_NEWLINE_NUL: mb->nllen = 1; mb->nl[0] = CHAR_NUL; break; case PCRE2_NEWLINE_CRLF: mb->nllen = 2; mb->nl[0] = CHAR_CR; mb->nl[1] = CHAR_NL; break; case PCRE2_NEWLINE_ANY: mb->nltype = NLTYPE_ANY; break; case PCRE2_NEWLINE_ANYCRLF: mb->nltype = NLTYPE_ANYCRLF; break; default: return PCRE2_ERROR_INTERNAL; } /* Check a UTF string for validity if required. For 8-bit and 16-bit strings, we must also check that a starting offset does not point into the middle of a multiunit character. We check only the portion of the subject that is going to be inspected during matching - from the offset minus the maximum back reference to the given length. This saves time when a small part of a large subject is being matched by the use of a starting offset. Note that the maximum lookbehind is a number of characters, not code units. */ #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) { PCRE2_SPTR check_subject = start_match; /* start_match includes offset */ if (start_offset > 0) { #if PCRE2_CODE_UNIT_WIDTH != 32 unsigned int i; if (start_match < end_subject && NOT_FIRSTCU(*start_match)) return PCRE2_ERROR_BADUTFOFFSET; for (i = re->max_lookbehind; i > 0 && check_subject > subject; i--) { check_subject--; while (check_subject > subject && #if PCRE2_CODE_UNIT_WIDTH == 8 (*check_subject & 0xc0) == 0x80) #else /* 16-bit */ (*check_subject & 0xfc00) == 0xdc00) #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ check_subject--; } #else /* In the 32-bit library, one code unit equals one character. */ check_subject -= re->max_lookbehind; if (check_subject < subject) check_subject = subject; #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ } /* Validate the relevant portion of the subject. After an error, adjust the offset to be an absolute offset in the whole string. */ match_data->rc = PRIV(valid_utf)(check_subject, length - (PCRE2_SIZE)(check_subject - subject), &(match_data->startchar)); if (match_data->rc != 0) { match_data->startchar += (PCRE2_SIZE)(check_subject - subject); return match_data->rc; } } #endif /* SUPPORT_UNICODE */ /* Set up the first code unit to match, if available. If there's no first code unit there may be a bitmap of possible first characters. */ if ((re->flags & PCRE2_FIRSTSET) != 0) { has_first_cu = TRUE; first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); if ((re->flags & PCRE2_FIRSTCASELESS) != 0) { first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (first_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0) first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); #else if (first_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0)) first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); #endif #endif /* SUPPORT_UNICODE */ } } else if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) start_bits = re->start_bitmap; /* There may be a "last known required code unit" set. */ if ((re->flags & PCRE2_LASTSET) != 0) { has_req_cu = TRUE; req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit); if ((re->flags & PCRE2_LASTCASELESS) != 0) { req_cu2 = TABLE_GET(req_cu, mb->tables + fcc_offset, req_cu); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (req_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0) req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu); #else if (req_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0)) req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu); #endif #endif /* SUPPORT_UNICODE */ } } /* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT, free the memory that was obtained. */ if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0) { match_data->memctl.free((void *)match_data->subject, match_data->memctl.memory_data); match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT; } /* Fill in fields that are always returned in the match data. */ match_data->code = re; match_data->subject = NULL; /* Default for no match */ match_data->mark = NULL; match_data->matchedby = PCRE2_MATCHEDBY_DFA_INTERPRETER; /* Call the main matching function, looping for a non-anchored regex after a failed match. If not restarting, perform certain optimizations at the start of a match. */ for (;;) { /* ----------------- Start of match optimizations ---------------- */ /* There are some optimizations that avoid running the match if a known starting point is not found, or if a known later code unit is not present. However, there is an option (settable at compile time) that disables these, for testing and for ensuring that all callouts do actually occur. The optimizations must also be avoided when restarting a DFA match. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && (options & PCRE2_DFA_RESTART) == 0) { /* If firstline is TRUE, the start of the match is constrained to the first line of a multiline string. That is, the match must be before or at the first newline following the start of matching. Temporarily adjust end_subject so that we stop the optimization scans for a first code unit immediately after the first character of a newline (the first code unit can legitimately be a newline). If the match fails at the newline, later code breaks this loop. */ if (firstline) { PCRE2_SPTR t = start_match; #ifdef SUPPORT_UNICODE if (utf) { while (t < end_subject && !IS_NEWLINE(t)) { t++; ACROSSCHAR(t < end_subject, t, t++); } } else #endif while (t < end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } /* Anchored: check the first code unit if one is recorded. This may seem pointless but it can help in detecting a no match case without scanning for the required code unit. */ if (anchored) { if (has_first_cu || start_bits != NULL) { BOOL ok = start_match < end_subject; if (ok) { PCRE2_UCHAR c = UCHAR21TEST(start_match); ok = has_first_cu && (c == first_cu || c == first_cu2); if (!ok && start_bits != NULL) { #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif ok = (start_bits[c/8] & (1u << (c&7))) != 0; } } if (!ok) break; } } /* Not anchored. Advance to a unique first code unit if there is one. */ else { if (has_first_cu) { if (first_cu != first_cu2) /* Caseless */ { /* In 16-bit and 32_bit modes we have to do our own search, so can look for both cases at once. */ #if PCRE2_CODE_UNIT_WIDTH != 8 PCRE2_UCHAR smc; while (start_match < end_subject && (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) start_match++; #else /* In 8-bit mode, the use of memchr() gives a big speed up, even though we have to call it twice in order to find the earliest occurrence of the code unit in either of its cases. Caching is used to remember the positions of previously found code units. This can make a huge difference when the strings are very long and only one case is actually present. */ PCRE2_SPTR pp1 = NULL; PCRE2_SPTR pp2 = NULL; PCRE2_SIZE searchlength = end_subject - start_match; /* If we haven't got a previously found position for first_cu, or if the current starting position is later, we need to do a search. If the code unit is not found, set it to the end. */ if (memchr_found_first_cu == NULL || start_match > memchr_found_first_cu) { pp1 = memchr(start_match, first_cu, searchlength); memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1; } /* If the start is before a previously found position, use the previous position, or NULL if a previous search failed. */ else pp1 = (memchr_found_first_cu == end_subject)? NULL : memchr_found_first_cu; /* Do the same thing for the other case. */ if (memchr_found_first_cu2 == NULL || start_match > memchr_found_first_cu2) { pp2 = memchr(start_match, first_cu2, searchlength); memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2; } else pp2 = (memchr_found_first_cu2 == end_subject)? NULL : memchr_found_first_cu2; /* Set the start to the end of the subject if neither case was found. Otherwise, use the earlier found point. */ if (pp1 == NULL) start_match = (pp2 == NULL)? end_subject : pp2; else start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; #endif /* 8-bit handling */ } /* The caseful case is much simpler. */ else { #if PCRE2_CODE_UNIT_WIDTH != 8 while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) start_match++; #else /* 8-bit code units */ start_match = memchr(start_match, first_cu, end_subject - start_match); if (start_match == NULL) start_match = end_subject; #endif } /* If we can't find the required code unit, having reached the true end of the subject, break the bumpalong loop, to force a match failure, except when doing partial matching, when we let the next cycle run at the end of the subject. To see why, consider the pattern /(?<=abc)def/, which partially matches "abc", even though the string does not contain the starting character "d". If we have not reached the true end of the subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) we also let the cycle run, because the matching string is legitimately allowed to start with the first code unit of a newline. */ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && start_match >= mb->end_subject) break; } /* If there's no first code unit, advance to just after a linebreak for a multiline match if required. */ else if (startline) { if (start_match > mb->start_subject + start_offset) { #ifdef SUPPORT_UNICODE if (utf) { while (start_match < end_subject && !WAS_NEWLINE(start_match)) { start_match++; ACROSSCHAR(start_match < end_subject, start_match, start_match++); } } else #endif while (start_match < end_subject && !WAS_NEWLINE(start_match)) start_match++; /* If we have just passed a CR and the newline option is ANY or ANYCRLF, and we are now at a LF, advance the match position by one more code unit. */ if (start_match[-1] == CHAR_CR && (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && start_match < end_subject && UCHAR21TEST(start_match) == CHAR_NL) start_match++; } } /* If there's no first code unit or a requirement for a multiline line start, advance to a non-unique first code unit if any have been identified. The bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all code units greater than 254 set the 255 bit. */ else if (start_bits != NULL) { while (start_match < end_subject) { uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif if ((start_bits[c/8] & (1u << (c&7))) != 0) break; start_match++; } /* See comment above in first_cu checking about the next line. */ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && start_match >= mb->end_subject) break; } } /* End of first code unit handling */ /* Restore fudged end_subject */ end_subject = mb->end_subject; /* The following two optimizations are disabled for partial matching. */ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0) { PCRE2_SPTR p; /* The minimum matching length is a lower bound; no actual string of that length may actually match the pattern. Although the value is, strictly, in characters, we treat it as code units to avoid spending too much time in this optimization. */ if (end_subject - start_match < re->minlength) goto NOMATCH_EXIT; /* If req_cu is set, we know that that code unit must appear in the subject for the match to succeed. If the first code unit is set, req_cu must be later in the subject; otherwise the test starts at the match point. This optimization can save a huge amount of backtracking in patterns with nested unlimited repeats that aren't going to match. Writing separate code for cased/caseless versions makes it go faster, as does using an autoincrement and backing off on a match. As in the case of the first code unit, using memchr() in the 8-bit library gives a big speed up. Unlike the first_cu check above, we do not need to call memchr() twice in the caseless case because we only need to check for the presence of the character in either case, not find the first occurrence. The search can be skipped if the code unit was found later than the current starting point in a previous iteration of the bumpalong loop. HOWEVER: when the subject string is very, very long, searching to its end can take a long time, and give bad performance on quite ordinary patterns. This showed up when somebody was matching something like /^\d+C/ on a 32-megabyte string... so we don't do this when the string is sufficiently long, but it's worth searching a lot more for unanchored patterns. */ p = start_match + (has_first_cu? 1:0); if (has_req_cu && p > req_cu_ptr) { PCRE2_SIZE check_length = end_subject - start_match; if (check_length < REQ_CU_MAX || (!anchored && check_length < REQ_CU_MAX * 1000)) { if (req_cu != req_cu2) /* Caseless */ { #if PCRE2_CODE_UNIT_WIDTH != 8 while (p < end_subject) { uint32_t pp = UCHAR21INCTEST(p); if (pp == req_cu || pp == req_cu2) { p--; break; } } #else /* 8-bit code units */ PCRE2_SPTR pp = p; p = memchr(pp, req_cu, end_subject - pp); if (p == NULL) { p = memchr(pp, req_cu2, end_subject - pp); if (p == NULL) p = end_subject; } #endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ } /* The caseful case */ else { #if PCRE2_CODE_UNIT_WIDTH != 8 while (p < end_subject) { if (UCHAR21INCTEST(p) == req_cu) { p--; break; } } #else /* 8-bit code units */ p = memchr(p, req_cu, end_subject - p); if (p == NULL) p = end_subject; #endif } /* If we can't find the required code unit, break the matching loop, forcing a match failure. */ if (p >= end_subject) break; /* If we have found the required code unit, save the point where we found it, so that we don't search again next time round the loop if the start hasn't passed this code unit yet. */ req_cu_ptr = p; } } } } /* ------------ End of start of match optimizations ------------ */ /* Give no match if we have passed the bumpalong limit. */ if (start_match > bumpalong_limit) break; /* OK, now we can do the business */ mb->start_used_ptr = start_match; mb->last_used_ptr = start_match; mb->recursive = NULL; rc = internal_dfa_match( mb, /* fixed match data */ mb->start_code, /* this subexpression's code */ start_match, /* where we currently are */ start_offset, /* start offset in subject */ match_data->ovector, /* offset vector */ (uint32_t)match_data->oveccount * 2, /* actual size of same */ workspace, /* workspace vector */ (int)wscount, /* size of same */ 0, /* function recurse level */ base_recursion_workspace); /* initial workspace for recursion */ /* Anything other than "no match" means we are done, always; otherwise, carry on only if not anchored. */ if (rc != PCRE2_ERROR_NOMATCH || anchored) { if (rc == PCRE2_ERROR_PARTIAL && match_data->oveccount > 0) { match_data->ovector[0] = (PCRE2_SIZE)(start_match - subject); match_data->ovector[1] = (PCRE2_SIZE)(end_subject - subject); } match_data->leftchar = (PCRE2_SIZE)(mb->start_used_ptr - subject); match_data->rightchar = (PCRE2_SIZE)( mb->last_used_ptr - subject); match_data->startchar = (PCRE2_SIZE)(start_match - subject); match_data->rc = rc; if (rc >= 0 &&(options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { length = CU2BYTES(length + was_zero_terminated); match_data->subject = match_data->memctl.malloc(length, match_data->memctl.memory_data); if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY; memcpy((void *)match_data->subject, subject, length); match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } else { if (rc >= 0 || rc == PCRE2_ERROR_PARTIAL) match_data->subject = subject; } goto EXIT; } /* Advance to the next subject character unless we are at the end of a line and firstline is set. */ if (firstline && IS_NEWLINE(start_match)) break; start_match++; #ifdef SUPPORT_UNICODE if (utf) { ACROSSCHAR(start_match < end_subject, start_match, start_match++); } #endif if (start_match > end_subject) break; /* If we have just passed a CR and we are now at a LF, and the pattern does not contain any explicit matches for \r or \n, and the newline option is CRLF or ANY or ANYCRLF, advance the match position by one more character. */ if (UCHAR21TEST(start_match - 1) == CHAR_CR && start_match < end_subject && UCHAR21TEST(start_match) == CHAR_NL && (re->flags & PCRE2_HASCRORLF) == 0 && (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF || mb->nllen == 2)) start_match++; } /* "Bumpalong" loop */ NOMATCH_EXIT: rc = PCRE2_ERROR_NOMATCH; EXIT: while (rws->next != NULL) { RWS_anchor *next = rws->next; rws->next = next->next; mb->memctl.free(next, mb->memctl.memory_data); } return rc; } /* End of pcre2_dfa_match.c */ vfu-4.22/vstring/pcre2/pcre2_serialize.c0000644000175000017500000002350014145574056016563 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains functions for serializing and deserializing a sequence of compiled codes. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /* Magic number to provide a small check against being handed junk. */ #define SERIALIZED_DATA_MAGIC 0x50523253u /* Deserialization is limited to the current PCRE version and character width. */ #define SERIALIZED_DATA_VERSION \ ((PCRE2_MAJOR) | ((PCRE2_MINOR) << 16)) #define SERIALIZED_DATA_CONFIG \ (sizeof(PCRE2_UCHAR) | ((sizeof(void*)) << 8) | ((sizeof(PCRE2_SIZE)) << 16)) /************************************************* * Serialize compiled patterns * *************************************************/ PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION pcre2_serialize_encode(const pcre2_code **codes, int32_t number_of_codes, uint8_t **serialized_bytes, PCRE2_SIZE *serialized_size, pcre2_general_context *gcontext) { uint8_t *bytes; uint8_t *dst_bytes; int32_t i; PCRE2_SIZE total_size; const pcre2_real_code *re; const uint8_t *tables; pcre2_serialized_data *data; const pcre2_memctl *memctl = (gcontext != NULL) ? &gcontext->memctl : &PRIV(default_compile_context).memctl; if (codes == NULL || serialized_bytes == NULL || serialized_size == NULL) return PCRE2_ERROR_NULL; if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA; /* Compute total size. */ total_size = sizeof(pcre2_serialized_data) + TABLES_LENGTH; tables = NULL; for (i = 0; i < number_of_codes; i++) { if (codes[i] == NULL) return PCRE2_ERROR_NULL; re = (const pcre2_real_code *)(codes[i]); if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; if (tables == NULL) tables = re->tables; else if (tables != re->tables) return PCRE2_ERROR_MIXEDTABLES; total_size += re->blocksize; } /* Initialize the byte stream. */ bytes = memctl->malloc(total_size + sizeof(pcre2_memctl), memctl->memory_data); if (bytes == NULL) return PCRE2_ERROR_NOMEMORY; /* The controller is stored as a hidden parameter. */ memcpy(bytes, memctl, sizeof(pcre2_memctl)); bytes += sizeof(pcre2_memctl); data = (pcre2_serialized_data *)bytes; data->magic = SERIALIZED_DATA_MAGIC; data->version = SERIALIZED_DATA_VERSION; data->config = SERIALIZED_DATA_CONFIG; data->number_of_codes = number_of_codes; /* Copy all compiled code data. */ dst_bytes = bytes + sizeof(pcre2_serialized_data); memcpy(dst_bytes, tables, TABLES_LENGTH); dst_bytes += TABLES_LENGTH; for (i = 0; i < number_of_codes; i++) { re = (const pcre2_real_code *)(codes[i]); (void)memcpy(dst_bytes, (char *)re, re->blocksize); /* Certain fields in the compiled code block are re-set during deserialization. In order to ensure that the serialized data stream is always the same for the same pattern, set them to zero here. We can't assume the copy of the pattern is correctly aligned for accessing the fields as part of a structure. Note the use of sizeof(void *) in the second of these, to specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a pointer to uint8_t), gcc gives a warning because the first argument is also a pointer to uint8_t. Casting the first argument to (void *) can stop this, but it didn't stop Coverity giving the same complaint. */ (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0, sizeof(pcre2_memctl)); (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0, sizeof(void *)); (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0, sizeof(void *)); dst_bytes += re->blocksize; } *serialized_bytes = bytes; *serialized_size = total_size; return number_of_codes; } /************************************************* * Deserialize compiled patterns * *************************************************/ PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION pcre2_serialize_decode(pcre2_code **codes, int32_t number_of_codes, const uint8_t *bytes, pcre2_general_context *gcontext) { const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes; const pcre2_memctl *memctl = (gcontext != NULL) ? &gcontext->memctl : &PRIV(default_compile_context).memctl; const uint8_t *src_bytes; pcre2_real_code *dst_re; uint8_t *tables; int32_t i, j; /* Sanity checks. */ if (data == NULL || codes == NULL) return PCRE2_ERROR_NULL; if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA; if (data->number_of_codes <= 0) return PCRE2_ERROR_BADSERIALIZEDDATA; if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC; if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE; if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE; if (number_of_codes > data->number_of_codes) number_of_codes = data->number_of_codes; src_bytes = bytes + sizeof(pcre2_serialized_data); /* Decode tables. The reference count for the tables is stored immediately following them. */ tables = memctl->malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE), memctl->memory_data); if (tables == NULL) return PCRE2_ERROR_NOMEMORY; memcpy(tables, src_bytes, TABLES_LENGTH); *(PCRE2_SIZE *)(tables + TABLES_LENGTH) = number_of_codes; src_bytes += TABLES_LENGTH; /* Decode the byte stream. We must not try to read the size from the compiled code block in the stream, because it might be unaligned, which causes errors on hardware such as Sparc-64 that doesn't like unaligned memory accesses. The type of the blocksize field is given its own name to ensure that it is the same here as in the block. */ for (i = 0; i < number_of_codes; i++) { CODE_BLOCKSIZE_TYPE blocksize; memcpy(&blocksize, src_bytes + offsetof(pcre2_real_code, blocksize), sizeof(CODE_BLOCKSIZE_TYPE)); if (blocksize <= sizeof(pcre2_real_code)) return PCRE2_ERROR_BADSERIALIZEDDATA; /* The allocator provided by gcontext replaces the original one. */ dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize, (pcre2_memctl *)gcontext); if (dst_re == NULL) { memctl->free(tables, memctl->memory_data); for (j = 0; j < i; j++) { memctl->free(codes[j], memctl->memory_data); codes[j] = NULL; } return PCRE2_ERROR_NOMEMORY; } /* The new allocator must be preserved. */ memcpy(((uint8_t *)dst_re) + sizeof(pcre2_memctl), src_bytes + sizeof(pcre2_memctl), blocksize - sizeof(pcre2_memctl)); if (dst_re->magic_number != MAGIC_NUMBER || dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 || dst_re->name_count > MAX_NAME_COUNT) { memctl->free(dst_re, memctl->memory_data); return PCRE2_ERROR_BADSERIALIZEDDATA; } /* At the moment only one table is supported. */ dst_re->tables = tables; dst_re->executable_jit = NULL; dst_re->flags |= PCRE2_DEREF_TABLES; codes[i] = dst_re; src_bytes += blocksize; } return number_of_codes; } /************************************************* * Get the number of serialized patterns * *************************************************/ PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION pcre2_serialize_get_number_of_codes(const uint8_t *bytes) { const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes; if (data == NULL) return PCRE2_ERROR_NULL; if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC; if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE; if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE; return data->number_of_codes; } /************************************************* * Free the allocated stream * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_serialize_free(uint8_t *bytes) { if (bytes != NULL) { pcre2_memctl *memctl = (pcre2_memctl *)(bytes - sizeof(pcre2_memctl)); memctl->free(memctl, memctl->memory_data); } } /* End of pcre2_serialize.c */ vfu-4.22/vstring/pcre2/makefile0000644000175000017500000002154314145574056015042 0ustar cadecade ### MAKEMAKE STARTS HERE ####################################################### ### Created by makemake.pl on Fri Nov 19 01:51:13 2021 ######################### ### GLOBAL TARGETS ############################################################# default: mm_update all re: mm_update rebuild li: mm_update link all: mm_update libpcre2.a clean: mm_update clean-libpcre2.a rebuild: mm_update rebuild-libpcre2.a link: mm_update link-libpcre2.a ### GLOBAL (AND USER) DEFS ##################################################### AR = ar rv CC = gcc LD = gcc MKDIR = mkdir -p RANLIB = ranlib RMDIR = rm -rf RMFILE = rm -f SRC = *.c *.cpp *.cc *.cxx ### TARGET 1: libpcre2.a ####################################################### CC_1 = gcc LD_1 = gcc AR_1 = ar rv RANLIB_1 = ranlib CCFLAGS_1 = -DHAVE_CONFIG_H -fPIC -I. -O2 $(CCDEF) LDFLAGS_1 = $(LDDEF) DEPFLAGS_1 = ARFLAGS_1 = TARGET_1 = libpcre2.a ### SOURCES FOR TARGET 1: libpcre2.a ########################################### SRC_1= \ pcre2_auto_possess.c \ pcre2_chartables.c \ pcre2_compile.c \ pcre2_config.c \ pcre2_context.c \ pcre2_convert.c \ pcre2_dfa_match.c \ pcre2_error.c \ pcre2_extuni.c \ pcre2_find_bracket.c \ pcre2_jit_compile.c \ pcre2_maketables.c \ pcre2_match.c \ pcre2_match_data.c \ pcre2_newline.c \ pcre2_ord2utf.c \ pcre2_pattern_info.c \ pcre2_script_run.c \ pcre2_serialize.c \ pcre2_string_utils.c \ pcre2_study.c \ pcre2_substitute.c \ pcre2_substring.c \ pcre2_tables.c \ pcre2_ucd.c \ pcre2_valid_utf.c \ pcre2_xclass.c \ #### OBJECTS FOR TARGET 1: libpcre2.a ########################################## OBJ_1= \ .OBJ.libpcre2.a/pcre2_auto_possess.o \ .OBJ.libpcre2.a/pcre2_chartables.o \ .OBJ.libpcre2.a/pcre2_compile.o \ .OBJ.libpcre2.a/pcre2_config.o \ .OBJ.libpcre2.a/pcre2_context.o \ .OBJ.libpcre2.a/pcre2_convert.o \ .OBJ.libpcre2.a/pcre2_dfa_match.o \ .OBJ.libpcre2.a/pcre2_error.o \ .OBJ.libpcre2.a/pcre2_extuni.o \ .OBJ.libpcre2.a/pcre2_find_bracket.o \ .OBJ.libpcre2.a/pcre2_jit_compile.o \ .OBJ.libpcre2.a/pcre2_maketables.o \ .OBJ.libpcre2.a/pcre2_match.o \ .OBJ.libpcre2.a/pcre2_match_data.o \ .OBJ.libpcre2.a/pcre2_newline.o \ .OBJ.libpcre2.a/pcre2_ord2utf.o \ .OBJ.libpcre2.a/pcre2_pattern_info.o \ .OBJ.libpcre2.a/pcre2_script_run.o \ .OBJ.libpcre2.a/pcre2_serialize.o \ .OBJ.libpcre2.a/pcre2_string_utils.o \ .OBJ.libpcre2.a/pcre2_study.o \ .OBJ.libpcre2.a/pcre2_substitute.o \ .OBJ.libpcre2.a/pcre2_substring.o \ .OBJ.libpcre2.a/pcre2_tables.o \ .OBJ.libpcre2.a/pcre2_ucd.o \ .OBJ.libpcre2.a/pcre2_valid_utf.o \ .OBJ.libpcre2.a/pcre2_xclass.o \ ### TARGET DEFINITION FOR TARGET 1: libpcre2.a ################################# .OBJ.libpcre2.a: $(MKDIR) .OBJ.libpcre2.a libpcre2.a: .OBJ.libpcre2.a $(OBJ_1) $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) clean-libpcre2.a: $(RMFILE) $(TARGET_1) $(RMDIR) .OBJ.libpcre2.a rebuild-libpcre2.a: clean-libpcre2.a libpcre2.a re-libpcre2.a: rebuild-libpcre2.a link-libpcre2.a: .OBJ.libpcre2.a $(OBJ_1) $(RMFILE) libpcre2.a $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) ### TARGET OBJECTS FOR TARGET 1: libpcre2.a #################################### .OBJ.libpcre2.a/pcre2_auto_possess.o: pcre2_auto_possess.c pcre2_auto_possess.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_auto_possess.c -o .OBJ.libpcre2.a/pcre2_auto_possess.o .OBJ.libpcre2.a/pcre2_chartables.o: pcre2_chartables.c pcre2_chartables.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_chartables.c -o .OBJ.libpcre2.a/pcre2_chartables.o .OBJ.libpcre2.a/pcre2_compile.o: pcre2_compile.c pcre2_compile.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_compile.c -o .OBJ.libpcre2.a/pcre2_compile.o .OBJ.libpcre2.a/pcre2_config.o: pcre2_config.c pcre2_config.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_config.c -o .OBJ.libpcre2.a/pcre2_config.o .OBJ.libpcre2.a/pcre2_context.o: pcre2_context.c pcre2_context.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_context.c -o .OBJ.libpcre2.a/pcre2_context.o .OBJ.libpcre2.a/pcre2_convert.o: pcre2_convert.c pcre2_convert.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_convert.c -o .OBJ.libpcre2.a/pcre2_convert.o .OBJ.libpcre2.a/pcre2_dfa_match.o: pcre2_dfa_match.c pcre2_dfa_match.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_dfa_match.c -o .OBJ.libpcre2.a/pcre2_dfa_match.o .OBJ.libpcre2.a/pcre2_error.o: pcre2_error.c pcre2_error.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_error.c -o .OBJ.libpcre2.a/pcre2_error.o .OBJ.libpcre2.a/pcre2_extuni.o: pcre2_extuni.c pcre2_extuni.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_extuni.c -o .OBJ.libpcre2.a/pcre2_extuni.o .OBJ.libpcre2.a/pcre2_find_bracket.o: pcre2_find_bracket.c pcre2_find_bracket.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_find_bracket.c -o .OBJ.libpcre2.a/pcre2_find_bracket.o .OBJ.libpcre2.a/pcre2_jit_compile.o: pcre2_jit_compile.c pcre2_jit_compile.c pcre2_internal.h pcre2.h \ pcre2_ucp.h pcre2_jit_match.c pcre2_jit_misc.c $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_jit_compile.c -o .OBJ.libpcre2.a/pcre2_jit_compile.o .OBJ.libpcre2.a/pcre2_maketables.o: pcre2_maketables.c pcre2_maketables.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_maketables.c -o .OBJ.libpcre2.a/pcre2_maketables.o .OBJ.libpcre2.a/pcre2_match.o: pcre2_match.c pcre2_match.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_match.c -o .OBJ.libpcre2.a/pcre2_match.o .OBJ.libpcre2.a/pcre2_match_data.o: pcre2_match_data.c pcre2_match_data.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_match_data.c -o .OBJ.libpcre2.a/pcre2_match_data.o .OBJ.libpcre2.a/pcre2_newline.o: pcre2_newline.c pcre2_newline.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_newline.c -o .OBJ.libpcre2.a/pcre2_newline.o .OBJ.libpcre2.a/pcre2_ord2utf.o: pcre2_ord2utf.c pcre2_ord2utf.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_ord2utf.c -o .OBJ.libpcre2.a/pcre2_ord2utf.o .OBJ.libpcre2.a/pcre2_pattern_info.o: pcre2_pattern_info.c pcre2_pattern_info.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_pattern_info.c -o .OBJ.libpcre2.a/pcre2_pattern_info.o .OBJ.libpcre2.a/pcre2_script_run.o: pcre2_script_run.c pcre2_script_run.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_script_run.c -o .OBJ.libpcre2.a/pcre2_script_run.o .OBJ.libpcre2.a/pcre2_serialize.o: pcre2_serialize.c pcre2_serialize.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_serialize.c -o .OBJ.libpcre2.a/pcre2_serialize.o .OBJ.libpcre2.a/pcre2_string_utils.o: pcre2_string_utils.c pcre2_string_utils.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_string_utils.c -o .OBJ.libpcre2.a/pcre2_string_utils.o .OBJ.libpcre2.a/pcre2_study.o: pcre2_study.c pcre2_study.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_study.c -o .OBJ.libpcre2.a/pcre2_study.o .OBJ.libpcre2.a/pcre2_substitute.o: pcre2_substitute.c pcre2_substitute.c pcre2_internal.h pcre2.h \ pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_substitute.c -o .OBJ.libpcre2.a/pcre2_substitute.o .OBJ.libpcre2.a/pcre2_substring.o: pcre2_substring.c pcre2_substring.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_substring.c -o .OBJ.libpcre2.a/pcre2_substring.o .OBJ.libpcre2.a/pcre2_tables.o: pcre2_tables.c pcre2_tables.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_tables.c -o .OBJ.libpcre2.a/pcre2_tables.o .OBJ.libpcre2.a/pcre2_ucd.o: pcre2_ucd.c pcre2_ucd.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_ucd.c -o .OBJ.libpcre2.a/pcre2_ucd.o .OBJ.libpcre2.a/pcre2_valid_utf.o: pcre2_valid_utf.c pcre2_valid_utf.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_valid_utf.c -o .OBJ.libpcre2.a/pcre2_valid_utf.o .OBJ.libpcre2.a/pcre2_xclass.o: pcre2_xclass.c pcre2_xclass.c pcre2_internal.h pcre2.h pcre2_ucp.h $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c pcre2_xclass.c -o .OBJ.libpcre2.a/pcre2_xclass.o mm_update: ### MAKEMAKE ENDS HERE ######################################################### vfu-4.22/vstring/pcre2/pcre2_study.c0000644000175000017500000015047314145574056015756 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains functions for scanning a compiled pattern and collecting data (e.g. minimum matching length). */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /* The maximum remembered capturing brackets minimum. */ #define MAX_CACHE_BACKREF 128 /* Set a bit in the starting code unit bit map. */ #define SET_BIT(c) re->start_bitmap[(c)/8] |= (1u << ((c)&7)) /* Returns from set_start_bits() */ enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN, SSB_TOODEEP }; /************************************************* * Find the minimum subject length for a group * *************************************************/ /* Scan a parenthesized group and compute the minimum length of subject that is needed to match it. This is a lower bound; it does not mean there is a string of that length that matches. In UTF mode, the result is in characters rather than code units. The field in a compiled pattern for storing the minimum length is 16-bits long (on the grounds that anything longer than that is pathological), so we give up when we reach that amount. This also means that integer overflow for really crazy patterns cannot happen. Backreference minimum lengths are cached to speed up multiple references. This function is called only when the highest back reference in the pattern is less than or equal to MAX_CACHE_BACKREF, which is one less than the size of the caching vector. The zeroth element contains the number of the highest set value. Arguments: re compiled pattern block code pointer to start of group (the bracket) startcode pointer to start of the whole pattern's code utf UTF flag recurses chain of recurse_check to catch mutual recursion countptr pointer to call count (to catch over complexity) backref_cache vector for caching back references. This function is no longer called when the pattern contains (*ACCEPT); however, the old code for returning -1 is retained, just in case. Returns: the minimum length -1 \C in UTF-8 mode or (*ACCEPT) or pattern too complicated -2 internal error (missing capturing bracket) -3 internal error (opcode not listed) */ static int find_minlength(const pcre2_real_code *re, PCRE2_SPTR code, PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr, int *backref_cache) { int length = -1; int branchlength = 0; int prev_cap_recno = -1; int prev_cap_d = 0; int prev_recurse_recno = -1; int prev_recurse_d = 0; uint32_t once_fudge = 0; BOOL had_recurse = FALSE; BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0; PCRE2_SPTR nextbranch = code + GET(code, 1); PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE; recurse_check this_recurse; /* If this is a "could be empty" group, its minimum length is 0. */ if (*code >= OP_SBRA && *code <= OP_SCOND) return 0; /* Skip over capturing bracket number */ if (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE; /* A large and/or complex regex can take too long to process. */ if ((*countptr)++ > 1000) return -1; /* Scan along the opcodes for this branch. If we get to the end of the branch, check the length against that of the other branches. If the accumulated length passes 16-bits, reset to that value and skip the rest of the branch. */ for (;;) { int d, min, recno; PCRE2_UCHAR op, *cs, *ce; if (branchlength >= UINT16_MAX) { branchlength = UINT16_MAX; cc = (PCRE2_UCHAR *)nextbranch; } op = *cc; switch (op) { case OP_COND: case OP_SCOND: /* If there is only one branch in a condition, the implied branch has zero length, so we don't add anything. This covers the DEFINE "condition" automatically. If there are two branches we can treat it the same as any other non-capturing subpattern. */ cs = cc + GET(cc, 1); if (*cs != OP_ALT) { cc = cs + 1 + LINK_SIZE; break; } goto PROCESS_NON_CAPTURE; case OP_BRA: /* There's a special case of OP_BRA, when it is wrapped round a repeated OP_RECURSE. We'd like to process the latter at this level so that remembering the value works for repeated cases. So we do nothing, but set a fudge value to skip over the OP_KET after the recurse. */ if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET) { once_fudge = 1 + LINK_SIZE; cc += 1 + LINK_SIZE; break; } /* Fall through */ case OP_ONCE: case OP_SCRIPT_RUN: case OP_SBRA: case OP_BRAPOS: case OP_SBRAPOS: PROCESS_NON_CAPTURE: d = find_minlength(re, cc, startcode, utf, recurses, countptr, backref_cache); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); cc += 1 + LINK_SIZE; break; /* To save time for repeated capturing subpatterns, we remember the length of the previous one. Unfortunately we can't do the same for the unnumbered ones above. Nor can we do this if (?| is present in the pattern because captures with the same number are not then identical. */ case OP_CBRA: case OP_SCBRA: case OP_CBRAPOS: case OP_SCBRAPOS: recno = (int)GET2(cc, 1+LINK_SIZE); if (dupcapused || recno != prev_cap_recno) { prev_cap_recno = recno; prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr, backref_cache); if (prev_cap_d < 0) return prev_cap_d; } branchlength += prev_cap_d; do cc += GET(cc, 1); while (*cc == OP_ALT); cc += 1 + LINK_SIZE; break; /* ACCEPT makes things far too complicated; we have to give up. In fact, from 10.34 onwards, if a pattern contains (*ACCEPT), this function is not used. However, leave the code in place, just in case. */ case OP_ACCEPT: case OP_ASSERT_ACCEPT: return -1; /* Reached end of a branch; if it's a ket it is the end of a nested call. If it's ALT it is an alternation in a nested call. If it is END it's the end of the outer call. All can be handled by the same code. If the length of any branch is zero, there is no need to scan any subsequent branches. */ case OP_ALT: case OP_KET: case OP_KETRMAX: case OP_KETRMIN: case OP_KETRPOS: case OP_END: if (length < 0 || (!had_recurse && branchlength < length)) length = branchlength; if (op != OP_ALT || length == 0) return length; nextbranch = cc + GET(cc, 1); cc += 1 + LINK_SIZE; branchlength = 0; had_recurse = FALSE; break; /* Skip over assertive subpatterns */ case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: do cc += GET(cc, 1); while (*cc == OP_ALT); /* Fall through */ /* Skip over things that don't match chars */ case OP_REVERSE: case OP_CREF: case OP_DNCREF: case OP_RREF: case OP_DNRREF: case OP_FALSE: case OP_TRUE: case OP_CALLOUT: case OP_SOD: case OP_SOM: case OP_EOD: case OP_EODN: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: cc += PRIV(OP_lengths)[*cc]; break; case OP_CALLOUT_STR: cc += GET(cc, 1 + 2*LINK_SIZE); break; /* Skip over a subpattern that has a {0} or {0,x} quantifier */ case OP_BRAZERO: case OP_BRAMINZERO: case OP_BRAPOSZERO: case OP_SKIPZERO: cc += PRIV(OP_lengths)[*cc]; do cc += GET(cc, 1); while (*cc == OP_ALT); cc += 1 + LINK_SIZE; break; /* Handle literal characters and + repetitions */ case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_PLUS: case OP_PLUSI: case OP_MINPLUS: case OP_MINPLUSI: case OP_POSPLUS: case OP_POSPLUSI: case OP_NOTPLUS: case OP_NOTPLUSI: case OP_NOTMINPLUS: case OP_NOTMINPLUSI: case OP_NOTPOSPLUS: case OP_NOTPOSPLUSI: branchlength++; cc += 2; #ifdef SUPPORT_UNICODE if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEPOSPLUS: branchlength++; cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2; break; /* Handle exact repetitions. The count is already in characters, but we may need to skip over a multibyte character in UTF mode. */ case OP_EXACT: case OP_EXACTI: case OP_NOTEXACT: case OP_NOTEXACTI: branchlength += GET2(cc,1); cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; case OP_TYPEEXACT: branchlength += GET2(cc,1); cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); break; /* Handle single-char non-literal matchers */ case OP_PROP: case OP_NOTPROP: cc += 2; /* Fall through */ case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_EXTUNI: case OP_HSPACE: case OP_NOT_HSPACE: case OP_VSPACE: case OP_NOT_VSPACE: branchlength++; cc++; break; /* "Any newline" might match two characters, but it also might match just one. */ case OP_ANYNL: branchlength += 1; cc++; break; /* The single-byte matcher means we can't proceed in UTF mode. (In non-UTF mode \C will actually be turned into OP_ALLANY, so won't ever appear, but leave the code, just in case.) */ case OP_ANYBYTE: #ifdef SUPPORT_UNICODE if (utf) return -1; #endif branchlength++; cc++; break; /* For repeated character types, we have to test for \p and \P, which have an extra two bytes of parameters. */ case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSSTAR: case OP_TYPEPOSQUERY: if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2; cc += PRIV(OP_lengths)[op]; break; case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: if (cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2; cc += PRIV(OP_lengths)[op]; break; /* Check a class for variable quantification */ case OP_CLASS: case OP_NCLASS: #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: /* The original code caused an unsigned overflow in 64 bit systems, so now we use a conditional statement. */ if (op == OP_XCLASS) cc += GET(cc, 1); else cc += PRIV(OP_lengths)[OP_CLASS]; #else cc += PRIV(OP_lengths)[OP_CLASS]; #endif switch (*cc) { case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSPLUS: branchlength++; /* Fall through */ case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSQUERY: cc++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: branchlength += GET2(cc,1); cc += 1 + 2 * IMM2_SIZE; break; default: branchlength++; break; } break; /* Backreferences and subroutine calls (OP_RECURSE) are treated in the same way: we find the minimum length for the subpattern. A recursion (backreference or subroutine) causes an a flag to be set that causes the length of this branch to be ignored. The logic is that a recursion can only make sense if there is another alternative that stops the recursing. That will provide the minimum length (when no recursion happens). If PCRE2_MATCH_UNSET_BACKREF is set, a backreference to an unset bracket matches an empty string (by default it causes a matching failure), so in that case we must set the minimum length to zero. For backreferenes, if duplicate numbers are present in the pattern we check for a reference to a duplicate. If it is, we don't know which version will be referenced, so we have to set the minimum length to zero. */ /* Duplicate named pattern back reference. */ case OP_DNREF: case OP_DNREFI: if (!dupcapused && (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { int count = GET2(cc, 1+IMM2_SIZE); PCRE2_UCHAR *slot = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + GET2(cc, 1) * re->name_entry_size; d = INT_MAX; /* Scan all groups with the same name; find the shortest. */ while (count-- > 0) { int dd, i; recno = GET2(slot, 0); if (recno <= backref_cache[0] && backref_cache[recno] >= 0) dd = backref_cache[recno]; else { ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); if (cs == NULL) return -2; do ce += GET(ce, 1); while (*ce == OP_ALT); dd = 0; if (!dupcapused || (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL) { if (cc > cs && cc < ce) /* Simple recursion */ { had_recurse = TRUE; } else { recurse_check *r = recurses; for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; if (r != NULL) /* Mutual recursion */ { had_recurse = TRUE; } else { this_recurse.prev = recurses; /* No recursion */ this_recurse.group = cs; dd = find_minlength(re, cs, startcode, utf, &this_recurse, countptr, backref_cache); if (dd < 0) return dd; } } } backref_cache[recno] = dd; for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; backref_cache[0] = recno; } if (dd < d) d = dd; if (d <= 0) break; /* No point looking at any more */ slot += re->name_entry_size; } } else d = 0; cc += 1 + 2*IMM2_SIZE; goto REPEAT_BACK_REFERENCE; /* Single back reference by number. References by name are converted to by number when there is no duplication. */ case OP_REF: case OP_REFI: recno = GET2(cc, 1); if (recno <= backref_cache[0] && backref_cache[recno] >= 0) d = backref_cache[recno]; else { int i; d = 0; if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); if (cs == NULL) return -2; do ce += GET(ce, 1); while (*ce == OP_ALT); if (!dupcapused || (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL) { if (cc > cs && cc < ce) /* Simple recursion */ { had_recurse = TRUE; } else { recurse_check *r = recurses; for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; if (r != NULL) /* Mutual recursion */ { had_recurse = TRUE; } else /* No recursion */ { this_recurse.prev = recurses; this_recurse.group = cs; d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr, backref_cache); if (d < 0) return d; } } } } backref_cache[recno] = d; for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; backref_cache[0] = recno; } cc += 1 + IMM2_SIZE; /* Handle repeated back references */ REPEAT_BACK_REFERENCE: switch (*cc) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSQUERY: min = 0; cc++; break; case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSPLUS: min = 1; cc++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: min = GET2(cc, 1); cc += 1 + 2 * IMM2_SIZE; break; default: min = 1; break; } /* Take care not to overflow: (1) min and d are ints, so check that their product is not greater than INT_MAX. (2) branchlength is limited to UINT16_MAX (checked at the top of the loop). */ if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d) branchlength = UINT16_MAX; else branchlength += min * d; break; /* Recursion always refers to the first occurrence of a subpattern with a given number. Therefore, we can always make use of caching, even when the pattern contains multiple subpatterns with the same number. */ case OP_RECURSE: cs = ce = (PCRE2_UCHAR *)startcode + GET(cc, 1); recno = GET2(cs, 1+LINK_SIZE); if (recno == prev_recurse_recno) { branchlength += prev_recurse_d; } else { do ce += GET(ce, 1); while (*ce == OP_ALT); if (cc > cs && cc < ce) /* Simple recursion */ had_recurse = TRUE; else { recurse_check *r = recurses; for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; if (r != NULL) /* Mutual recursion */ had_recurse = TRUE; else { this_recurse.prev = recurses; this_recurse.group = cs; prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr, backref_cache); if (prev_recurse_d < 0) return prev_recurse_d; prev_recurse_recno = recno; branchlength += prev_recurse_d; } } } cc += 1 + LINK_SIZE + once_fudge; once_fudge = 0; break; /* Anything else does not or need not match a character. We can get the item's length from the table, but for those that can match zero occurrences of a character, we must take special action for UTF-8 characters. As it happens, the "NOT" versions of these opcodes are used at present only for ASCII characters, so they could be omitted from this list. However, in future that may change, so we include them here so as not to leave a gotcha for a future maintainer. */ case OP_UPTO: case OP_UPTOI: case OP_NOTUPTO: case OP_NOTUPTOI: case OP_MINUPTO: case OP_MINUPTOI: case OP_NOTMINUPTO: case OP_NOTMINUPTOI: case OP_POSUPTO: case OP_POSUPTOI: case OP_NOTPOSUPTO: case OP_NOTPOSUPTOI: case OP_STAR: case OP_STARI: case OP_NOTSTAR: case OP_NOTSTARI: case OP_MINSTAR: case OP_MINSTARI: case OP_NOTMINSTAR: case OP_NOTMINSTARI: case OP_POSSTAR: case OP_POSSTARI: case OP_NOTPOSSTAR: case OP_NOTPOSSTARI: case OP_QUERY: case OP_QUERYI: case OP_NOTQUERY: case OP_NOTQUERYI: case OP_MINQUERY: case OP_MINQUERYI: case OP_NOTMINQUERY: case OP_NOTMINQUERYI: case OP_POSQUERY: case OP_POSQUERYI: case OP_NOTPOSQUERY: case OP_NOTPOSQUERYI: cc += PRIV(OP_lengths)[op]; #ifdef SUPPORT_UNICODE if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; /* Skip these, but we need to add in the name length. */ case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: cc += PRIV(OP_lengths)[op] + cc[1]; break; /* The remaining opcodes are just skipped over. */ case OP_CLOSE: case OP_COMMIT: case OP_FAIL: case OP_PRUNE: case OP_SET_SOM: case OP_SKIP: case OP_THEN: cc += PRIV(OP_lengths)[op]; break; /* This should not occur: we list all opcodes explicitly so that when new ones get added they are properly considered. */ default: return -3; } } /* Control never gets here */ } /************************************************* * Set a bit and maybe its alternate case * *************************************************/ /* Given a character, set its first code unit's bit in the table, and also the corresponding bit for the other version of a letter if we are caseless. Arguments: re points to the regex block p points to the first code unit of the character caseless TRUE if caseless utf TRUE for UTF mode ucp TRUE for UCP mode Returns: pointer after the character */ static PCRE2_SPTR set_table_bit(pcre2_real_code *re, PCRE2_SPTR p, BOOL caseless, BOOL utf, BOOL ucp) { uint32_t c = *p++; /* First code unit */ (void)utf; /* Stop compiler warnings when UTF not supported */ (void)ucp; /* In 16-bit and 32-bit modes, code units greater than 0xff set the bit for 0xff. */ #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 0xff) SET_BIT(0xff); else #endif SET_BIT(c); /* In UTF-8 or UTF-16 mode, pick up the remaining code units in order to find the end of the character, even when caseless. */ #ifdef SUPPORT_UNICODE if (utf) { #if PCRE2_CODE_UNIT_WIDTH == 8 if (c >= 0xc0) GETUTF8INC(c, p); #elif PCRE2_CODE_UNIT_WIDTH == 16 if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, p); #endif } #endif /* SUPPORT_UNICODE */ /* If caseless, handle the other case of the character. */ if (caseless) { #ifdef SUPPORT_UNICODE if (utf || ucp) { c = UCD_OTHERCASE(c); #if PCRE2_CODE_UNIT_WIDTH == 8 if (utf) { PCRE2_UCHAR buff[6]; (void)PRIV(ord2utf)(c, buff); SET_BIT(buff[0]); } else if (c < 256) SET_BIT(c); #else /* 16-bit or 32-bit mode */ if (c > 0xff) SET_BIT(0xff); else SET_BIT(c); #endif } else #endif /* SUPPORT_UNICODE */ /* Not UTF or UCP */ if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]); } return p; } /************************************************* * Set bits for a positive character type * *************************************************/ /* This function sets starting bits for a character type. In UTF-8 mode, we can only do a direct setting for bytes less than 128, as otherwise there can be confusion with bytes in the middle of UTF-8 characters. In a "traditional" environment, the tables will only recognize ASCII characters anyway, but in at least one Windows environment, some higher bytes bits were set in the tables. So we deal with that case by considering the UTF-8 encoding. Arguments: re the regex block cbit type the type of character wanted table_limit 32 for non-UTF-8; 16 for UTF-8 Returns: nothing */ static void set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type]; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (table_limit == 32) return; for (c = 128; c < 256; c++) { if ((re->tables[cbits_offset + c/8] & (1u << (c&7))) != 0) { PCRE2_UCHAR buff[6]; (void)PRIV(ord2utf)(c, buff); SET_BIT(buff[0]); } } #endif /* UTF-8 */ } /************************************************* * Set bits for a negative character type * *************************************************/ /* This function sets starting bits for a negative character type such as \D. In UTF-8 mode, we can only do a direct setting for bytes less than 128, as otherwise there can be confusion with bytes in the middle of UTF-8 characters. Unlike in the positive case, where we can set appropriate starting bits for specific high-valued UTF-8 characters, in this case we have to set the bits for all high-valued characters. The lowest is 0xc2, but we overkill by starting at 0xc0 (192) for simplicity. Arguments: re the regex block cbit type the type of character wanted table_limit 32 for non-UTF-8; 16 for UTF-8 Returns: nothing */ static void set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= ~(re->tables[c+cbits_offset+cbit_type]); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (table_limit != 32) for (c = 24; c < 32; c++) re->start_bitmap[c] = 0xff; #endif } /************************************************* * Create bitmap of starting code units * *************************************************/ /* This function scans a compiled unanchored expression recursively and attempts to build a bitmap of the set of possible starting code units whose values are less than 256. In 16-bit and 32-bit mode, values above 255 all cause the 255 bit to be set. When calling set[_not]_type_bits() in UTF-8 (sic) mode we pass a value of 16 rather than 32 as the final argument. (See comments in those functions for the reason.) The SSB_CONTINUE return is useful for parenthesized groups in patterns such as (a*)b where the group provides some optional starting code units but scanning must continue at the outer level to find at least one mandatory code unit. At the outermost level, this function fails unless the result is SSB_DONE. We restrict recursion (for nested groups) to 1000 to avoid stack overflow issues. Arguments: re points to the compiled regex block code points to an expression utf TRUE if in UTF mode ucp TRUE if in UCP mode depthptr pointer to recurse depth Returns: SSB_FAIL => Failed to find any starting code units SSB_DONE => Found mandatory starting code units SSB_CONTINUE => Found optional starting code units SSB_UNKNOWN => Hit an unrecognized opcode SSB_TOODEEP => Recursion is too deep */ static int set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf, BOOL ucp, int *depthptr) { uint32_t c; int yield = SSB_DONE; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 int table_limit = utf? 16:32; #else int table_limit = 32; #endif *depthptr += 1; if (*depthptr > 1000) return SSB_TOODEEP; do { BOOL try_next = TRUE; PCRE2_SPTR tcode = code + 1 + LINK_SIZE; if (*code == OP_CBRA || *code == OP_SCBRA || *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE; while (try_next) /* Loop for items in this branch */ { int rc; uint8_t *classmap = NULL; #ifdef SUPPORT_WIDE_CHARS PCRE2_UCHAR xclassflags; #endif switch(*tcode) { /* If we reach something we don't understand, it means a new opcode has been created that hasn't been added to this function. Hopefully this problem will be discovered during testing. */ default: return SSB_UNKNOWN; /* Fail for a valid opcode that implies no starting bits. */ case OP_ACCEPT: case OP_ASSERT_ACCEPT: case OP_ALLANY: case OP_ANY: case OP_ANYBYTE: case OP_CIRCM: case OP_CLOSE: case OP_COMMIT: case OP_COMMIT_ARG: case OP_COND: case OP_CREF: case OP_FALSE: case OP_TRUE: case OP_DNCREF: case OP_DNREF: case OP_DNREFI: case OP_DNRREF: case OP_DOLL: case OP_DOLLM: case OP_END: case OP_EOD: case OP_EODN: case OP_EXTUNI: case OP_FAIL: case OP_MARK: case OP_NOT: case OP_NOTEXACT: case OP_NOTEXACTI: case OP_NOTI: case OP_NOTMINPLUS: case OP_NOTMINPLUSI: case OP_NOTMINQUERY: case OP_NOTMINQUERYI: case OP_NOTMINSTAR: case OP_NOTMINSTARI: case OP_NOTMINUPTO: case OP_NOTMINUPTOI: case OP_NOTPLUS: case OP_NOTPLUSI: case OP_NOTPOSPLUS: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERY: case OP_NOTPOSQUERYI: case OP_NOTPOSSTAR: case OP_NOTPOSSTARI: case OP_NOTPOSUPTO: case OP_NOTPOSUPTOI: case OP_NOTPROP: case OP_NOTQUERY: case OP_NOTQUERYI: case OP_NOTSTAR: case OP_NOTSTARI: case OP_NOTUPTO: case OP_NOTUPTOI: case OP_NOT_HSPACE: case OP_NOT_VSPACE: case OP_PRUNE: case OP_PRUNE_ARG: case OP_RECURSE: case OP_REF: case OP_REFI: case OP_REVERSE: case OP_RREF: case OP_SCOND: case OP_SET_SOM: case OP_SKIP: case OP_SKIP_ARG: case OP_SOD: case OP_SOM: case OP_THEN: case OP_THEN_ARG: return SSB_FAIL; /* OP_CIRC happens only at the start of an anchored branch (multiline ^ uses OP_CIRCM). Skip over it. */ case OP_CIRC: tcode += PRIV(OP_lengths)[OP_CIRC]; break; /* A "real" property test implies no starting bits, but the fake property PT_CLIST identifies a list of characters. These lists are short, as they are used for characters with more than one "other case", so there is no point in recognizing them for OP_NOTPROP. */ case OP_PROP: if (tcode[1] != PT_CLIST) return SSB_FAIL; { const uint32_t *p = PRIV(ucd_caseless_sets) + tcode[2]; while ((c = *p++) < NOTACHAR) { #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (utf) { PCRE2_UCHAR buff[6]; (void)PRIV(ord2utf)(c, buff); c = buff[0]; } #endif if (c > 0xff) SET_BIT(0xff); else SET_BIT(c); } } try_next = FALSE; break; /* We can ignore word boundary tests. */ case OP_WORD_BOUNDARY: case OP_NOT_WORD_BOUNDARY: tcode++; break; /* If we hit a bracket or a positive lookahead assertion, recurse to set bits from within the subpattern. If it can't find anything, we have to give up. If it finds some mandatory character(s), we are done for this branch. Otherwise, carry on scanning after the subpattern. */ case OP_BRA: case OP_SBRA: case OP_CBRA: case OP_SCBRA: case OP_BRAPOS: case OP_SBRAPOS: case OP_CBRAPOS: case OP_SCBRAPOS: case OP_ONCE: case OP_SCRIPT_RUN: case OP_ASSERT: case OP_ASSERT_NA: rc = set_start_bits(re, tcode, utf, ucp, depthptr); if (rc == SSB_DONE) { try_next = FALSE; } else if (rc == SSB_CONTINUE) { do tcode += GET(tcode, 1); while (*tcode == OP_ALT); tcode += 1 + LINK_SIZE; } else return rc; /* FAIL, UNKNOWN, or TOODEEP */ break; /* If we hit ALT or KET, it means we haven't found anything mandatory in this branch, though we might have found something optional. For ALT, we continue with the next alternative, but we have to arrange that the final result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET, return SSB_CONTINUE: if this is the top level, that indicates failure, but after a nested subpattern, it causes scanning to continue. */ case OP_ALT: yield = SSB_CONTINUE; try_next = FALSE; break; case OP_KET: case OP_KETRMAX: case OP_KETRMIN: case OP_KETRPOS: return SSB_CONTINUE; /* Skip over callout */ case OP_CALLOUT: tcode += PRIV(OP_lengths)[OP_CALLOUT]; break; case OP_CALLOUT_STR: tcode += GET(tcode, 1 + 2*LINK_SIZE); break; /* Skip over lookbehind and negative lookahead assertions */ case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERTBACK_NA: do tcode += GET(tcode, 1); while (*tcode == OP_ALT); tcode += 1 + LINK_SIZE; break; /* BRAZERO does the bracket, but carries on. */ case OP_BRAZERO: case OP_BRAMINZERO: case OP_BRAPOSZERO: rc = set_start_bits(re, ++tcode, utf, ucp, depthptr); if (rc == SSB_FAIL || rc == SSB_UNKNOWN || rc == SSB_TOODEEP) return rc; do tcode += GET(tcode,1); while (*tcode == OP_ALT); tcode += 1 + LINK_SIZE; break; /* SKIPZERO skips the bracket. */ case OP_SKIPZERO: tcode++; do tcode += GET(tcode,1); while (*tcode == OP_ALT); tcode += 1 + LINK_SIZE; break; /* Single-char * or ? sets the bit and tries the next item */ case OP_STAR: case OP_MINSTAR: case OP_POSSTAR: case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: tcode = set_table_bit(re, tcode + 1, FALSE, utf, ucp); break; case OP_STARI: case OP_MINSTARI: case OP_POSSTARI: case OP_QUERYI: case OP_MINQUERYI: case OP_POSQUERYI: tcode = set_table_bit(re, tcode + 1, TRUE, utf, ucp); break; /* Single-char upto sets the bit and tries the next */ case OP_UPTO: case OP_MINUPTO: case OP_POSUPTO: tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, FALSE, utf, ucp); break; case OP_UPTOI: case OP_MINUPTOI: case OP_POSUPTOI: tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, TRUE, utf, ucp); break; /* At least one single char sets the bit and stops */ case OP_EXACT: tcode += IMM2_SIZE; /* Fall through */ case OP_CHAR: case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: (void)set_table_bit(re, tcode + 1, FALSE, utf, ucp); try_next = FALSE; break; case OP_EXACTI: tcode += IMM2_SIZE; /* Fall through */ case OP_CHARI: case OP_PLUSI: case OP_MINPLUSI: case OP_POSPLUSI: (void)set_table_bit(re, tcode + 1, TRUE, utf, ucp); try_next = FALSE; break; /* Special spacing and line-terminating items. These recognize specific lists of characters. The difference between VSPACE and ANYNL is that the latter can match the two-character CRLF sequence, but that is not relevant for finding the first character, so their code here is identical. */ case OP_HSPACE: SET_BIT(CHAR_HT); SET_BIT(CHAR_SPACE); /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set the bits for 0xA0 and for code units >= 255, independently of UTF. */ #if PCRE2_CODE_UNIT_WIDTH != 8 SET_BIT(0xA0); SET_BIT(0xFF); #else /* For the 8-bit library in UTF-8 mode, set the bits for the first code units of horizontal space characters. */ #ifdef SUPPORT_UNICODE if (utf) { SET_BIT(0xC2); /* For U+00A0 */ SET_BIT(0xE1); /* For U+1680, U+180E */ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ SET_BIT(0xE3); /* For U+3000 */ } else #endif /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless the code is EBCDIC. */ { #ifndef EBCDIC SET_BIT(0xA0); #endif /* Not EBCDIC */ } #endif /* 8-bit support */ try_next = FALSE; break; case OP_ANYNL: case OP_VSPACE: SET_BIT(CHAR_LF); SET_BIT(CHAR_VT); SET_BIT(CHAR_FF); SET_BIT(CHAR_CR); /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set the bits for NEL and for code units >= 255, independently of UTF. */ #if PCRE2_CODE_UNIT_WIDTH != 8 SET_BIT(CHAR_NEL); SET_BIT(0xFF); #else /* For the 8-bit library in UTF-8 mode, set the bits for the first code units of vertical space characters. */ #ifdef SUPPORT_UNICODE if (utf) { SET_BIT(0xC2); /* For U+0085 (NEL) */ SET_BIT(0xE2); /* For U+2028, U+2029 */ } else #endif /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */ { SET_BIT(CHAR_NEL); } #endif /* 8-bit support */ try_next = FALSE; break; /* Single character types set the bits and stop. Note that if PCRE2_UCP is set, we do not see these opcodes because \d etc are converted to properties. Therefore, these apply in the case when only characters less than 256 are recognized to match the types. */ case OP_NOT_DIGIT: set_nottype_bits(re, cbit_digit, table_limit); try_next = FALSE; break; case OP_DIGIT: set_type_bits(re, cbit_digit, table_limit); try_next = FALSE; break; case OP_NOT_WHITESPACE: set_nottype_bits(re, cbit_space, table_limit); try_next = FALSE; break; case OP_WHITESPACE: set_type_bits(re, cbit_space, table_limit); try_next = FALSE; break; case OP_NOT_WORDCHAR: set_nottype_bits(re, cbit_word, table_limit); try_next = FALSE; break; case OP_WORDCHAR: set_type_bits(re, cbit_word, table_limit); try_next = FALSE; break; /* One or more character type fudges the pointer and restarts, knowing it will hit a single character type and stop there. */ case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEPOSPLUS: tcode++; break; case OP_TYPEEXACT: tcode += 1 + IMM2_SIZE; break; /* Zero or more repeats of character types set the bits and then try again. */ case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEPOSUPTO: tcode += IMM2_SIZE; /* Fall through */ case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPOSSTAR: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSQUERY: switch(tcode[1]) { default: case OP_ANY: case OP_ALLANY: return SSB_FAIL; case OP_HSPACE: SET_BIT(CHAR_HT); SET_BIT(CHAR_SPACE); /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set the bits for 0xA0 and for code units >= 255, independently of UTF. */ #if PCRE2_CODE_UNIT_WIDTH != 8 SET_BIT(0xA0); SET_BIT(0xFF); #else /* For the 8-bit library in UTF-8 mode, set the bits for the first code units of horizontal space characters. */ #ifdef SUPPORT_UNICODE if (utf) { SET_BIT(0xC2); /* For U+00A0 */ SET_BIT(0xE1); /* For U+1680, U+180E */ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ SET_BIT(0xE3); /* For U+3000 */ } else #endif /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless the code is EBCDIC. */ { #ifndef EBCDIC SET_BIT(0xA0); #endif /* Not EBCDIC */ } #endif /* 8-bit support */ break; case OP_ANYNL: case OP_VSPACE: SET_BIT(CHAR_LF); SET_BIT(CHAR_VT); SET_BIT(CHAR_FF); SET_BIT(CHAR_CR); /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set the bits for NEL and for code units >= 255, independently of UTF. */ #if PCRE2_CODE_UNIT_WIDTH != 8 SET_BIT(CHAR_NEL); SET_BIT(0xFF); #else /* For the 8-bit library in UTF-8 mode, set the bits for the first code units of vertical space characters. */ #ifdef SUPPORT_UNICODE if (utf) { SET_BIT(0xC2); /* For U+0085 (NEL) */ SET_BIT(0xE2); /* For U+2028, U+2029 */ } else #endif /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */ { SET_BIT(CHAR_NEL); } #endif /* 8-bit support */ break; case OP_NOT_DIGIT: set_nottype_bits(re, cbit_digit, table_limit); break; case OP_DIGIT: set_type_bits(re, cbit_digit, table_limit); break; case OP_NOT_WHITESPACE: set_nottype_bits(re, cbit_space, table_limit); break; case OP_WHITESPACE: set_type_bits(re, cbit_space, table_limit); break; case OP_NOT_WORDCHAR: set_nottype_bits(re, cbit_word, table_limit); break; case OP_WORDCHAR: set_type_bits(re, cbit_word, table_limit); break; } tcode += 2; break; /* Extended class: if there are any property checks, or if this is a negative XCLASS without a map, give up. If there are no property checks, there must be wide characters on the XCLASS list, because otherwise an XCLASS would not have been created. This means that code points >= 255 are potential starters. In the UTF-8 case we can scan them and set bits for the relevant leading bytes. */ #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: xclassflags = tcode[1 + LINK_SIZE]; if ((xclassflags & XCL_HASPROP) != 0 || (xclassflags & (XCL_MAP|XCL_NOT)) == XCL_NOT) return SSB_FAIL; /* We have a positive XCLASS or a negative one without a map. Set up the map pointer if there is one, and fall through. */ classmap = ((xclassflags & XCL_MAP) == 0)? NULL : (uint8_t *)(tcode + 1 + LINK_SIZE + 1); /* In UTF-8 mode, scan the character list and set bits for leading bytes, then jump to handle the map. */ #if PCRE2_CODE_UNIT_WIDTH == 8 if (utf && (xclassflags & XCL_NOT) == 0) { PCRE2_UCHAR b, e; PCRE2_SPTR p = tcode + 1 + LINK_SIZE + 1 + ((classmap == NULL)? 0:32); tcode += GET(tcode, 1); for (;;) switch (*p++) { case XCL_SINGLE: b = *p++; while ((*p & 0xc0) == 0x80) p++; re->start_bitmap[b/8] |= (1u << (b&7)); break; case XCL_RANGE: b = *p++; while ((*p & 0xc0) == 0x80) p++; e = *p++; while ((*p & 0xc0) == 0x80) p++; for (; b <= e; b++) re->start_bitmap[b/8] |= (1u << (b&7)); break; case XCL_END: goto HANDLE_CLASSMAP; default: return SSB_UNKNOWN; /* Internal error, should not occur */ } } #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */ #endif /* SUPPORT_WIDE_CHARS */ /* It seems that the fall through comment must be outside the #ifdef if it is to avoid the gcc compiler warning. */ /* Fall through */ /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter because it starts a character with a value > 255. In 8-bit non-UTF mode, there is no difference between CLASS and NCLASS. In all other wide character modes, set the 0xFF bit to indicate code units >= 255. */ case OP_NCLASS: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (utf) { re->start_bitmap[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */ memset(re->start_bitmap+25, 0xff, 7); /* Bits for 0xc9 - 0xff */ } #elif PCRE2_CODE_UNIT_WIDTH != 8 SET_BIT(0xFF); /* For characters >= 255 */ #endif /* Fall through */ /* Enter here for a positive non-XCLASS. If we have fallen through from an XCLASS, classmap will already be set; just advance the code pointer. Otherwise, set up classmap for a a non-XCLASS and advance past it. */ case OP_CLASS: if (*tcode == OP_XCLASS) tcode += GET(tcode, 1); else { classmap = (uint8_t *)(++tcode); tcode += 32 / sizeof(PCRE2_UCHAR); } /* When wide characters are supported, classmap may be NULL. In UTF-8 (sic) mode, the bits in a class bit map correspond to character values, not to byte values. However, the bit map we are constructing is for byte values. So we have to do a conversion for characters whose code point is greater than 127. In fact, there are only two possible starting bytes for characters in the range 128 - 255. */ #if defined SUPPORT_WIDE_CHARS && PCRE2_CODE_UNIT_WIDTH == 8 HANDLE_CLASSMAP: #endif if (classmap != NULL) { #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (utf) { for (c = 0; c < 16; c++) re->start_bitmap[c] |= classmap[c]; for (c = 128; c < 256; c++) { if ((classmap[c/8] & (1u << (c&7))) != 0) { int d = (c >> 6) | 0xc0; /* Set bit for this starter */ re->start_bitmap[d/8] |= (1u << (d&7)); /* and then skip on to the */ c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */ } } } else #endif /* In all modes except UTF-8, the two bit maps are compatible. */ { for (c = 0; c < 32; c++) re->start_bitmap[c] |= classmap[c]; } } /* Act on what follows the class. For a zero minimum repeat, continue; otherwise stop processing. */ switch (*tcode) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSQUERY: tcode++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE; else try_next = FALSE; break; default: try_next = FALSE; break; } break; /* End of class handling case */ } /* End of switch for opcodes */ } /* End of try_next loop */ code += GET(code, 1); /* Advance to next branch */ } while (*code == OP_ALT); return yield; } /************************************************* * Study a compiled expression * *************************************************/ /* This function is handed a compiled expression that it must study to produce information that will speed up the matching. Argument: re points to the compiled expression Returns: 0 normally; non-zero should never normally occur 1 unknown opcode in set_start_bits 2 missing capturing bracket 3 unknown opcode in find_minlength */ int PRIV(study)(pcre2_real_code *re) { int count = 0; PCRE2_UCHAR *code; BOOL utf = (re->overall_options & PCRE2_UTF) != 0; BOOL ucp = (re->overall_options & PCRE2_UCP) != 0; /* Find start of compiled code */ code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_entry_size * re->name_count; /* For a pattern that has a first code unit, or a multiline pattern that matches only at "line start", there is no point in seeking a list of starting code units. */ if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0) { int depth = 0; int rc = set_start_bits(re, code, utf, ucp, &depth); if (rc == SSB_UNKNOWN) return 1; /* If a list of starting code units was set up, scan the list to see if only one or two were listed. Having only one listed is rare because usually a single starting code unit will have been recognized and PCRE2_FIRSTSET set. If two are listed, see if they are caseless versions of the same character; if so we can replace the list with a caseless first code unit. This gives better performance and is plausibly worth doing for patterns such as [Ww]ord or (word|WORD). */ if (rc == SSB_DONE) { int i; int a = -1; int b = -1; uint8_t *p = re->start_bitmap; uint32_t flags = PCRE2_FIRSTMAPSET; for (i = 0; i < 256; p++, i += 8) { uint8_t x = *p; if (x != 0) { int c; uint8_t y = x & (~x + 1); /* Least significant bit */ if (y != x) goto DONE; /* More than one bit set */ /* In the 16-bit and 32-bit libraries, the bit for 0xff means "0xff and all wide characters", so we cannot use it here. */ #if PCRE2_CODE_UNIT_WIDTH != 8 if (i == 248 && x == 0x80) goto DONE; #endif /* Compute the character value */ c = i; switch (x) { case 1: break; case 2: c += 1; break; case 4: c += 2; break; case 8: c += 3; break; case 16: c += 4; break; case 32: c += 5; break; case 64: c += 6; break; case 128: c += 7; break; } /* c contains the code unit value, in the range 0-255. In 8-bit UTF mode, only values < 128 can be used. In all the other cases, c is a character value. */ #if PCRE2_CODE_UNIT_WIDTH == 8 if (utf && c > 127) goto DONE; #endif if (a < 0) a = c; /* First one found, save in a */ else if (b < 0) /* Second one found */ { int d = TABLE_GET((unsigned int)c, re->tables + fcc_offset, c); #ifdef SUPPORT_UNICODE if (utf || ucp) { if (UCD_CASESET(c) != 0) goto DONE; /* Multiple case set */ if (c > 127) d = UCD_OTHERCASE(c); } #endif /* SUPPORT_UNICODE */ if (d != a) goto DONE; /* Not the other case of a */ b = c; /* Save second in b */ } else goto DONE; /* More than two characters found */ } } /* Replace the start code unit bits with a first code unit, but only if it is not the same as a required later code unit. This is because a search for a required code unit starts after an explicit first code unit, but at a code unit found from the bitmap. Patterns such as /a*a/ don't work if both the start unit and required unit are the same. */ if (a >= 0 && ( (re->flags & PCRE2_LASTSET) == 0 || ( re->last_codeunit != (uint32_t)a && (b < 0 || re->last_codeunit != (uint32_t)b) ) )) { re->first_codeunit = a; flags = PCRE2_FIRSTSET; if (b >= 0) flags |= PCRE2_FIRSTCASELESS; } DONE: re->flags |= flags; } } /* Find the minimum length of subject string. If the pattern can match an empty string, the minimum length is already known. If the pattern contains (*ACCEPT) all bets are off, and we don't even try to find a minimum length. If there are more back references than the size of the vector we are going to cache them in, do nothing. A pattern that complicated will probably take a long time to analyze and may in any case turn out to be too complicated. Note that back reference minima are held as 16-bit numbers. */ if ((re->flags & (PCRE2_MATCH_EMPTY|PCRE2_HASACCEPT)) == 0 && re->top_backref <= MAX_CACHE_BACKREF) { int min; int backref_cache[MAX_CACHE_BACKREF+1]; backref_cache[0] = 0; /* Highest one that is set */ min = find_minlength(re, code, code, utf, NULL, &count, backref_cache); switch(min) { case -1: /* \C in UTF mode or over-complex regex */ break; /* Leave minlength unchanged (will be zero) */ case -2: return 2; /* missing capturing bracket */ case -3: return 3; /* unrecognized opcode */ default: re->minlength = (min > UINT16_MAX)? UINT16_MAX : min; break; } } return 0; } /* End of pcre2_study.c */ vfu-4.22/vstring/pcre2/pcre2_jit_match.c0000644000175000017500000001440714145574056016544 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE #error This file must be included from pcre2_jit_compile.c. #endif #ifdef SUPPORT_JIT static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_function executable_func) { sljit_u8 local_space[MACHINE_STACK_SIZE]; struct sljit_stack local_stack; local_stack.min_start = local_space; local_stack.start = local_space; local_stack.end = local_space + MACHINE_STACK_SIZE; local_stack.top = local_space + MACHINE_STACK_SIZE; arguments->stack = &local_stack; return executable_func(arguments); } #endif /************************************************* * Do a JIT pattern match * *************************************************/ /* This function runs a JIT pattern match. Arguments: code points to the compiled expression subject points to the subject string length length of subject string (may contain binary zeros) start_offset where to start in the subject string options option bits match_data points to a match_data block mcontext points to a match context Returns: > 0 => success; value is the number of ovector pairs filled = 0 => success, but ovector is not big enough -1 => failed to match (PCRE_ERROR_NOMATCH) < -1 => some kind of unexpected problem */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_jit_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext) { #ifndef SUPPORT_JIT (void)code; (void)subject; (void)length; (void)start_offset; (void)options; (void)match_data; (void)mcontext; return PCRE2_ERROR_JIT_BADOPTION; #else /* SUPPORT_JIT */ pcre2_real_code *re = (pcre2_real_code *)code; executable_functions *functions = (executable_functions *)re->executable_jit; pcre2_jit_stack *jit_stack; uint32_t oveccount = match_data->oveccount; uint32_t max_oveccount; union { void *executable_func; jit_function call_executable_func; } convert_executable_func; jit_arguments arguments; int rc; int index = 0; if ((options & PCRE2_PARTIAL_HARD) != 0) index = 2; else if ((options & PCRE2_PARTIAL_SOFT) != 0) index = 1; if (functions == NULL || functions->executable_funcs[index] == NULL) return PCRE2_ERROR_JIT_BADOPTION; /* Sanity checks should be handled by pcre_exec. */ arguments.str = subject + start_offset; arguments.begin = subject; arguments.end = subject + length; arguments.match_data = match_data; arguments.startchar_ptr = subject; arguments.mark_ptr = NULL; arguments.options = options; if (mcontext != NULL) { arguments.callout = mcontext->callout; arguments.callout_data = mcontext->callout_data; arguments.offset_limit = mcontext->offset_limit; arguments.limit_match = (mcontext->match_limit < re->limit_match)? mcontext->match_limit : re->limit_match; if (mcontext->jit_callback != NULL) jit_stack = mcontext->jit_callback(mcontext->jit_callback_data); else jit_stack = (pcre2_jit_stack *)mcontext->jit_callback_data; } else { arguments.callout = NULL; arguments.callout_data = NULL; arguments.offset_limit = PCRE2_UNSET; arguments.limit_match = (MATCH_LIMIT < re->limit_match)? MATCH_LIMIT : re->limit_match; jit_stack = NULL; } max_oveccount = functions->top_bracket; if (oveccount > max_oveccount) oveccount = max_oveccount; arguments.oveccount = oveccount << 1; convert_executable_func.executable_func = functions->executable_funcs[index]; if (jit_stack != NULL) { arguments.stack = (struct sljit_stack *)(jit_stack->stack); rc = convert_executable_func.call_executable_func(&arguments); } else rc = jit_machine_stack_exec(&arguments, convert_executable_func.call_executable_func); if (rc > (int)oveccount) rc = 0; match_data->code = re; match_data->subject = (rc >= 0 || rc == PCRE2_ERROR_PARTIAL)? subject : NULL; match_data->rc = rc; match_data->startchar = arguments.startchar_ptr - subject; match_data->leftchar = 0; match_data->rightchar = 0; match_data->mark = arguments.mark_ptr; match_data->matchedby = PCRE2_MATCHEDBY_JIT; return match_data->rc; #endif /* SUPPORT_JIT */ } /* End of pcre2_jit_match.c */ vfu-4.22/vstring/pcre2/pcre2_xclass.c0000644000175000017500000002036714145574056016101 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2019 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains an internal function that is used to match an extended class. It is used by pcre2_auto_possessify() and by both pcre2_match() and pcre2_def_match(). */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Match character against an XCLASS * *************************************************/ /* This function is called to match a character against an extended class that might contain codepoints above 255 and/or Unicode properties. Arguments: c the character data points to the flag code unit of the XCLASS data utf TRUE if in UTF mode Returns: TRUE if character matches, else FALSE */ BOOL PRIV(xclass)(uint32_t c, PCRE2_SPTR data, BOOL utf) { PCRE2_UCHAR t; BOOL negated = (*data & XCL_NOT) != 0; #if PCRE2_CODE_UNIT_WIDTH == 8 /* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */ utf = TRUE; #endif /* Code points < 256 are matched against a bitmap, if one is present. If not, we still carry on, because there may be ranges that start below 256 in the additional data. */ if (c < 256) { if ((*data & XCL_HASPROP) == 0) { if ((*data & XCL_MAP) == 0) return negated; return (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0; } if ((*data & XCL_MAP) != 0 && (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0) return !negated; /* char found */ } /* First skip the bit map if present. Then match against the list of Unicode properties or large chars or ranges that end with a large char. We won't ever encounter XCL_PROP or XCL_NOTPROP when UTF support is not compiled. */ if ((*data++ & XCL_MAP) != 0) data += 32 / sizeof(PCRE2_UCHAR); while ((t = *data++) != XCL_END) { uint32_t x, y; if (t == XCL_SINGLE) { #ifdef SUPPORT_UNICODE if (utf) { GETCHARINC(x, data); /* macro generates multiple statements */ } else #endif x = *data++; if (c == x) return !negated; } else if (t == XCL_RANGE) { #ifdef SUPPORT_UNICODE if (utf) { GETCHARINC(x, data); /* macro generates multiple statements */ GETCHARINC(y, data); /* macro generates multiple statements */ } else #endif { x = *data++; y = *data++; } if (c >= x && c <= y) return !negated; } #ifdef SUPPORT_UNICODE else /* XCL_PROP & XCL_NOTPROP */ { const ucd_record *prop = GET_UCD(c); BOOL isprop = t == XCL_PROP; switch(*data) { case PT_ANY: if (isprop) return !negated; break; case PT_LAMP: if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt) == isprop) return !negated; break; case PT_GC: if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == isprop) return !negated; break; case PT_PC: if ((data[1] == prop->chartype) == isprop) return !negated; break; case PT_SC: if ((data[1] == prop->script) == isprop) return !negated; break; case PT_ALNUM: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N) == isprop) return !negated; break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(c) { HSPACE_CASES: VSPACE_CASES: if (isprop) return !negated; break; default: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == isprop) return !negated; break; } break; case PT_WORD: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE) == isprop) return !negated; break; case PT_UCNC: if (c < 0xa0) { if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || c == CHAR_GRAVE_ACCENT) == isprop) return !negated; } else { if ((c < 0xd800 || c > 0xdfff) == isprop) return !negated; } break; /* The following three properties can occur only in an XCLASS, as there is no \p or \P coding for them. */ /* Graphic character. Implement this as not Z (space or separator) and not C (other), except for Cf (format) with a few exceptions. This seems to be what Perl does. The exceptional characters are: U+061C Arabic Letter Mark U+180E Mongolian Vowel Separator U+2066 - U+2069 Various "isolate"s */ case PT_PXGRAPH: if ((PRIV(ucp_gentype)[prop->chartype] != ucp_Z && (PRIV(ucp_gentype)[prop->chartype] != ucp_C || (prop->chartype == ucp_Cf && c != 0x061c && c != 0x180e && (c < 0x2066 || c > 0x2069)) )) == isprop) return !negated; break; /* Printable character: same as graphic, with the addition of Zs, i.e. not Zl and not Zp, and U+180E. */ case PT_PXPRINT: if ((prop->chartype != ucp_Zl && prop->chartype != ucp_Zp && (PRIV(ucp_gentype)[prop->chartype] != ucp_C || (prop->chartype == ucp_Cf && c != 0x061c && (c < 0x2066 || c > 0x2069)) )) == isprop) return !negated; break; /* Punctuation: all Unicode punctuation, plus ASCII characters that Unicode treats as symbols rather than punctuation, for Perl compatibility (these are $+<=>^`|~). */ case PT_PXPUNCT: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_P || (c < 128 && PRIV(ucp_gentype)[prop->chartype] == ucp_S)) == isprop) return !negated; break; /* This should never occur, but compilers may mutter if there is no default. */ default: return FALSE; } data += 2; } #else (void)utf; /* Avoid compiler warning */ #endif /* SUPPORT_UNICODE */ } return negated; /* char did not match */ } /* End of pcre2_xclass.c */ vfu-4.22/vstring/pcre2/pcre2_ucd.c0000644000175000017500000103416014145574056015354 0ustar cadecade/* This module is generated by the maint/MultiStage2.py script. Do not modify it by hand. Instead modify the script and run it to regenerate this code. As well as being part of the PCRE2 library, this module is #included by the pcre2test program, which redefines the PRIV macro to change table names from _pcre2_xxx to xxxx, thereby avoiding name clashes with the library. At present, just one of these tables is actually needed. */ #ifndef PCRE2_PCRE2TEST #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #endif /* PCRE2_PCRE2TEST */ /* Unicode character database. */ /* This file was autogenerated by the MultiStage2.py script. */ /* Total size: 102844 bytes, block size: 128. */ /* The tables herein are needed only when UCP support is built, and in PCRE2 that happens automatically with UTF support. This module should not be referenced otherwise, so it should not matter whether it is compiled or not. However a comment was received about space saving - maybe the guy linked all the modules rather than using a library - so we include a condition to cut out the tables when not needed. But don't leave a totally empty module because some compilers barf at that. Instead, just supply some small dummy tables. */ #ifndef SUPPORT_UNICODE const ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0,0,0 }}; const uint16_t PRIV(ucd_stage1)[] = {0}; const uint16_t PRIV(ucd_stage2)[] = {0}; const uint32_t PRIV(ucd_caseless_sets)[] = {0}; #else const char *PRIV(unicode_version) = "14.0.0"; /* If the 32-bit library is run in non-32-bit mode, character values greater than 0x10ffff may be encountered. For these we set up a special record. */ #if PCRE2_CODE_UNIT_WIDTH == 32 const ucd_record PRIV(dummy_ucd_record)[] = {{ ucp_Unknown, /* script */ ucp_Cn, /* type unassigned */ ucp_gbOther, /* grapheme break property */ 0, /* case set */ 0, /* other case */ ucp_Unknown, /* script extension */ 0, /* dummy filler */ }}; #endif /* When recompiling tables with a new Unicode version, please check the types in this structure definition from pcre2_internal.h (the actual field names will be different): typedef struct { uint8_t property_0; uint8_t property_1; uint8_t property_2; uint8_t property_3; pcre_int32 property_4; pcre_int16 property_5; uint16_t property_6; } ucd_record; */ /* This table contains lists of characters that are caseless sets of more than one character. Each list is terminated by NOTACHAR. */ const uint32_t PRIV(ucd_caseless_sets)[] = { NOTACHAR, 0x0053, 0x0073, 0x017f, NOTACHAR, 0x01c4, 0x01c5, 0x01c6, NOTACHAR, 0x01c7, 0x01c8, 0x01c9, NOTACHAR, 0x01ca, 0x01cb, 0x01cc, NOTACHAR, 0x01f1, 0x01f2, 0x01f3, NOTACHAR, 0x0345, 0x0399, 0x03b9, 0x1fbe, NOTACHAR, 0x00b5, 0x039c, 0x03bc, NOTACHAR, 0x03a3, 0x03c2, 0x03c3, NOTACHAR, 0x0392, 0x03b2, 0x03d0, NOTACHAR, 0x0398, 0x03b8, 0x03d1, 0x03f4, NOTACHAR, 0x03a6, 0x03c6, 0x03d5, NOTACHAR, 0x03a0, 0x03c0, 0x03d6, NOTACHAR, 0x039a, 0x03ba, 0x03f0, NOTACHAR, 0x03a1, 0x03c1, 0x03f1, NOTACHAR, 0x0395, 0x03b5, 0x03f5, NOTACHAR, 0x0412, 0x0432, 0x1c80, NOTACHAR, 0x0414, 0x0434, 0x1c81, NOTACHAR, 0x041e, 0x043e, 0x1c82, NOTACHAR, 0x0421, 0x0441, 0x1c83, NOTACHAR, 0x0422, 0x0442, 0x1c84, 0x1c85, NOTACHAR, 0x042a, 0x044a, 0x1c86, NOTACHAR, 0x0462, 0x0463, 0x1c87, NOTACHAR, 0x1e60, 0x1e61, 0x1e9b, NOTACHAR, 0x03a9, 0x03c9, 0x2126, NOTACHAR, 0x004b, 0x006b, 0x212a, NOTACHAR, 0x00c5, 0x00e5, 0x212b, NOTACHAR, 0x1c88, 0xa64a, 0xa64b, NOTACHAR, }; /* When #included in pcre2test, we don't need the table of digit sets, nor the the large main UCD tables. */ #ifndef PCRE2_PCRE2TEST /* This table lists the code points for the '9' characters in each set of decimal digits. It is used to ensure that all the digits in a script run come from the same set. */ const uint32_t PRIV(ucd_digit_sets)[] = { 66, /* Number of subsequent values */ 0x00039, 0x00669, 0x006f9, 0x007c9, 0x0096f, 0x009ef, 0x00a6f, 0x00aef, 0x00b6f, 0x00bef, 0x00c6f, 0x00cef, 0x00d6f, 0x00def, 0x00e59, 0x00ed9, 0x00f29, 0x01049, 0x01099, 0x017e9, 0x01819, 0x0194f, 0x019d9, 0x01a89, 0x01a99, 0x01b59, 0x01bb9, 0x01c49, 0x01c59, 0x0a629, 0x0a8d9, 0x0a909, 0x0a9d9, 0x0a9f9, 0x0aa59, 0x0abf9, 0x0ff19, 0x104a9, 0x10d39, 0x1106f, 0x110f9, 0x1113f, 0x111d9, 0x112f9, 0x11459, 0x114d9, 0x11659, 0x116c9, 0x11739, 0x118e9, 0x11959, 0x11c59, 0x11d59, 0x11da9, 0x16a69, 0x16ac9, 0x16b59, 0x1d7d7, 0x1d7e1, 0x1d7eb, 0x1d7f5, 0x1d7ff, 0x1e149, 0x1e2f9, 0x1e959, 0x1fbf9, }; /* This vector is a list of lists of scripts for the Script Extension property. Each sublist is zero-terminated. */ const uint8_t PRIV(ucd_script_sets)[] = { /* 0 */ 0, /* 1 */ 1, 11, 0, /* 4 */ 1, 144, 0, /* 7 */ 1, 64, 0, /* 10 */ 1, 50, 0, /* 13 */ 1, 56, 0, /* 16 */ 3, 15, 0, /* 19 */ 4, 23, 0, /* 22 */ 6, 84, 0, /* 25 */ 12, 36, 0, /* 28 */ 13, 18, 0, /* 31 */ 13, 34, 0, /* 34 */ 13, 118, 0, /* 37 */ 13, 50, 0, /* 40 */ 15, 107, 0, /* 43 */ 15, 150, 0, /* 46 */ 15, 100, 0, /* 49 */ 15, 54, 0, /* 52 */ 17, 34, 0, /* 55 */ 107, 54, 0, /* 58 */ 21, 108, 0, /* 61 */ 22, 129, 0, /* 64 */ 23, 34, 0, /* 67 */ 27, 30, 0, /* 70 */ 29, 150, 0, /* 73 */ 34, 38, 0, /* 76 */ 112, 158, 0, /* 79 */ 38, 65, 0, /* 82 */ 1, 50, 56, 0, /* 86 */ 1, 56, 156, 0, /* 90 */ 3, 96, 49, 0, /* 94 */ 96, 39, 53, 0, /* 98 */ 157, 12, 36, 0, /* 102 */ 12, 110, 36, 0, /* 106 */ 15, 107, 29, 0, /* 110 */ 15, 107, 34, 0, /* 114 */ 23, 27, 30, 0, /* 118 */ 69, 34, 39, 0, /* 122 */ 3, 15, 107, 29, 0, /* 127 */ 7, 25, 52, 51, 0, /* 132 */ 15, 142, 85, 111, 0, /* 137 */ 4, 24, 23, 27, 30, 0, /* 143 */ 1, 64, 144, 50, 56, 156, 0, /* 150 */ 4, 24, 23, 27, 30, 61, 0, /* 157 */ 15, 29, 37, 44, 54, 55, 0, /* 164 */ 132, 1, 64, 144, 50, 56, 156, 0, /* 172 */ 3, 15, 107, 29, 150, 44, 55, 124, 0, /* 181 */ 132, 1, 95, 112, 158, 121, 144, 148, 50, 0, /* 191 */ 15, 142, 21, 22, 108, 85, 111, 114, 109, 102, 124, 0, /* 203 */ 3, 15, 107, 21, 22, 29, 34, 37, 44, 54, 55, 124, 0, /* 216 */ 3, 15, 107, 21, 22, 29, 34, 37, 44, 100, 54, 55, 124, 0, /* 230 */ 15, 142, 21, 22, 108, 29, 85, 111, 114, 150, 109, 102, 124, 0, /* 244 */ 15, 142, 21, 22, 108, 29, 85, 111, 37, 114, 150, 109, 102, 124, 0, /* 259 */ 3, 15, 142, 143, 138, 107, 21, 22, 29, 111, 37, 150, 44, 109, 48, 49, 102, 54, 55, 124, 0, /* 280 */ 3, 15, 142, 143, 138, 107, 21, 22, 29, 35, 111, 37, 150, 44, 109, 48, 49, 102, 54, 55, 124, 0, /* 302 */ }; /* These are the main two-stage UCD tables. The fields in each record are: script (8 bits), character type (8 bits), grapheme break property (8 bits), offset to multichar other cases or zero (8 bits), offset to other case or zero (32 bits, signed), script extension (16 bits, signed), and a dummy 16-bit field to make the whole thing a multiple of 4 bytes. */ const ucd_record PRIV(ucd_records)[] = { /* 11964 bytes, record size 12 */ { 10, 0, 2, 0, 0, 10, 256, }, /* 0 */ { 10, 0, 2, 0, 0, 10, 0, }, /* 1 */ { 10, 0, 1, 0, 0, 10, 0, }, /* 2 */ { 10, 0, 0, 0, 0, 10, 0, }, /* 3 */ { 10, 29, 12, 0, 0, 10, 0, }, /* 4 */ { 10, 21, 12, 0, 0, 10, 0, }, /* 5 */ { 10, 23, 12, 0, 0, 10, 0, }, /* 6 */ { 10, 22, 12, 0, 0, 10, 0, }, /* 7 */ { 10, 18, 12, 0, 0, 10, 0, }, /* 8 */ { 10, 25, 12, 0, 0, 10, 0, }, /* 9 */ { 10, 17, 12, 0, 0, 10, 0, }, /* 10 */ { 10, 13, 12, 0, 0, 10, 0, }, /* 11 */ { 34, 9, 12, 0, 32, 34, 0, }, /* 12 */ { 34, 9, 12, 100, 32, 34, 0, }, /* 13 */ { 34, 9, 12, 1, 32, 34, 0, }, /* 14 */ { 10, 24, 12, 0, 0, 10, 0, }, /* 15 */ { 10, 16, 12, 0, 0, 10, 0, }, /* 16 */ { 34, 5, 12, 0, -32, 34, 0, }, /* 17 */ { 34, 5, 12, 100, -32, 34, 0, }, /* 18 */ { 34, 5, 12, 1, -32, 34, 0, }, /* 19 */ { 10, 26, 12, 0, 0, 10, 0, }, /* 20 */ { 10, 26, 14, 0, 0, 10, 0, }, /* 21 */ { 34, 7, 12, 0, 0, 34, 0, }, /* 22 */ { 10, 20, 12, 0, 0, 10, 0, }, /* 23 */ { 10, 1, 2, 0, 0, 10, 0, }, /* 24 */ { 10, 15, 12, 0, 0, 10, 0, }, /* 25 */ { 10, 5, 12, 26, 775, 10, 0, }, /* 26 */ { 10, 19, 12, 0, 0, 10, 0, }, /* 27 */ { 34, 9, 12, 104, 32, 34, 0, }, /* 28 */ { 34, 5, 12, 0, 7615, 34, 0, }, /* 29 */ { 34, 5, 12, 104, -32, 34, 0, }, /* 30 */ { 34, 5, 12, 0, 121, 34, 0, }, /* 31 */ { 34, 9, 12, 0, 1, 34, 0, }, /* 32 */ { 34, 5, 12, 0, -1, 34, 0, }, /* 33 */ { 34, 9, 12, 0, 0, 34, 0, }, /* 34 */ { 34, 5, 12, 0, 0, 34, 0, }, /* 35 */ { 34, 9, 12, 0, -121, 34, 0, }, /* 36 */ { 34, 5, 12, 1, -268, 34, 0, }, /* 37 */ { 34, 5, 12, 0, 195, 34, 0, }, /* 38 */ { 34, 9, 12, 0, 210, 34, 0, }, /* 39 */ { 34, 9, 12, 0, 206, 34, 0, }, /* 40 */ { 34, 9, 12, 0, 205, 34, 0, }, /* 41 */ { 34, 9, 12, 0, 79, 34, 0, }, /* 42 */ { 34, 9, 12, 0, 202, 34, 0, }, /* 43 */ { 34, 9, 12, 0, 203, 34, 0, }, /* 44 */ { 34, 9, 12, 0, 207, 34, 0, }, /* 45 */ { 34, 5, 12, 0, 97, 34, 0, }, /* 46 */ { 34, 9, 12, 0, 211, 34, 0, }, /* 47 */ { 34, 9, 12, 0, 209, 34, 0, }, /* 48 */ { 34, 5, 12, 0, 163, 34, 0, }, /* 49 */ { 34, 9, 12, 0, 213, 34, 0, }, /* 50 */ { 34, 5, 12, 0, 130, 34, 0, }, /* 51 */ { 34, 9, 12, 0, 214, 34, 0, }, /* 52 */ { 34, 9, 12, 0, 218, 34, 0, }, /* 53 */ { 34, 9, 12, 0, 217, 34, 0, }, /* 54 */ { 34, 9, 12, 0, 219, 34, 0, }, /* 55 */ { 34, 5, 12, 0, 56, 34, 0, }, /* 56 */ { 34, 9, 12, 5, 2, 34, 0, }, /* 57 */ { 34, 8, 12, 5, 1, 34, 0, }, /* 58 */ { 34, 5, 12, 5, -2, 34, 0, }, /* 59 */ { 34, 9, 12, 9, 2, 34, 0, }, /* 60 */ { 34, 8, 12, 9, 1, 34, 0, }, /* 61 */ { 34, 5, 12, 9, -2, 34, 0, }, /* 62 */ { 34, 9, 12, 13, 2, 34, 0, }, /* 63 */ { 34, 8, 12, 13, 1, 34, 0, }, /* 64 */ { 34, 5, 12, 13, -2, 34, 0, }, /* 65 */ { 34, 5, 12, 0, -79, 34, 0, }, /* 66 */ { 34, 9, 12, 17, 2, 34, 0, }, /* 67 */ { 34, 8, 12, 17, 1, 34, 0, }, /* 68 */ { 34, 5, 12, 17, -2, 34, 0, }, /* 69 */ { 34, 9, 12, 0, -97, 34, 0, }, /* 70 */ { 34, 9, 12, 0, -56, 34, 0, }, /* 71 */ { 34, 9, 12, 0, -130, 34, 0, }, /* 72 */ { 34, 9, 12, 0, 10795, 34, 0, }, /* 73 */ { 34, 9, 12, 0, -163, 34, 0, }, /* 74 */ { 34, 9, 12, 0, 10792, 34, 0, }, /* 75 */ { 34, 5, 12, 0, 10815, 34, 0, }, /* 76 */ { 34, 9, 12, 0, -195, 34, 0, }, /* 77 */ { 34, 9, 12, 0, 69, 34, 0, }, /* 78 */ { 34, 9, 12, 0, 71, 34, 0, }, /* 79 */ { 34, 5, 12, 0, 10783, 34, 0, }, /* 80 */ { 34, 5, 12, 0, 10780, 34, 0, }, /* 81 */ { 34, 5, 12, 0, 10782, 34, 0, }, /* 82 */ { 34, 5, 12, 0, -210, 34, 0, }, /* 83 */ { 34, 5, 12, 0, -206, 34, 0, }, /* 84 */ { 34, 5, 12, 0, -205, 34, 0, }, /* 85 */ { 34, 5, 12, 0, -202, 34, 0, }, /* 86 */ { 34, 5, 12, 0, -203, 34, 0, }, /* 87 */ { 34, 5, 12, 0, 42319, 34, 0, }, /* 88 */ { 34, 5, 12, 0, 42315, 34, 0, }, /* 89 */ { 34, 5, 12, 0, -207, 34, 0, }, /* 90 */ { 34, 5, 12, 0, 42280, 34, 0, }, /* 91 */ { 34, 5, 12, 0, 42308, 34, 0, }, /* 92 */ { 34, 5, 12, 0, -209, 34, 0, }, /* 93 */ { 34, 5, 12, 0, -211, 34, 0, }, /* 94 */ { 34, 5, 12, 0, 10743, 34, 0, }, /* 95 */ { 34, 5, 12, 0, 42305, 34, 0, }, /* 96 */ { 34, 5, 12, 0, 10749, 34, 0, }, /* 97 */ { 34, 5, 12, 0, -213, 34, 0, }, /* 98 */ { 34, 5, 12, 0, -214, 34, 0, }, /* 99 */ { 34, 5, 12, 0, 10727, 34, 0, }, /* 100 */ { 34, 5, 12, 0, -218, 34, 0, }, /* 101 */ { 34, 5, 12, 0, 42307, 34, 0, }, /* 102 */ { 34, 5, 12, 0, 42282, 34, 0, }, /* 103 */ { 34, 5, 12, 0, -69, 34, 0, }, /* 104 */ { 34, 5, 12, 0, -217, 34, 0, }, /* 105 */ { 34, 5, 12, 0, -71, 34, 0, }, /* 106 */ { 34, 5, 12, 0, -219, 34, 0, }, /* 107 */ { 34, 5, 12, 0, 42261, 34, 0, }, /* 108 */ { 34, 5, 12, 0, 42258, 34, 0, }, /* 109 */ { 34, 6, 12, 0, 0, 34, 0, }, /* 110 */ { 10, 6, 12, 0, 0, 10, 0, }, /* 111 */ { 4, 24, 12, 0, 0, 4, 0, }, /* 112 */ { 28, 12, 3, 0, 0, 28, 0, }, /* 113 */ { 28, 12, 3, 0, 0, 20, 0, }, /* 114 */ { 28, 12, 3, 21, 116, 20, 0, }, /* 115 */ { 28, 12, 3, 0, 0, 34, 0, }, /* 116 */ { 20, 9, 12, 0, 1, 20, 0, }, /* 117 */ { 20, 5, 12, 0, -1, 20, 0, }, /* 118 */ { 20, 24, 12, 0, 0, 20, 0, }, /* 119 */ { 0, 2, 12, 0, 0, 0, 0, }, /* 120 */ { 20, 6, 12, 0, 0, 20, 0, }, /* 121 */ { 20, 5, 12, 0, 130, 20, 0, }, /* 122 */ { 20, 9, 12, 0, 116, 20, 0, }, /* 123 */ { 20, 9, 12, 0, 38, 20, 0, }, /* 124 */ { 20, 9, 12, 0, 37, 20, 0, }, /* 125 */ { 20, 9, 12, 0, 64, 20, 0, }, /* 126 */ { 20, 9, 12, 0, 63, 20, 0, }, /* 127 */ { 20, 5, 12, 0, 0, 20, 0, }, /* 128 */ { 20, 9, 12, 0, 32, 20, 0, }, /* 129 */ { 20, 9, 12, 34, 32, 20, 0, }, /* 130 */ { 20, 9, 12, 59, 32, 20, 0, }, /* 131 */ { 20, 9, 12, 38, 32, 20, 0, }, /* 132 */ { 20, 9, 12, 21, 32, 20, 0, }, /* 133 */ { 20, 9, 12, 51, 32, 20, 0, }, /* 134 */ { 20, 9, 12, 26, 32, 20, 0, }, /* 135 */ { 20, 9, 12, 47, 32, 20, 0, }, /* 136 */ { 20, 9, 12, 55, 32, 20, 0, }, /* 137 */ { 20, 9, 12, 30, 32, 20, 0, }, /* 138 */ { 20, 9, 12, 43, 32, 20, 0, }, /* 139 */ { 20, 9, 12, 96, 32, 20, 0, }, /* 140 */ { 20, 5, 12, 0, -38, 20, 0, }, /* 141 */ { 20, 5, 12, 0, -37, 20, 0, }, /* 142 */ { 20, 5, 12, 0, -32, 20, 0, }, /* 143 */ { 20, 5, 12, 34, -32, 20, 0, }, /* 144 */ { 20, 5, 12, 59, -32, 20, 0, }, /* 145 */ { 20, 5, 12, 38, -32, 20, 0, }, /* 146 */ { 20, 5, 12, 21, -116, 20, 0, }, /* 147 */ { 20, 5, 12, 51, -32, 20, 0, }, /* 148 */ { 20, 5, 12, 26, -775, 20, 0, }, /* 149 */ { 20, 5, 12, 47, -32, 20, 0, }, /* 150 */ { 20, 5, 12, 55, -32, 20, 0, }, /* 151 */ { 20, 5, 12, 30, 1, 20, 0, }, /* 152 */ { 20, 5, 12, 30, -32, 20, 0, }, /* 153 */ { 20, 5, 12, 43, -32, 20, 0, }, /* 154 */ { 20, 5, 12, 96, -32, 20, 0, }, /* 155 */ { 20, 5, 12, 0, -64, 20, 0, }, /* 156 */ { 20, 5, 12, 0, -63, 20, 0, }, /* 157 */ { 20, 9, 12, 0, 8, 20, 0, }, /* 158 */ { 20, 5, 12, 34, -30, 20, 0, }, /* 159 */ { 20, 5, 12, 38, -25, 20, 0, }, /* 160 */ { 20, 9, 12, 0, 0, 20, 0, }, /* 161 */ { 20, 5, 12, 43, -15, 20, 0, }, /* 162 */ { 20, 5, 12, 47, -22, 20, 0, }, /* 163 */ { 20, 5, 12, 0, -8, 20, 0, }, /* 164 */ { 11, 9, 12, 0, 1, 11, 0, }, /* 165 */ { 11, 5, 12, 0, -1, 11, 0, }, /* 166 */ { 20, 5, 12, 51, -54, 20, 0, }, /* 167 */ { 20, 5, 12, 55, -48, 20, 0, }, /* 168 */ { 20, 5, 12, 0, 7, 20, 0, }, /* 169 */ { 20, 5, 12, 0, -116, 20, 0, }, /* 170 */ { 20, 9, 12, 38, -60, 20, 0, }, /* 171 */ { 20, 5, 12, 59, -64, 20, 0, }, /* 172 */ { 20, 25, 12, 0, 0, 20, 0, }, /* 173 */ { 20, 9, 12, 0, -7, 20, 0, }, /* 174 */ { 20, 9, 12, 0, -130, 20, 0, }, /* 175 */ { 13, 9, 12, 0, 80, 13, 0, }, /* 176 */ { 13, 9, 12, 0, 32, 13, 0, }, /* 177 */ { 13, 9, 12, 63, 32, 13, 0, }, /* 178 */ { 13, 9, 12, 67, 32, 13, 0, }, /* 179 */ { 13, 9, 12, 71, 32, 13, 0, }, /* 180 */ { 13, 9, 12, 75, 32, 13, 0, }, /* 181 */ { 13, 9, 12, 79, 32, 13, 0, }, /* 182 */ { 13, 9, 12, 84, 32, 13, 0, }, /* 183 */ { 13, 5, 12, 0, -32, 13, 0, }, /* 184 */ { 13, 5, 12, 63, -32, 13, 0, }, /* 185 */ { 13, 5, 12, 67, -32, 13, 0, }, /* 186 */ { 13, 5, 12, 71, -32, 13, 0, }, /* 187 */ { 13, 5, 12, 75, -32, 13, 0, }, /* 188 */ { 13, 5, 12, 79, -32, 13, 0, }, /* 189 */ { 13, 5, 12, 84, -32, 13, 0, }, /* 190 */ { 13, 5, 12, 0, -80, 13, 0, }, /* 191 */ { 13, 9, 12, 0, 1, 13, 0, }, /* 192 */ { 13, 5, 12, 0, -1, 13, 0, }, /* 193 */ { 13, 9, 12, 88, 1, 13, 0, }, /* 194 */ { 13, 5, 12, 88, -1, 13, 0, }, /* 195 */ { 13, 26, 12, 0, 0, 13, 0, }, /* 196 */ { 13, 12, 3, 0, 0, -34, 0, }, /* 197 */ { 13, 12, 3, 0, 0, -28, 0, }, /* 198 */ { 28, 12, 3, 0, 0, -31, 0, }, /* 199 */ { 13, 11, 3, 0, 0, 13, 0, }, /* 200 */ { 13, 9, 12, 0, 15, 13, 0, }, /* 201 */ { 13, 5, 12, 0, -15, 13, 0, }, /* 202 */ { 2, 9, 12, 0, 48, 2, 0, }, /* 203 */ { 2, 6, 12, 0, 0, 2, 0, }, /* 204 */ { 2, 21, 12, 0, 0, 2, 0, }, /* 205 */ { 2, 5, 12, 0, 0, 2, 0, }, /* 206 */ { 2, 5, 12, 0, -48, 2, 0, }, /* 207 */ { 2, 17, 12, 0, 0, 2, 0, }, /* 208 */ { 2, 26, 12, 0, 0, 2, 0, }, /* 209 */ { 2, 23, 12, 0, 0, 2, 0, }, /* 210 */ { 26, 12, 3, 0, 0, 26, 0, }, /* 211 */ { 26, 17, 12, 0, 0, 26, 0, }, /* 212 */ { 26, 21, 12, 0, 0, 26, 0, }, /* 213 */ { 26, 7, 12, 0, 0, 26, 0, }, /* 214 */ { 1, 1, 4, 0, 0, 1, 0, }, /* 215 */ { 10, 1, 4, 0, 0, 10, 0, }, /* 216 */ { 1, 25, 12, 0, 0, 1, 0, }, /* 217 */ { 1, 21, 12, 0, 0, 1, 0, }, /* 218 */ { 1, 23, 12, 0, 0, 1, 0, }, /* 219 */ { 10, 21, 12, 0, 0, -143, 0, }, /* 220 */ { 1, 26, 12, 0, 0, 1, 0, }, /* 221 */ { 1, 12, 3, 0, 0, 1, 0, }, /* 222 */ { 1, 1, 2, 0, 0, -82, 0, }, /* 223 */ { 10, 21, 12, 0, 0, -164, 0, }, /* 224 */ { 1, 7, 12, 0, 0, 1, 0, }, /* 225 */ { 10, 6, 12, 0, 0, -181, 0, }, /* 226 */ { 28, 12, 3, 0, 0, -10, 0, }, /* 227 */ { 1, 13, 12, 0, 0, -86, 0, }, /* 228 */ { 1, 21, 12, 0, 0, -4, 0, }, /* 229 */ { 1, 6, 12, 0, 0, 1, 0, }, /* 230 */ { 1, 13, 12, 0, 0, 1, 0, }, /* 231 */ { 50, 21, 12, 0, 0, 50, 0, }, /* 232 */ { 50, 1, 4, 0, 0, 50, 0, }, /* 233 */ { 50, 7, 12, 0, 0, 50, 0, }, /* 234 */ { 50, 12, 3, 0, 0, 50, 0, }, /* 235 */ { 56, 7, 12, 0, 0, 56, 0, }, /* 236 */ { 56, 12, 3, 0, 0, 56, 0, }, /* 237 */ { 64, 13, 12, 0, 0, 64, 0, }, /* 238 */ { 64, 7, 12, 0, 0, 64, 0, }, /* 239 */ { 64, 12, 3, 0, 0, 64, 0, }, /* 240 */ { 64, 6, 12, 0, 0, 64, 0, }, /* 241 */ { 64, 26, 12, 0, 0, 64, 0, }, /* 242 */ { 64, 21, 12, 0, 0, 64, 0, }, /* 243 */ { 64, 23, 12, 0, 0, 64, 0, }, /* 244 */ { 90, 7, 12, 0, 0, 90, 0, }, /* 245 */ { 90, 12, 3, 0, 0, 90, 0, }, /* 246 */ { 90, 6, 12, 0, 0, 90, 0, }, /* 247 */ { 90, 21, 12, 0, 0, 90, 0, }, /* 248 */ { 95, 7, 12, 0, 0, 95, 0, }, /* 249 */ { 95, 12, 3, 0, 0, 95, 0, }, /* 250 */ { 95, 21, 12, 0, 0, 95, 0, }, /* 251 */ { 1, 24, 12, 0, 0, 1, 0, }, /* 252 */ { 15, 12, 3, 0, 0, 15, 0, }, /* 253 */ { 15, 10, 5, 0, 0, 15, 0, }, /* 254 */ { 15, 7, 12, 0, 0, 15, 0, }, /* 255 */ { 28, 12, 3, 0, 0, -216, 0, }, /* 256 */ { 28, 12, 3, 0, 0, -203, 0, }, /* 257 */ { 10, 21, 12, 0, 0, -259, 0, }, /* 258 */ { 10, 21, 12, 0, 0, -280, 0, }, /* 259 */ { 15, 13, 12, 0, 0, -132, 0, }, /* 260 */ { 15, 21, 12, 0, 0, 15, 0, }, /* 261 */ { 15, 6, 12, 0, 0, 15, 0, }, /* 262 */ { 3, 7, 12, 0, 0, 3, 0, }, /* 263 */ { 3, 12, 3, 0, 0, 3, 0, }, /* 264 */ { 3, 10, 5, 0, 0, 3, 0, }, /* 265 */ { 3, 10, 3, 0, 0, 3, 0, }, /* 266 */ { 3, 13, 12, 0, 0, -90, 0, }, /* 267 */ { 3, 23, 12, 0, 0, 3, 0, }, /* 268 */ { 3, 15, 12, 0, 0, 3, 0, }, /* 269 */ { 3, 26, 12, 0, 0, 3, 0, }, /* 270 */ { 3, 21, 12, 0, 0, 3, 0, }, /* 271 */ { 22, 12, 3, 0, 0, 22, 0, }, /* 272 */ { 22, 10, 5, 0, 0, 22, 0, }, /* 273 */ { 22, 7, 12, 0, 0, 22, 0, }, /* 274 */ { 22, 13, 12, 0, 0, -61, 0, }, /* 275 */ { 22, 21, 12, 0, 0, 22, 0, }, /* 276 */ { 21, 12, 3, 0, 0, 21, 0, }, /* 277 */ { 21, 10, 5, 0, 0, 21, 0, }, /* 278 */ { 21, 7, 12, 0, 0, 21, 0, }, /* 279 */ { 21, 13, 12, 0, 0, -58, 0, }, /* 280 */ { 21, 21, 12, 0, 0, 21, 0, }, /* 281 */ { 21, 23, 12, 0, 0, 21, 0, }, /* 282 */ { 44, 12, 3, 0, 0, 44, 0, }, /* 283 */ { 44, 10, 5, 0, 0, 44, 0, }, /* 284 */ { 44, 7, 12, 0, 0, 44, 0, }, /* 285 */ { 44, 10, 3, 0, 0, 44, 0, }, /* 286 */ { 44, 13, 12, 0, 0, 44, 0, }, /* 287 */ { 44, 26, 12, 0, 0, 44, 0, }, /* 288 */ { 44, 15, 12, 0, 0, 44, 0, }, /* 289 */ { 54, 12, 3, 0, 0, 54, 0, }, /* 290 */ { 54, 7, 12, 0, 0, 54, 0, }, /* 291 */ { 54, 10, 3, 0, 0, 54, 0, }, /* 292 */ { 54, 10, 5, 0, 0, 54, 0, }, /* 293 */ { 54, 13, 12, 0, 0, -55, 0, }, /* 294 */ { 54, 15, 12, 0, 0, -55, 0, }, /* 295 */ { 54, 26, 12, 0, 0, -55, 0, }, /* 296 */ { 54, 26, 12, 0, 0, 54, 0, }, /* 297 */ { 54, 23, 12, 0, 0, 54, 0, }, /* 298 */ { 55, 12, 3, 0, 0, 55, 0, }, /* 299 */ { 55, 10, 5, 0, 0, 55, 0, }, /* 300 */ { 55, 7, 12, 0, 0, 55, 0, }, /* 301 */ { 55, 13, 12, 0, 0, 55, 0, }, /* 302 */ { 55, 21, 12, 0, 0, 55, 0, }, /* 303 */ { 55, 15, 12, 0, 0, 55, 0, }, /* 304 */ { 55, 26, 12, 0, 0, 55, 0, }, /* 305 */ { 29, 7, 12, 0, 0, 29, 0, }, /* 306 */ { 29, 12, 3, 0, 0, 29, 0, }, /* 307 */ { 29, 10, 5, 0, 0, 29, 0, }, /* 308 */ { 29, 21, 12, 0, 0, 29, 0, }, /* 309 */ { 29, 10, 3, 0, 0, 29, 0, }, /* 310 */ { 29, 13, 12, 0, 0, -70, 0, }, /* 311 */ { 37, 12, 3, 0, 0, 37, 0, }, /* 312 */ { 37, 10, 5, 0, 0, 37, 0, }, /* 313 */ { 37, 7, 12, 0, 0, 37, 0, }, /* 314 */ { 37, 10, 3, 0, 0, 37, 0, }, /* 315 */ { 37, 7, 4, 0, 0, 37, 0, }, /* 316 */ { 37, 26, 12, 0, 0, 37, 0, }, /* 317 */ { 37, 15, 12, 0, 0, 37, 0, }, /* 318 */ { 37, 13, 12, 0, 0, 37, 0, }, /* 319 */ { 48, 12, 3, 0, 0, 48, 0, }, /* 320 */ { 48, 10, 5, 0, 0, 48, 0, }, /* 321 */ { 48, 7, 12, 0, 0, 48, 0, }, /* 322 */ { 48, 10, 3, 0, 0, 48, 0, }, /* 323 */ { 48, 13, 12, 0, 0, 48, 0, }, /* 324 */ { 48, 21, 12, 0, 0, 48, 0, }, /* 325 */ { 57, 7, 12, 0, 0, 57, 0, }, /* 326 */ { 57, 12, 3, 0, 0, 57, 0, }, /* 327 */ { 57, 7, 5, 0, 0, 57, 0, }, /* 328 */ { 57, 6, 12, 0, 0, 57, 0, }, /* 329 */ { 57, 21, 12, 0, 0, 57, 0, }, /* 330 */ { 57, 13, 12, 0, 0, 57, 0, }, /* 331 */ { 33, 7, 12, 0, 0, 33, 0, }, /* 332 */ { 33, 12, 3, 0, 0, 33, 0, }, /* 333 */ { 33, 7, 5, 0, 0, 33, 0, }, /* 334 */ { 33, 6, 12, 0, 0, 33, 0, }, /* 335 */ { 33, 13, 12, 0, 0, 33, 0, }, /* 336 */ { 58, 7, 12, 0, 0, 58, 0, }, /* 337 */ { 58, 26, 12, 0, 0, 58, 0, }, /* 338 */ { 58, 21, 12, 0, 0, 58, 0, }, /* 339 */ { 58, 12, 3, 0, 0, 58, 0, }, /* 340 */ { 58, 13, 12, 0, 0, 58, 0, }, /* 341 */ { 58, 15, 12, 0, 0, 58, 0, }, /* 342 */ { 58, 22, 12, 0, 0, 58, 0, }, /* 343 */ { 58, 18, 12, 0, 0, 58, 0, }, /* 344 */ { 58, 10, 5, 0, 0, 58, 0, }, /* 345 */ { 39, 7, 12, 0, 0, 39, 0, }, /* 346 */ { 39, 10, 12, 0, 0, 39, 0, }, /* 347 */ { 39, 12, 3, 0, 0, 39, 0, }, /* 348 */ { 39, 10, 5, 0, 0, 39, 0, }, /* 349 */ { 39, 13, 12, 0, 0, -94, 0, }, /* 350 */ { 39, 21, 12, 0, 0, 39, 0, }, /* 351 */ { 39, 13, 12, 0, 0, 39, 0, }, /* 352 */ { 39, 26, 12, 0, 0, 39, 0, }, /* 353 */ { 17, 9, 12, 0, 7264, 17, 0, }, /* 354 */ { 17, 5, 12, 0, 3008, 17, 0, }, /* 355 */ { 10, 21, 12, 0, 0, -52, 0, }, /* 356 */ { 17, 6, 12, 0, 0, 17, 0, }, /* 357 */ { 24, 7, 6, 0, 0, 24, 0, }, /* 358 */ { 24, 7, 7, 0, 0, 24, 0, }, /* 359 */ { 24, 7, 8, 0, 0, 24, 0, }, /* 360 */ { 16, 7, 12, 0, 0, 16, 0, }, /* 361 */ { 16, 12, 3, 0, 0, 16, 0, }, /* 362 */ { 16, 21, 12, 0, 0, 16, 0, }, /* 363 */ { 16, 15, 12, 0, 0, 16, 0, }, /* 364 */ { 16, 26, 12, 0, 0, 16, 0, }, /* 365 */ { 9, 9, 12, 0, 38864, 9, 0, }, /* 366 */ { 9, 9, 12, 0, 8, 9, 0, }, /* 367 */ { 9, 5, 12, 0, -8, 9, 0, }, /* 368 */ { 8, 17, 12, 0, 0, 8, 0, }, /* 369 */ { 8, 7, 12, 0, 0, 8, 0, }, /* 370 */ { 8, 26, 12, 0, 0, 8, 0, }, /* 371 */ { 8, 21, 12, 0, 0, 8, 0, }, /* 372 */ { 41, 29, 12, 0, 0, 41, 0, }, /* 373 */ { 41, 7, 12, 0, 0, 41, 0, }, /* 374 */ { 41, 22, 12, 0, 0, 41, 0, }, /* 375 */ { 41, 18, 12, 0, 0, 41, 0, }, /* 376 */ { 46, 7, 12, 0, 0, 46, 0, }, /* 377 */ { 46, 14, 12, 0, 0, 46, 0, }, /* 378 */ { 51, 7, 12, 0, 0, 51, 0, }, /* 379 */ { 51, 12, 3, 0, 0, 51, 0, }, /* 380 */ { 51, 10, 5, 0, 0, 51, 0, }, /* 381 */ { 25, 7, 12, 0, 0, 25, 0, }, /* 382 */ { 25, 12, 3, 0, 0, 25, 0, }, /* 383 */ { 25, 10, 5, 0, 0, 25, 0, }, /* 384 */ { 10, 21, 12, 0, 0, -127, 0, }, /* 385 */ { 7, 7, 12, 0, 0, 7, 0, }, /* 386 */ { 7, 12, 3, 0, 0, 7, 0, }, /* 387 */ { 52, 7, 12, 0, 0, 52, 0, }, /* 388 */ { 52, 12, 3, 0, 0, 52, 0, }, /* 389 */ { 32, 7, 12, 0, 0, 32, 0, }, /* 390 */ { 32, 12, 3, 0, 0, 32, 0, }, /* 391 */ { 32, 10, 5, 0, 0, 32, 0, }, /* 392 */ { 32, 21, 12, 0, 0, 32, 0, }, /* 393 */ { 32, 6, 12, 0, 0, 32, 0, }, /* 394 */ { 32, 23, 12, 0, 0, 32, 0, }, /* 395 */ { 32, 13, 12, 0, 0, 32, 0, }, /* 396 */ { 32, 15, 12, 0, 0, 32, 0, }, /* 397 */ { 38, 21, 12, 0, 0, 38, 0, }, /* 398 */ { 10, 21, 12, 0, 0, -79, 0, }, /* 399 */ { 38, 17, 12, 0, 0, 38, 0, }, /* 400 */ { 38, 12, 3, 0, 0, 38, 0, }, /* 401 */ { 38, 1, 2, 0, 0, 38, 0, }, /* 402 */ { 38, 13, 12, 0, 0, 38, 0, }, /* 403 */ { 38, 7, 12, 0, 0, 38, 0, }, /* 404 */ { 38, 6, 12, 0, 0, 38, 0, }, /* 405 */ { 35, 7, 12, 0, 0, 35, 0, }, /* 406 */ { 35, 12, 3, 0, 0, 35, 0, }, /* 407 */ { 35, 10, 5, 0, 0, 35, 0, }, /* 408 */ { 35, 26, 12, 0, 0, 35, 0, }, /* 409 */ { 35, 21, 12, 0, 0, 35, 0, }, /* 410 */ { 35, 13, 12, 0, 0, 35, 0, }, /* 411 */ { 53, 7, 12, 0, 0, 53, 0, }, /* 412 */ { 40, 7, 12, 0, 0, 40, 0, }, /* 413 */ { 40, 13, 12, 0, 0, 40, 0, }, /* 414 */ { 40, 15, 12, 0, 0, 40, 0, }, /* 415 */ { 40, 26, 12, 0, 0, 40, 0, }, /* 416 */ { 32, 26, 12, 0, 0, 32, 0, }, /* 417 */ { 6, 7, 12, 0, 0, 6, 0, }, /* 418 */ { 6, 12, 3, 0, 0, 6, 0, }, /* 419 */ { 6, 10, 5, 0, 0, 6, 0, }, /* 420 */ { 6, 21, 12, 0, 0, 6, 0, }, /* 421 */ { 91, 7, 12, 0, 0, 91, 0, }, /* 422 */ { 91, 10, 5, 0, 0, 91, 0, }, /* 423 */ { 91, 12, 3, 0, 0, 91, 0, }, /* 424 */ { 91, 10, 12, 0, 0, 91, 0, }, /* 425 */ { 91, 13, 12, 0, 0, 91, 0, }, /* 426 */ { 91, 21, 12, 0, 0, 91, 0, }, /* 427 */ { 91, 6, 12, 0, 0, 91, 0, }, /* 428 */ { 28, 11, 3, 0, 0, 28, 0, }, /* 429 */ { 62, 12, 3, 0, 0, 62, 0, }, /* 430 */ { 62, 10, 5, 0, 0, 62, 0, }, /* 431 */ { 62, 7, 12, 0, 0, 62, 0, }, /* 432 */ { 62, 10, 3, 0, 0, 62, 0, }, /* 433 */ { 62, 13, 12, 0, 0, 62, 0, }, /* 434 */ { 62, 21, 12, 0, 0, 62, 0, }, /* 435 */ { 62, 26, 12, 0, 0, 62, 0, }, /* 436 */ { 76, 12, 3, 0, 0, 76, 0, }, /* 437 */ { 76, 10, 5, 0, 0, 76, 0, }, /* 438 */ { 76, 7, 12, 0, 0, 76, 0, }, /* 439 */ { 76, 13, 12, 0, 0, 76, 0, }, /* 440 */ { 93, 7, 12, 0, 0, 93, 0, }, /* 441 */ { 93, 12, 3, 0, 0, 93, 0, }, /* 442 */ { 93, 10, 5, 0, 0, 93, 0, }, /* 443 */ { 93, 21, 12, 0, 0, 93, 0, }, /* 444 */ { 70, 7, 12, 0, 0, 70, 0, }, /* 445 */ { 70, 10, 5, 0, 0, 70, 0, }, /* 446 */ { 70, 12, 3, 0, 0, 70, 0, }, /* 447 */ { 70, 21, 12, 0, 0, 70, 0, }, /* 448 */ { 70, 13, 12, 0, 0, 70, 0, }, /* 449 */ { 73, 13, 12, 0, 0, 73, 0, }, /* 450 */ { 73, 7, 12, 0, 0, 73, 0, }, /* 451 */ { 73, 6, 12, 0, 0, 73, 0, }, /* 452 */ { 73, 21, 12, 0, 0, 73, 0, }, /* 453 */ { 13, 5, 12, 63, -6222, 13, 0, }, /* 454 */ { 13, 5, 12, 67, -6221, 13, 0, }, /* 455 */ { 13, 5, 12, 71, -6212, 13, 0, }, /* 456 */ { 13, 5, 12, 75, -6210, 13, 0, }, /* 457 */ { 13, 5, 12, 79, -6210, 13, 0, }, /* 458 */ { 13, 5, 12, 79, -6211, 13, 0, }, /* 459 */ { 13, 5, 12, 84, -6204, 13, 0, }, /* 460 */ { 13, 5, 12, 88, -6180, 13, 0, }, /* 461 */ { 13, 5, 12, 108, 35267, 13, 0, }, /* 462 */ { 17, 9, 12, 0, -3008, 17, 0, }, /* 463 */ { 76, 21, 12, 0, 0, 76, 0, }, /* 464 */ { 28, 12, 3, 0, 0, -122, 0, }, /* 465 */ { 28, 12, 3, 0, 0, 15, 0, }, /* 466 */ { 10, 21, 12, 0, 0, -40, 0, }, /* 467 */ { 28, 12, 3, 0, 0, -16, 0, }, /* 468 */ { 28, 12, 3, 0, 0, -46, 0, }, /* 469 */ { 28, 12, 3, 0, 0, -157, 0, }, /* 470 */ { 10, 10, 5, 0, 0, -16, 0, }, /* 471 */ { 10, 7, 12, 0, 0, -43, 0, }, /* 472 */ { 10, 7, 12, 0, 0, -16, 0, }, /* 473 */ { 10, 7, 12, 0, 0, 15, 0, }, /* 474 */ { 10, 7, 12, 0, 0, -172, 0, }, /* 475 */ { 10, 7, 12, 0, 0, -40, 0, }, /* 476 */ { 28, 12, 3, 0, 0, -106, 0, }, /* 477 */ { 10, 10, 5, 0, 0, 3, 0, }, /* 478 */ { 28, 12, 3, 0, 0, -40, 0, }, /* 479 */ { 10, 7, 12, 0, 0, 150, 0, }, /* 480 */ { 13, 5, 12, 0, 0, 13, 0, }, /* 481 */ { 13, 6, 12, 0, 0, 13, 0, }, /* 482 */ { 34, 5, 12, 0, 35332, 34, 0, }, /* 483 */ { 34, 5, 12, 0, 3814, 34, 0, }, /* 484 */ { 34, 5, 12, 0, 35384, 34, 0, }, /* 485 */ { 28, 12, 3, 0, 0, -37, 0, }, /* 486 */ { 28, 12, 3, 0, 0, 50, 0, }, /* 487 */ { 34, 9, 12, 92, 1, 34, 0, }, /* 488 */ { 34, 5, 12, 92, -1, 34, 0, }, /* 489 */ { 34, 5, 12, 92, -58, 34, 0, }, /* 490 */ { 34, 9, 12, 0, -7615, 34, 0, }, /* 491 */ { 20, 5, 12, 0, 8, 20, 0, }, /* 492 */ { 20, 9, 12, 0, -8, 20, 0, }, /* 493 */ { 20, 5, 12, 0, 74, 20, 0, }, /* 494 */ { 20, 5, 12, 0, 86, 20, 0, }, /* 495 */ { 20, 5, 12, 0, 100, 20, 0, }, /* 496 */ { 20, 5, 12, 0, 128, 20, 0, }, /* 497 */ { 20, 5, 12, 0, 112, 20, 0, }, /* 498 */ { 20, 5, 12, 0, 126, 20, 0, }, /* 499 */ { 20, 8, 12, 0, -8, 20, 0, }, /* 500 */ { 20, 5, 12, 0, 9, 20, 0, }, /* 501 */ { 20, 9, 12, 0, -74, 20, 0, }, /* 502 */ { 20, 8, 12, 0, -9, 20, 0, }, /* 503 */ { 20, 5, 12, 21, -7173, 20, 0, }, /* 504 */ { 20, 9, 12, 0, -86, 20, 0, }, /* 505 */ { 20, 9, 12, 0, -100, 20, 0, }, /* 506 */ { 20, 9, 12, 0, -112, 20, 0, }, /* 507 */ { 20, 9, 12, 0, -128, 20, 0, }, /* 508 */ { 20, 9, 12, 0, -126, 20, 0, }, /* 509 */ { 28, 1, 3, 0, 0, 28, 0, }, /* 510 */ { 28, 1, 13, 0, 0, 28, 0, }, /* 511 */ { 10, 27, 2, 0, 0, 10, 0, }, /* 512 */ { 10, 28, 2, 0, 0, 10, 0, }, /* 513 */ { 10, 29, 12, 0, 0, -73, 0, }, /* 514 */ { 10, 21, 14, 0, 0, 10, 0, }, /* 515 */ { 0, 2, 2, 0, 0, 0, 0, }, /* 516 */ { 28, 12, 3, 0, 0, -110, 0, }, /* 517 */ { 10, 9, 12, 0, 0, 10, 0, }, /* 518 */ { 10, 5, 12, 0, 0, 10, 0, }, /* 519 */ { 20, 9, 12, 96, -7517, 20, 0, }, /* 520 */ { 34, 9, 12, 100, -8383, 34, 0, }, /* 521 */ { 34, 9, 12, 104, -8262, 34, 0, }, /* 522 */ { 34, 9, 12, 0, 28, 34, 0, }, /* 523 */ { 10, 7, 12, 0, 0, 10, 0, }, /* 524 */ { 10, 5, 14, 0, 0, 10, 0, }, /* 525 */ { 34, 5, 12, 0, -28, 34, 0, }, /* 526 */ { 34, 14, 12, 0, 16, 34, 0, }, /* 527 */ { 34, 14, 12, 0, -16, 34, 0, }, /* 528 */ { 34, 14, 12, 0, 0, 34, 0, }, /* 529 */ { 10, 25, 14, 0, 0, 10, 0, }, /* 530 */ { 10, 26, 12, 0, 26, 10, 0, }, /* 531 */ { 10, 26, 14, 0, 26, 10, 0, }, /* 532 */ { 10, 26, 12, 0, -26, 10, 0, }, /* 533 */ { 5, 26, 12, 0, 0, 5, 0, }, /* 534 */ { 18, 9, 12, 0, 48, 18, 0, }, /* 535 */ { 18, 5, 12, 0, -48, 18, 0, }, /* 536 */ { 34, 9, 12, 0, -10743, 34, 0, }, /* 537 */ { 34, 9, 12, 0, -3814, 34, 0, }, /* 538 */ { 34, 9, 12, 0, -10727, 34, 0, }, /* 539 */ { 34, 5, 12, 0, -10795, 34, 0, }, /* 540 */ { 34, 5, 12, 0, -10792, 34, 0, }, /* 541 */ { 34, 9, 12, 0, -10780, 34, 0, }, /* 542 */ { 34, 9, 12, 0, -10749, 34, 0, }, /* 543 */ { 34, 9, 12, 0, -10783, 34, 0, }, /* 544 */ { 34, 9, 12, 0, -10782, 34, 0, }, /* 545 */ { 34, 9, 12, 0, -10815, 34, 0, }, /* 546 */ { 11, 5, 12, 0, 0, 11, 0, }, /* 547 */ { 11, 26, 12, 0, 0, 11, 0, }, /* 548 */ { 11, 12, 3, 0, 0, 11, 0, }, /* 549 */ { 11, 21, 12, 0, 0, 11, 0, }, /* 550 */ { 11, 15, 12, 0, 0, 11, 0, }, /* 551 */ { 17, 5, 12, 0, -7264, 17, 0, }, /* 552 */ { 59, 7, 12, 0, 0, 59, 0, }, /* 553 */ { 59, 6, 12, 0, 0, 59, 0, }, /* 554 */ { 59, 21, 12, 0, 0, 59, 0, }, /* 555 */ { 59, 12, 3, 0, 0, 59, 0, }, /* 556 */ { 13, 12, 3, 0, 0, 13, 0, }, /* 557 */ { 10, 21, 12, 0, 0, -28, 0, }, /* 558 */ { 23, 26, 12, 0, 0, 23, 0, }, /* 559 */ { 10, 21, 12, 0, 0, -150, 0, }, /* 560 */ { 10, 21, 12, 0, 0, -137, 0, }, /* 561 */ { 23, 6, 12, 0, 0, 23, 0, }, /* 562 */ { 10, 7, 12, 0, 0, 23, 0, }, /* 563 */ { 23, 14, 12, 0, 0, 23, 0, }, /* 564 */ { 10, 22, 12, 0, 0, -150, 0, }, /* 565 */ { 10, 18, 12, 0, 0, -150, 0, }, /* 566 */ { 10, 26, 12, 0, 0, -137, 0, }, /* 567 */ { 10, 17, 12, 0, 0, -137, 0, }, /* 568 */ { 10, 22, 12, 0, 0, -137, 0, }, /* 569 */ { 10, 18, 12, 0, 0, -137, 0, }, /* 570 */ { 28, 12, 3, 0, 0, -19, 0, }, /* 571 */ { 24, 10, 3, 0, 0, 24, 0, }, /* 572 */ { 10, 17, 14, 0, 0, -137, 0, }, /* 573 */ { 10, 6, 12, 0, 0, -67, 0, }, /* 574 */ { 10, 7, 12, 0, 0, -114, 0, }, /* 575 */ { 10, 21, 14, 0, 0, -114, 0, }, /* 576 */ { 10, 26, 12, 0, 0, 23, 0, }, /* 577 */ { 27, 7, 12, 0, 0, 27, 0, }, /* 578 */ { 28, 12, 3, 0, 0, -67, 0, }, /* 579 */ { 10, 24, 12, 0, 0, -67, 0, }, /* 580 */ { 27, 6, 12, 0, 0, 27, 0, }, /* 581 */ { 10, 17, 12, 0, 0, -67, 0, }, /* 582 */ { 30, 7, 12, 0, 0, 30, 0, }, /* 583 */ { 30, 6, 12, 0, 0, 30, 0, }, /* 584 */ { 4, 7, 12, 0, 0, 4, 0, }, /* 585 */ { 24, 7, 12, 0, 0, 24, 0, }, /* 586 */ { 10, 15, 12, 0, 0, 23, 0, }, /* 587 */ { 24, 26, 12, 0, 0, 24, 0, }, /* 588 */ { 10, 26, 14, 0, 0, 23, 0, }, /* 589 */ { 30, 26, 12, 0, 0, 30, 0, }, /* 590 */ { 23, 7, 12, 0, 0, 23, 0, }, /* 591 */ { 61, 7, 12, 0, 0, 61, 0, }, /* 592 */ { 61, 6, 12, 0, 0, 61, 0, }, /* 593 */ { 61, 26, 12, 0, 0, 61, 0, }, /* 594 */ { 86, 7, 12, 0, 0, 86, 0, }, /* 595 */ { 86, 6, 12, 0, 0, 86, 0, }, /* 596 */ { 86, 21, 12, 0, 0, 86, 0, }, /* 597 */ { 77, 7, 12, 0, 0, 77, 0, }, /* 598 */ { 77, 6, 12, 0, 0, 77, 0, }, /* 599 */ { 77, 21, 12, 0, 0, 77, 0, }, /* 600 */ { 77, 13, 12, 0, 0, 77, 0, }, /* 601 */ { 13, 9, 12, 108, 1, 13, 0, }, /* 602 */ { 13, 5, 12, 108, -35267, 13, 0, }, /* 603 */ { 13, 7, 12, 0, 0, 13, 0, }, /* 604 */ { 13, 21, 12, 0, 0, 13, 0, }, /* 605 */ { 79, 7, 12, 0, 0, 79, 0, }, /* 606 */ { 79, 14, 12, 0, 0, 79, 0, }, /* 607 */ { 79, 12, 3, 0, 0, 79, 0, }, /* 608 */ { 79, 21, 12, 0, 0, 79, 0, }, /* 609 */ { 10, 24, 12, 0, 0, -64, 0, }, /* 610 */ { 34, 9, 12, 0, -35332, 34, 0, }, /* 611 */ { 34, 9, 12, 0, -42280, 34, 0, }, /* 612 */ { 34, 5, 12, 0, 48, 34, 0, }, /* 613 */ { 34, 9, 12, 0, -42308, 34, 0, }, /* 614 */ { 34, 9, 12, 0, -42319, 34, 0, }, /* 615 */ { 34, 9, 12, 0, -42315, 34, 0, }, /* 616 */ { 34, 9, 12, 0, -42305, 34, 0, }, /* 617 */ { 34, 9, 12, 0, -42258, 34, 0, }, /* 618 */ { 34, 9, 12, 0, -42282, 34, 0, }, /* 619 */ { 34, 9, 12, 0, -42261, 34, 0, }, /* 620 */ { 34, 9, 12, 0, 928, 34, 0, }, /* 621 */ { 34, 9, 12, 0, -48, 34, 0, }, /* 622 */ { 34, 9, 12, 0, -42307, 34, 0, }, /* 623 */ { 34, 9, 12, 0, -35384, 34, 0, }, /* 624 */ { 49, 7, 12, 0, 0, 49, 0, }, /* 625 */ { 49, 12, 3, 0, 0, 49, 0, }, /* 626 */ { 49, 10, 5, 0, 0, 49, 0, }, /* 627 */ { 49, 26, 12, 0, 0, 49, 0, }, /* 628 */ { 10, 15, 12, 0, 0, -244, 0, }, /* 629 */ { 10, 15, 12, 0, 0, -230, 0, }, /* 630 */ { 10, 26, 12, 0, 0, -191, 0, }, /* 631 */ { 10, 23, 12, 0, 0, -191, 0, }, /* 632 */ { 65, 7, 12, 0, 0, 65, 0, }, /* 633 */ { 65, 21, 12, 0, 0, 65, 0, }, /* 634 */ { 75, 10, 5, 0, 0, 75, 0, }, /* 635 */ { 75, 7, 12, 0, 0, 75, 0, }, /* 636 */ { 75, 12, 3, 0, 0, 75, 0, }, /* 637 */ { 75, 21, 12, 0, 0, 75, 0, }, /* 638 */ { 75, 13, 12, 0, 0, 75, 0, }, /* 639 */ { 15, 12, 3, 0, 0, -16, 0, }, /* 640 */ { 15, 7, 12, 0, 0, -49, 0, }, /* 641 */ { 69, 13, 12, 0, 0, 69, 0, }, /* 642 */ { 69, 7, 12, 0, 0, 69, 0, }, /* 643 */ { 69, 12, 3, 0, 0, 69, 0, }, /* 644 */ { 10, 21, 12, 0, 0, -118, 0, }, /* 645 */ { 69, 21, 12, 0, 0, 69, 0, }, /* 646 */ { 74, 7, 12, 0, 0, 74, 0, }, /* 647 */ { 74, 12, 3, 0, 0, 74, 0, }, /* 648 */ { 74, 10, 5, 0, 0, 74, 0, }, /* 649 */ { 74, 21, 12, 0, 0, 74, 0, }, /* 650 */ { 84, 12, 3, 0, 0, 84, 0, }, /* 651 */ { 84, 10, 5, 0, 0, 84, 0, }, /* 652 */ { 84, 7, 12, 0, 0, 84, 0, }, /* 653 */ { 84, 21, 12, 0, 0, 84, 0, }, /* 654 */ { 10, 6, 12, 0, 0, -22, 0, }, /* 655 */ { 84, 13, 12, 0, 0, 84, 0, }, /* 656 */ { 39, 6, 12, 0, 0, 39, 0, }, /* 657 */ { 68, 7, 12, 0, 0, 68, 0, }, /* 658 */ { 68, 12, 3, 0, 0, 68, 0, }, /* 659 */ { 68, 10, 5, 0, 0, 68, 0, }, /* 660 */ { 68, 13, 12, 0, 0, 68, 0, }, /* 661 */ { 68, 21, 12, 0, 0, 68, 0, }, /* 662 */ { 92, 7, 12, 0, 0, 92, 0, }, /* 663 */ { 92, 12, 3, 0, 0, 92, 0, }, /* 664 */ { 92, 6, 12, 0, 0, 92, 0, }, /* 665 */ { 92, 21, 12, 0, 0, 92, 0, }, /* 666 */ { 87, 7, 12, 0, 0, 87, 0, }, /* 667 */ { 87, 10, 5, 0, 0, 87, 0, }, /* 668 */ { 87, 12, 3, 0, 0, 87, 0, }, /* 669 */ { 87, 21, 12, 0, 0, 87, 0, }, /* 670 */ { 87, 6, 12, 0, 0, 87, 0, }, /* 671 */ { 34, 5, 12, 0, -928, 34, 0, }, /* 672 */ { 9, 5, 12, 0, -38864, 9, 0, }, /* 673 */ { 87, 13, 12, 0, 0, 87, 0, }, /* 674 */ { 24, 7, 9, 0, 0, 24, 0, }, /* 675 */ { 24, 7, 10, 0, 0, 24, 0, }, /* 676 */ { 0, 4, 12, 0, 0, 0, 0, }, /* 677 */ { 0, 3, 12, 0, 0, 0, 0, }, /* 678 */ { 26, 25, 12, 0, 0, 26, 0, }, /* 679 */ { 10, 18, 12, 0, 0, -7, 0, }, /* 680 */ { 10, 22, 12, 0, 0, -7, 0, }, /* 681 */ { 1, 7, 12, 0, 0, -13, 0, }, /* 682 */ { 1, 26, 12, 0, 0, -13, 0, }, /* 683 */ { 10, 6, 3, 0, 0, -67, 0, }, /* 684 */ { 36, 7, 12, 0, 0, 36, 0, }, /* 685 */ { 10, 21, 12, 0, 0, -98, 0, }, /* 686 */ { 10, 21, 12, 0, 0, -25, 0, }, /* 687 */ { 10, 15, 12, 0, 0, -102, 0, }, /* 688 */ { 10, 26, 12, 0, 0, -25, 0, }, /* 689 */ { 20, 14, 12, 0, 0, 20, 0, }, /* 690 */ { 20, 15, 12, 0, 0, 20, 0, }, /* 691 */ { 20, 26, 12, 0, 0, 20, 0, }, /* 692 */ { 71, 7, 12, 0, 0, 71, 0, }, /* 693 */ { 67, 7, 12, 0, 0, 67, 0, }, /* 694 */ { 28, 12, 3, 0, 0, -1, 0, }, /* 695 */ { 10, 15, 12, 0, 0, -1, 0, }, /* 696 */ { 42, 7, 12, 0, 0, 42, 0, }, /* 697 */ { 42, 15, 12, 0, 0, 42, 0, }, /* 698 */ { 19, 7, 12, 0, 0, 19, 0, }, /* 699 */ { 19, 14, 12, 0, 0, 19, 0, }, /* 700 */ { 118, 7, 12, 0, 0, 118, 0, }, /* 701 */ { 118, 12, 3, 0, 0, 118, 0, }, /* 702 */ { 60, 7, 12, 0, 0, 60, 0, }, /* 703 */ { 60, 21, 12, 0, 0, 60, 0, }, /* 704 */ { 43, 7, 12, 0, 0, 43, 0, }, /* 705 */ { 43, 21, 12, 0, 0, 43, 0, }, /* 706 */ { 43, 14, 12, 0, 0, 43, 0, }, /* 707 */ { 14, 9, 12, 0, 40, 14, 0, }, /* 708 */ { 14, 5, 12, 0, -40, 14, 0, }, /* 709 */ { 47, 7, 12, 0, 0, 47, 0, }, /* 710 */ { 45, 7, 12, 0, 0, 45, 0, }, /* 711 */ { 45, 13, 12, 0, 0, 45, 0, }, /* 712 */ { 136, 9, 12, 0, 40, 136, 0, }, /* 713 */ { 136, 5, 12, 0, -40, 136, 0, }, /* 714 */ { 106, 7, 12, 0, 0, 106, 0, }, /* 715 */ { 104, 7, 12, 0, 0, 104, 0, }, /* 716 */ { 104, 21, 12, 0, 0, 104, 0, }, /* 717 */ { 161, 9, 12, 0, 39, 161, 0, }, /* 718 */ { 161, 5, 12, 0, -39, 161, 0, }, /* 719 */ { 110, 7, 12, 0, 0, 110, 0, }, /* 720 */ { 12, 7, 12, 0, 0, 12, 0, }, /* 721 */ { 81, 7, 12, 0, 0, 81, 0, }, /* 722 */ { 81, 21, 12, 0, 0, 81, 0, }, /* 723 */ { 81, 15, 12, 0, 0, 81, 0, }, /* 724 */ { 120, 7, 12, 0, 0, 120, 0, }, /* 725 */ { 120, 26, 12, 0, 0, 120, 0, }, /* 726 */ { 120, 15, 12, 0, 0, 120, 0, }, /* 727 */ { 116, 7, 12, 0, 0, 116, 0, }, /* 728 */ { 116, 15, 12, 0, 0, 116, 0, }, /* 729 */ { 128, 7, 12, 0, 0, 128, 0, }, /* 730 */ { 128, 15, 12, 0, 0, 128, 0, }, /* 731 */ { 66, 7, 12, 0, 0, 66, 0, }, /* 732 */ { 66, 15, 12, 0, 0, 66, 0, }, /* 733 */ { 66, 21, 12, 0, 0, 66, 0, }, /* 734 */ { 72, 7, 12, 0, 0, 72, 0, }, /* 735 */ { 72, 21, 12, 0, 0, 72, 0, }, /* 736 */ { 98, 7, 12, 0, 0, 98, 0, }, /* 737 */ { 97, 7, 12, 0, 0, 97, 0, }, /* 738 */ { 97, 15, 12, 0, 0, 97, 0, }, /* 739 */ { 31, 7, 12, 0, 0, 31, 0, }, /* 740 */ { 31, 12, 3, 0, 0, 31, 0, }, /* 741 */ { 31, 15, 12, 0, 0, 31, 0, }, /* 742 */ { 31, 21, 12, 0, 0, 31, 0, }, /* 743 */ { 88, 7, 12, 0, 0, 88, 0, }, /* 744 */ { 88, 15, 12, 0, 0, 88, 0, }, /* 745 */ { 88, 21, 12, 0, 0, 88, 0, }, /* 746 */ { 117, 7, 12, 0, 0, 117, 0, }, /* 747 */ { 117, 15, 12, 0, 0, 117, 0, }, /* 748 */ { 112, 7, 12, 0, 0, 112, 0, }, /* 749 */ { 112, 26, 12, 0, 0, 112, 0, }, /* 750 */ { 112, 12, 3, 0, 0, 112, 0, }, /* 751 */ { 112, 15, 12, 0, 0, 112, 0, }, /* 752 */ { 112, 21, 12, 0, 0, 112, 0, }, /* 753 */ { 112, 21, 12, 0, 0, -76, 0, }, /* 754 */ { 78, 7, 12, 0, 0, 78, 0, }, /* 755 */ { 78, 21, 12, 0, 0, 78, 0, }, /* 756 */ { 83, 7, 12, 0, 0, 83, 0, }, /* 757 */ { 83, 15, 12, 0, 0, 83, 0, }, /* 758 */ { 82, 7, 12, 0, 0, 82, 0, }, /* 759 */ { 82, 15, 12, 0, 0, 82, 0, }, /* 760 */ { 121, 7, 12, 0, 0, 121, 0, }, /* 761 */ { 121, 21, 12, 0, 0, 121, 0, }, /* 762 */ { 121, 15, 12, 0, 0, 121, 0, }, /* 763 */ { 89, 7, 12, 0, 0, 89, 0, }, /* 764 */ { 130, 9, 12, 0, 64, 130, 0, }, /* 765 */ { 130, 5, 12, 0, -64, 130, 0, }, /* 766 */ { 130, 15, 12, 0, 0, 130, 0, }, /* 767 */ { 144, 7, 12, 0, 0, 144, 0, }, /* 768 */ { 144, 12, 3, 0, 0, 144, 0, }, /* 769 */ { 144, 13, 12, 0, 0, 144, 0, }, /* 770 */ { 1, 15, 12, 0, 0, 1, 0, }, /* 771 */ { 156, 7, 12, 0, 0, 156, 0, }, /* 772 */ { 156, 12, 3, 0, 0, 156, 0, }, /* 773 */ { 156, 17, 12, 0, 0, 156, 0, }, /* 774 */ { 147, 7, 12, 0, 0, 147, 0, }, /* 775 */ { 147, 15, 12, 0, 0, 147, 0, }, /* 776 */ { 148, 7, 12, 0, 0, 148, 0, }, /* 777 */ { 148, 12, 3, 0, 0, 148, 0, }, /* 778 */ { 148, 15, 12, 0, 0, 148, 0, }, /* 779 */ { 148, 21, 12, 0, 0, 148, 0, }, /* 780 */ { 158, 7, 12, 0, 0, 158, 0, }, /* 781 */ { 158, 12, 3, 0, 0, 158, 0, }, /* 782 */ { 158, 21, 12, 0, 0, 158, 0, }, /* 783 */ { 153, 7, 12, 0, 0, 153, 0, }, /* 784 */ { 153, 15, 12, 0, 0, 153, 0, }, /* 785 */ { 149, 7, 12, 0, 0, 149, 0, }, /* 786 */ { 94, 10, 5, 0, 0, 94, 0, }, /* 787 */ { 94, 12, 3, 0, 0, 94, 0, }, /* 788 */ { 94, 7, 12, 0, 0, 94, 0, }, /* 789 */ { 94, 21, 12, 0, 0, 94, 0, }, /* 790 */ { 94, 15, 12, 0, 0, 94, 0, }, /* 791 */ { 94, 13, 12, 0, 0, 94, 0, }, /* 792 */ { 85, 12, 3, 0, 0, 85, 0, }, /* 793 */ { 85, 10, 5, 0, 0, 85, 0, }, /* 794 */ { 85, 7, 12, 0, 0, 85, 0, }, /* 795 */ { 85, 21, 12, 0, 0, 85, 0, }, /* 796 */ { 85, 1, 4, 0, 0, 85, 0, }, /* 797 */ { 101, 7, 12, 0, 0, 101, 0, }, /* 798 */ { 101, 13, 12, 0, 0, 101, 0, }, /* 799 */ { 96, 12, 3, 0, 0, 96, 0, }, /* 800 */ { 96, 7, 12, 0, 0, 96, 0, }, /* 801 */ { 96, 10, 5, 0, 0, 96, 0, }, /* 802 */ { 96, 13, 12, 0, 0, 96, 0, }, /* 803 */ { 96, 21, 12, 0, 0, 96, 0, }, /* 804 */ { 111, 7, 12, 0, 0, 111, 0, }, /* 805 */ { 111, 12, 3, 0, 0, 111, 0, }, /* 806 */ { 111, 21, 12, 0, 0, 111, 0, }, /* 807 */ { 100, 12, 3, 0, 0, 100, 0, }, /* 808 */ { 100, 10, 5, 0, 0, 100, 0, }, /* 809 */ { 100, 7, 12, 0, 0, 100, 0, }, /* 810 */ { 100, 7, 4, 0, 0, 100, 0, }, /* 811 */ { 100, 21, 12, 0, 0, 100, 0, }, /* 812 */ { 100, 13, 12, 0, 0, 100, 0, }, /* 813 */ { 48, 15, 12, 0, 0, 48, 0, }, /* 814 */ { 108, 7, 12, 0, 0, 108, 0, }, /* 815 */ { 108, 10, 5, 0, 0, 108, 0, }, /* 816 */ { 108, 12, 3, 0, 0, 108, 0, }, /* 817 */ { 108, 21, 12, 0, 0, 108, 0, }, /* 818 */ { 129, 7, 12, 0, 0, 129, 0, }, /* 819 */ { 129, 21, 12, 0, 0, 129, 0, }, /* 820 */ { 109, 7, 12, 0, 0, 109, 0, }, /* 821 */ { 109, 12, 3, 0, 0, 109, 0, }, /* 822 */ { 109, 10, 5, 0, 0, 109, 0, }, /* 823 */ { 109, 13, 12, 0, 0, 109, 0, }, /* 824 */ { 107, 12, 3, 0, 0, 107, 0, }, /* 825 */ { 107, 12, 3, 0, 0, -55, 0, }, /* 826 */ { 107, 10, 5, 0, 0, 107, 0, }, /* 827 */ { 107, 10, 5, 0, 0, -55, 0, }, /* 828 */ { 107, 7, 12, 0, 0, 107, 0, }, /* 829 */ { 28, 12, 3, 0, 0, -55, 0, }, /* 830 */ { 107, 10, 3, 0, 0, 107, 0, }, /* 831 */ { 135, 7, 12, 0, 0, 135, 0, }, /* 832 */ { 135, 10, 5, 0, 0, 135, 0, }, /* 833 */ { 135, 12, 3, 0, 0, 135, 0, }, /* 834 */ { 135, 21, 12, 0, 0, 135, 0, }, /* 835 */ { 135, 13, 12, 0, 0, 135, 0, }, /* 836 */ { 124, 7, 12, 0, 0, 124, 0, }, /* 837 */ { 124, 10, 3, 0, 0, 124, 0, }, /* 838 */ { 124, 10, 5, 0, 0, 124, 0, }, /* 839 */ { 124, 12, 3, 0, 0, 124, 0, }, /* 840 */ { 124, 21, 12, 0, 0, 124, 0, }, /* 841 */ { 124, 13, 12, 0, 0, 124, 0, }, /* 842 */ { 123, 7, 12, 0, 0, 123, 0, }, /* 843 */ { 123, 10, 3, 0, 0, 123, 0, }, /* 844 */ { 123, 10, 5, 0, 0, 123, 0, }, /* 845 */ { 123, 12, 3, 0, 0, 123, 0, }, /* 846 */ { 123, 21, 12, 0, 0, 123, 0, }, /* 847 */ { 114, 7, 12, 0, 0, 114, 0, }, /* 848 */ { 114, 10, 5, 0, 0, 114, 0, }, /* 849 */ { 114, 12, 3, 0, 0, 114, 0, }, /* 850 */ { 114, 21, 12, 0, 0, 114, 0, }, /* 851 */ { 114, 13, 12, 0, 0, 114, 0, }, /* 852 */ { 102, 7, 12, 0, 0, 102, 0, }, /* 853 */ { 102, 12, 3, 0, 0, 102, 0, }, /* 854 */ { 102, 10, 5, 0, 0, 102, 0, }, /* 855 */ { 102, 21, 12, 0, 0, 102, 0, }, /* 856 */ { 102, 13, 12, 0, 0, 102, 0, }, /* 857 */ { 126, 7, 12, 0, 0, 126, 0, }, /* 858 */ { 126, 12, 3, 0, 0, 126, 0, }, /* 859 */ { 126, 10, 12, 0, 0, 126, 0, }, /* 860 */ { 126, 10, 5, 0, 0, 126, 0, }, /* 861 */ { 126, 13, 12, 0, 0, 126, 0, }, /* 862 */ { 126, 15, 12, 0, 0, 126, 0, }, /* 863 */ { 126, 21, 12, 0, 0, 126, 0, }, /* 864 */ { 126, 26, 12, 0, 0, 126, 0, }, /* 865 */ { 142, 7, 12, 0, 0, 142, 0, }, /* 866 */ { 142, 10, 5, 0, 0, 142, 0, }, /* 867 */ { 142, 12, 3, 0, 0, 142, 0, }, /* 868 */ { 142, 21, 12, 0, 0, 142, 0, }, /* 869 */ { 125, 9, 12, 0, 32, 125, 0, }, /* 870 */ { 125, 5, 12, 0, -32, 125, 0, }, /* 871 */ { 125, 13, 12, 0, 0, 125, 0, }, /* 872 */ { 125, 15, 12, 0, 0, 125, 0, }, /* 873 */ { 125, 7, 12, 0, 0, 125, 0, }, /* 874 */ { 154, 7, 12, 0, 0, 154, 0, }, /* 875 */ { 154, 10, 3, 0, 0, 154, 0, }, /* 876 */ { 154, 10, 5, 0, 0, 154, 0, }, /* 877 */ { 154, 12, 3, 0, 0, 154, 0, }, /* 878 */ { 154, 7, 4, 0, 0, 154, 0, }, /* 879 */ { 154, 21, 12, 0, 0, 154, 0, }, /* 880 */ { 154, 13, 12, 0, 0, 154, 0, }, /* 881 */ { 150, 7, 12, 0, 0, 150, 0, }, /* 882 */ { 150, 10, 5, 0, 0, 150, 0, }, /* 883 */ { 150, 12, 3, 0, 0, 150, 0, }, /* 884 */ { 150, 21, 12, 0, 0, 150, 0, }, /* 885 */ { 141, 7, 12, 0, 0, 141, 0, }, /* 886 */ { 141, 12, 3, 0, 0, 141, 0, }, /* 887 */ { 141, 10, 5, 0, 0, 141, 0, }, /* 888 */ { 141, 7, 4, 0, 0, 141, 0, }, /* 889 */ { 141, 21, 12, 0, 0, 141, 0, }, /* 890 */ { 140, 7, 12, 0, 0, 140, 0, }, /* 891 */ { 140, 12, 3, 0, 0, 140, 0, }, /* 892 */ { 140, 10, 5, 0, 0, 140, 0, }, /* 893 */ { 140, 7, 4, 0, 0, 140, 0, }, /* 894 */ { 140, 21, 12, 0, 0, 140, 0, }, /* 895 */ { 122, 7, 12, 0, 0, 122, 0, }, /* 896 */ { 133, 7, 12, 0, 0, 133, 0, }, /* 897 */ { 133, 10, 5, 0, 0, 133, 0, }, /* 898 */ { 133, 12, 3, 0, 0, 133, 0, }, /* 899 */ { 133, 21, 12, 0, 0, 133, 0, }, /* 900 */ { 133, 13, 12, 0, 0, 133, 0, }, /* 901 */ { 133, 15, 12, 0, 0, 133, 0, }, /* 902 */ { 134, 21, 12, 0, 0, 134, 0, }, /* 903 */ { 134, 7, 12, 0, 0, 134, 0, }, /* 904 */ { 134, 12, 3, 0, 0, 134, 0, }, /* 905 */ { 134, 10, 5, 0, 0, 134, 0, }, /* 906 */ { 138, 7, 12, 0, 0, 138, 0, }, /* 907 */ { 138, 12, 3, 0, 0, 138, 0, }, /* 908 */ { 138, 7, 4, 0, 0, 138, 0, }, /* 909 */ { 138, 13, 12, 0, 0, 138, 0, }, /* 910 */ { 143, 7, 12, 0, 0, 143, 0, }, /* 911 */ { 143, 10, 5, 0, 0, 143, 0, }, /* 912 */ { 143, 12, 3, 0, 0, 143, 0, }, /* 913 */ { 143, 13, 12, 0, 0, 143, 0, }, /* 914 */ { 145, 7, 12, 0, 0, 145, 0, }, /* 915 */ { 145, 12, 3, 0, 0, 145, 0, }, /* 916 */ { 145, 10, 5, 0, 0, 145, 0, }, /* 917 */ { 145, 21, 12, 0, 0, 145, 0, }, /* 918 */ { 54, 15, 12, 0, 0, 54, 0, }, /* 919 */ { 54, 21, 12, 0, 0, 54, 0, }, /* 920 */ { 63, 7, 12, 0, 0, 63, 0, }, /* 921 */ { 63, 14, 12, 0, 0, 63, 0, }, /* 922 */ { 63, 21, 12, 0, 0, 63, 0, }, /* 923 */ { 157, 7, 12, 0, 0, 157, 0, }, /* 924 */ { 157, 21, 12, 0, 0, 157, 0, }, /* 925 */ { 80, 7, 12, 0, 0, 80, 0, }, /* 926 */ { 80, 1, 2, 0, 0, 80, 0, }, /* 927 */ { 127, 7, 12, 0, 0, 127, 0, }, /* 928 */ { 115, 7, 12, 0, 0, 115, 0, }, /* 929 */ { 115, 13, 12, 0, 0, 115, 0, }, /* 930 */ { 115, 21, 12, 0, 0, 115, 0, }, /* 931 */ { 159, 7, 12, 0, 0, 159, 0, }, /* 932 */ { 159, 13, 12, 0, 0, 159, 0, }, /* 933 */ { 103, 7, 12, 0, 0, 103, 0, }, /* 934 */ { 103, 12, 3, 0, 0, 103, 0, }, /* 935 */ { 103, 21, 12, 0, 0, 103, 0, }, /* 936 */ { 119, 7, 12, 0, 0, 119, 0, }, /* 937 */ { 119, 12, 3, 0, 0, 119, 0, }, /* 938 */ { 119, 21, 12, 0, 0, 119, 0, }, /* 939 */ { 119, 26, 12, 0, 0, 119, 0, }, /* 940 */ { 119, 6, 12, 0, 0, 119, 0, }, /* 941 */ { 119, 13, 12, 0, 0, 119, 0, }, /* 942 */ { 119, 15, 12, 0, 0, 119, 0, }, /* 943 */ { 146, 9, 12, 0, 32, 146, 0, }, /* 944 */ { 146, 5, 12, 0, -32, 146, 0, }, /* 945 */ { 146, 15, 12, 0, 0, 146, 0, }, /* 946 */ { 146, 21, 12, 0, 0, 146, 0, }, /* 947 */ { 99, 7, 12, 0, 0, 99, 0, }, /* 948 */ { 99, 12, 3, 0, 0, 99, 0, }, /* 949 */ { 99, 10, 5, 0, 0, 99, 0, }, /* 950 */ { 99, 6, 12, 0, 0, 99, 0, }, /* 951 */ { 137, 6, 12, 0, 0, 137, 0, }, /* 952 */ { 139, 6, 12, 0, 0, 139, 0, }, /* 953 */ { 23, 21, 12, 0, 0, 23, 0, }, /* 954 */ { 155, 12, 3, 0, 0, 155, 0, }, /* 955 */ { 23, 10, 5, 0, 0, 23, 0, }, /* 956 */ { 137, 7, 12, 0, 0, 137, 0, }, /* 957 */ { 155, 7, 12, 0, 0, 155, 0, }, /* 958 */ { 139, 7, 12, 0, 0, 139, 0, }, /* 959 */ { 105, 7, 12, 0, 0, 105, 0, }, /* 960 */ { 105, 26, 12, 0, 0, 105, 0, }, /* 961 */ { 105, 12, 3, 0, 0, 105, 0, }, /* 962 */ { 105, 21, 12, 0, 0, 105, 0, }, /* 963 */ { 10, 1, 2, 0, 0, 105, 0, }, /* 964 */ { 10, 10, 3, 0, 0, 10, 0, }, /* 965 */ { 10, 10, 5, 0, 0, 10, 0, }, /* 966 */ { 20, 12, 3, 0, 0, 20, 0, }, /* 967 */ { 131, 26, 12, 0, 0, 131, 0, }, /* 968 */ { 131, 12, 3, 0, 0, 131, 0, }, /* 969 */ { 131, 21, 12, 0, 0, 131, 0, }, /* 970 */ { 18, 12, 3, 0, 0, 18, 0, }, /* 971 */ { 151, 7, 12, 0, 0, 151, 0, }, /* 972 */ { 151, 12, 3, 0, 0, 151, 0, }, /* 973 */ { 151, 6, 12, 0, 0, 151, 0, }, /* 974 */ { 151, 13, 12, 0, 0, 151, 0, }, /* 975 */ { 151, 26, 12, 0, 0, 151, 0, }, /* 976 */ { 160, 7, 12, 0, 0, 160, 0, }, /* 977 */ { 160, 12, 3, 0, 0, 160, 0, }, /* 978 */ { 152, 7, 12, 0, 0, 152, 0, }, /* 979 */ { 152, 12, 3, 0, 0, 152, 0, }, /* 980 */ { 152, 13, 12, 0, 0, 152, 0, }, /* 981 */ { 152, 23, 12, 0, 0, 152, 0, }, /* 982 */ { 113, 7, 12, 0, 0, 113, 0, }, /* 983 */ { 113, 15, 12, 0, 0, 113, 0, }, /* 984 */ { 113, 12, 3, 0, 0, 113, 0, }, /* 985 */ { 132, 9, 12, 0, 34, 132, 0, }, /* 986 */ { 132, 5, 12, 0, -34, 132, 0, }, /* 987 */ { 132, 12, 3, 0, 0, 132, 0, }, /* 988 */ { 132, 6, 12, 0, 0, 132, 0, }, /* 989 */ { 132, 13, 12, 0, 0, 132, 0, }, /* 990 */ { 132, 21, 12, 0, 0, 132, 0, }, /* 991 */ { 0, 2, 14, 0, 0, 0, 0, }, /* 992 */ { 10, 26, 11, 0, 0, 10, 0, }, /* 993 */ { 27, 26, 12, 0, 0, 27, 0, }, /* 994 */ { 10, 24, 3, 0, 0, 10, 0, }, /* 995 */ { 10, 1, 3, 0, 0, 10, 0, }, /* 996 */ }; const uint16_t PRIV(ucd_stage1)[] = { /* 17408 bytes */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */ 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, /* U+2000 */ 77, 77, 78, 79, 66, 66, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, /* U+2800 */ 90, 91, 92, 93, 94, 95, 96, 97, 98, 98, 98, 98, 98, 98, 98, 98, /* U+3000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+3800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+4000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 98, 98, 98, 98, /* U+4800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+5000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+5800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+6000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+6800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+7000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+7800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+8000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+8800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+9000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+9800 */ 100,101,101,101,101,101,101,101,101,102,103,103,104,105,106,107, /* U+A000 */ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,116, /* U+A800 */ 117,118,119,120,121,122,116,117,118,119,120,121,122,116,117,118, /* U+B000 */ 119,120,121,122,116,117,118,119,120,121,122,116,117,118,119,120, /* U+B800 */ 121,122,116,117,118,119,120,121,122,116,117,118,119,120,121,122, /* U+C000 */ 116,117,118,119,120,121,122,116,117,118,119,120,121,122,116,117, /* U+C800 */ 118,119,120,121,122,116,117,118,119,120,121,122,116,117,118,123, /* U+D000 */ 124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, /* U+D800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+E000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+E800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F000 */ 125,125, 98, 98,126,127,128,129,130,130,131,132,133,134,135,136, /* U+F800 */ 137,138,139,140,141,142,143,144,145,146,147,148,149,149,150,151, /* U+10000 */ 152,153,154,155,156,157,158,159,160,161,162,141,163,164,165,166, /* U+10800 */ 167,168,169,170,171,172,173,141,174,175,141,176,177,178,179,141, /* U+11000 */ 180,181,182,183,184,185,141,141,186,187,188,189,141,190,141,191, /* U+11800 */ 192,192,192,192,192,192,192,193,194,192,195,141,141,141,141,141, /* U+12000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,196, /* U+12800 */ 197,197,197,197,197,197,197,197,198,141,141,141,141,141,141,141, /* U+13000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+13800 */ 141,141,141,141,141,141,141,141,199,199,199,199,200,141,141,141, /* U+14000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+14800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+15000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+15800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+16000 */ 201,201,201,201,202,203,204,205,141,141,141,141,206,207,208,209, /* U+16800 */ 210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210, /* U+17000 */ 210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210, /* U+17800 */ 210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,211, /* U+18000 */ 210,210,210,210,210,210,212,212,212,213,214,141,141,141,141,141, /* U+18800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+19000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+19800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+1A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,215, /* U+1A800 */ 216,217,218,219,219,220,141,141,141,141,141,141,141,141,141,141, /* U+1B000 */ 141,141,141,141,141,141,141,141,221,222,141,141,141,141,141,141, /* U+1B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+1C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,223,224, /* U+1C800 */ 71,225,226,227,228,229,230,141,231,232,233,234,235,236,237,238, /* U+1D000 */ 239,239,239,239,240,241,141,141,141,141,141,141,141,141,242,141, /* U+1D800 */ 243,141,244,141,141,245,141,141,141,141,141,141,141,141,141,246, /* U+1E000 */ 247,248,249,141,141,141,141,141,250,251,252,141,253,254,141,141, /* U+1E800 */ 255,256,257,258,259,260,261,262,261,261,263,261,264,265,266,267, /* U+1F000 */ 268,269,270,261,271,272, 71,273,260,260,260,260,260,260,260,274, /* U+1F800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+20000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+20800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+21000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+21800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+22000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+22800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+23000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+23800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+24000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+24800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+25000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+25800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+26000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+26800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+27000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+27800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+28000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+28800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+29000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+29800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,275, 98, 98, /* U+2A000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2A800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,276, 98, /* U+2B000 */ 277, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2B800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2C000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,278, 98, 98, /* U+2C800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2D000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2D800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+2E000 */ 98, 98, 98, 98, 98, 98, 98,279,141,141,141,141,141,141,141,141, /* U+2E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+2F000 */ 98, 98, 98, 98,280,141,141,141,141,141,141,141,141,141,141,141, /* U+2F800 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+30000 */ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, /* U+30800 */ 98, 98, 98, 98, 98, 98,281,141,141,141,141,141,141,141,141,141, /* U+31000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+31800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+32000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+32800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+33000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+33800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+34000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+34800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+35000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+35800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+36000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+36800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+37000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+37800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+38000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+38800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+39000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+39800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+3F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+40000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+40800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+41000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+41800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+42000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+42800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+43000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+43800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+44000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+44800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+45000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+45800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+46000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+46800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+47000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+47800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+48000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+48800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+49000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+49800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+4F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+50000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+50800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+51000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+51800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+52000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+52800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+53000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+53800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+54000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+54800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+55000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+55800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+56000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+56800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+57000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+57800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+58000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+58800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+59000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+59800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+5F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+60000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+60800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+61000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+61800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+62000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+62800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+63000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+63800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+64000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+64800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+65000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+65800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+66000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+66800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+67000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+67800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+68000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+68800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+69000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+69800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+6F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+70000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+70800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+71000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+71800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+72000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+72800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+73000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+73800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+74000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+74800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+75000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+75800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+76000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+76800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+77000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+77800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+78000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+78800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+79000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+79800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+7F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+80000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+80800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+81000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+81800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+82000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+82800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+83000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+83800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+84000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+84800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+85000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+85800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+86000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+86800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+87000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+87800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+88000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+88800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+89000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+89800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+8F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+90000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+90800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+91000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+91800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+92000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+92800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+93000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+93800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+94000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+94800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+95000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+95800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+96000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+96800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+97000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+97800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+98000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+98800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+99000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+99800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9A000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9A800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9B000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9B800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9C000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9C800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9D000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9D800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9E000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9E800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9F000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+9F800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A0000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A0800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A1000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A1800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A2000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A2800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A3000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A3800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A4000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A4800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A5000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A5800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A6000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A6800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A7000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A7800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A8000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A8800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A9000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+A9800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AA000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AA800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AB000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AB800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AC000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AC800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AD000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AD800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AE000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AE800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AF000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+AF800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B0000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B0800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B1000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B1800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B2000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B2800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B3000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B3800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B4000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B4800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B5000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B5800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B6000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B6800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B7000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B7800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B8000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B8800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B9000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+B9800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BA000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BA800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BB000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BB800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BC000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BC800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BD000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BD800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BE000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BE800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BF000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+BF800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C0000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C0800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C1000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C1800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C2000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C2800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C3000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C3800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C4000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C4800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C5000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C5800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C6000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C6800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C7000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C7800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C8000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C8800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C9000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+C9800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CA000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CA800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CB000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CB800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CC000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CC800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CD000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CD800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CE000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CE800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CF000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+CF800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D0000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D0800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D1000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D1800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D2000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D2800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D3000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D3800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D4000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D4800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D5000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D5800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D6000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D6800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D7000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D7800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D8000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D8800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D9000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+D9800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DA000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DA800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DB000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DB800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DC000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DC800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DD000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DD800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DE000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DE800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DF000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+DF800 */ 282,283,284,285,283,283,283,283,283,283,283,283,283,283,283,283, /* U+E0000 */ 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, /* U+E0800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E1000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E1800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E2000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E2800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E3000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E3800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E4000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E4800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E5000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E5800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E6000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E6800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E7000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E7800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E8000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E8800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E9000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+E9800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EA000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EA800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EB000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EB800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EC000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EC800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+ED000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+ED800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EE000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EE800 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EF000 */ 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, /* U+EF800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F0000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F0800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F1000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F1800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F2000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F2800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F3000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F3800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F4000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F4800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F5000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F5800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F6000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F6800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F7000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F7800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F8000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F8800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F9000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+F9800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FA000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FA800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FB000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FB800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FC000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FC800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FD000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FD800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FE000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FE800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+FF000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,286, /* U+FF800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+100000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+100800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+101000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+101800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+102000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+102800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+103000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+103800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+104000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+104800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+105000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+105800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+106000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+106800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+107000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+107800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+108000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+108800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+109000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+109800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10A000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10A800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10B000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10B800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10C000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10C800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10D000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10D800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10E000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10E800 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, /* U+10F000 */ 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,286, /* U+10F800 */ }; const uint16_t PRIV(ucd_stage2)[] = { /* 73472 bytes, block = 128 */ /* block 0 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 5, 5, 6, 5, 5, 5, 7, 8, 5, 9, 5, 10, 5, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 5, 5, 9, 9, 9, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 12, 12, 12, 12, 12, 12, 12, 14, 12, 12, 12, 12, 12, 12, 12, 7, 5, 8, 15, 16, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 17, 17, 17, 17, 17, 17, 17, 19, 17, 17, 17, 17, 17, 17, 17, 7, 9, 8, 9, 1, /* block 1 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 6, 6, 6, 20, 5, 15, 21, 22, 23, 9, 24, 21, 15, 20, 9, 25, 25, 15, 26, 5, 5, 15, 25, 22, 27, 25, 25, 25, 5, 12, 12, 12, 12, 12, 28, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 12, 12, 12, 12, 12, 12, 12, 29, 17, 17, 17, 17, 17, 30, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 9, 17, 17, 17, 17, 17, 17, 17, 31, /* block 2 */ 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 34, 35, 32, 33, 32, 33, 32, 33, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 36, 32, 33, 32, 33, 32, 33, 37, /* block 3 */ 38, 39, 32, 33, 32, 33, 40, 32, 33, 41, 41, 32, 33, 35, 42, 43, 44, 32, 33, 41, 45, 46, 47, 48, 32, 33, 49, 35, 47, 50, 51, 52, 32, 33, 32, 33, 32, 33, 53, 32, 33, 53, 35, 35, 32, 33, 53, 32, 33, 54, 54, 32, 33, 32, 33, 55, 32, 33, 35, 22, 32, 33, 35, 56, 22, 22, 22, 22, 57, 58, 59, 60, 61, 62, 63, 64, 65, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 66, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 35, 67, 68, 69, 32, 33, 70, 71, 32, 33, 32, 33, 32, 33, 32, 33, /* block 4 */ 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 72, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 35, 35, 35, 35, 35, 35, 73, 32, 33, 74, 75, 76, 76, 32, 33, 77, 78, 79, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 80, 81, 82, 83, 84, 35, 85, 85, 35, 86, 35, 87, 88, 35, 35, 35, 85, 89, 35, 90, 35, 91, 92, 35, 93, 94, 92, 95, 96, 35, 35, 94, 35, 97, 98, 35, 35, 99, 35, 35, 35, 35, 35, 35, 35,100, 35, 35, /* block 5 */ 101, 35,102,101, 35, 35, 35,103,101,104,105,105,106, 35, 35, 35, 35, 35,107, 35, 22, 35, 35, 35, 35, 35, 35, 35, 35,108,109, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 110,110,110,110,110,110,110,110,110,111,111,111,111,111,111,111, 111,111, 15, 15, 15, 15,111,111,111,111,111,111,111,111,111,111, 111,111, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 110,110,110,110,110, 15, 15, 15, 15, 15,112,112,111, 15,111, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, /* block 6 */ 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,114,113,113,115,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,116,116,116,116,116,116,116,116,116,116,116,116,116, 117,118,117,118,111,119,117,118,120,120,121,122,122,122, 5,123, /* block 7 */ 120,120,120,120,119, 15,124, 5,125,125,125,120,126,120,127,127, 128,129,130,129,129,131,129,129,132,133,134,129,135,129,129,129, 136,137,120,138,129,129,139,129,129,140,129,129,141,142,142,142, 128,143,144,143,143,145,143,143,146,147,148,143,149,143,143,143, 150,151,152,153,143,143,154,143,143,155,143,143,156,157,157,158, 159,160,161,161,161,162,163,164,117,118,117,118,117,118,117,118, 117,118,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 167,168,169,170,171,172,173,117,118,174,117,118,128,175,175,175, /* block 8 */ 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, 177,177,178,177,179,177,177,177,177,177,177,177,177,177,180,177, 177,181,182,177,177,177,177,177,177,177,183,177,177,177,177,177, 184,184,185,184,186,184,184,184,184,184,184,184,184,184,187,184, 184,188,189,184,184,184,184,184,184,184,190,184,184,184,184,184, 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, 192,193,194,195,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, /* block 9 */ 192,193,196,197,198,199,199,198,200,200,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 201,192,193,192,193,192,193,192,193,192,193,192,193,192,193,202, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, /* block 10 */ 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 120,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203, 203,203,203,203,203,203,203,203,203,203,203,203,203,203,203,203, 203,203,203,203,203,203,203,120,120,204,205,205,205,205,205,205, 206,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207, 207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207, /* block 11 */ 207,207,207,207,207,207,207,206,206,205,208,120,120,209,209,210, 120,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211, 211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211, 211,211,211,211,211,211,211,211,211,211,211,211,211,211,212,211, 213,211,211,213,211,211,213,211,120,120,120,120,120,120,120,120, 214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214, 214,214,214,214,214,214,214,214,214,214,214,120,120,120,120,214, 214,214,214,213,213,120,120,120,120,120,120,120,120,120,120,120, /* block 12 */ 215,215,215,215,215,216,217,217,217,218,218,219,220,218,221,221, 222,222,222,222,222,222,222,222,222,222,222,220,223,218,218,224, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 226,225,225,225,225,225,225,225,225,225,225,227,227,227,227,227, 227,227,227,227,227,227,222,222,222,222,222,222,222,222,222,222, 228,228,228,228,228,228,228,228,228,228,218,218,218,218,225,225, 227,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 13 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,229,225,222,222,222,222,222,222,222,216,221,222, 222,222,222,222,222,230,230,222,222,221,222,222,222,222,225,225, 231,231,231,231,231,231,231,231,231,231,225,225,225,221,221,225, /* block 14 */ 232,232,232,232,232,232,232,232,232,232,232,232,232,232,120,233, 234,235,234,234,234,234,234,234,234,234,234,234,234,234,234,234, 234,234,234,234,234,234,234,234,234,234,234,234,234,234,234,234, 235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235, 235,235,235,235,235,235,235,235,235,235,235,120,120,234,234,234, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 15 */ 236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, 236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, 236,236,236,236,236,236,237,237,237,237,237,237,237,237,237,237, 237,236,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 238,238,238,238,238,238,238,238,238,238,239,239,239,239,239,239, 239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239, 239,239,239,239,239,239,239,239,239,239,239,240,240,240,240,240, 240,240,240,240,241,241,242,243,243,243,241,120,120,240,244,244, /* block 16 */ 245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, 245,245,245,245,245,245,246,246,246,246,247,246,246,246,246,246, 246,246,246,246,247,246,246,246,247,246,246,246,246,246,120,120, 248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,120, 249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, 249,249,249,249,249,249,249,249,249,250,250,250,120,120,251,120, 234,234,234,234,234,234,234,234,234,234,234,120,120,120,120,120, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 17 */ 225,225,225,225,225,225,225,225,252,225,225,225,225,225,225,120, 215,215,120,120,120,120,120,120,222,222,222,222,222,222,222,222, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,230,222,222,222,222,222,222, 222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,222, 222,222,216,222,222,222,222,222,222,222,222,222,222,222,222,222, 222,222,222,222,222,222,222,222,222,222,222,222,222,222,222,222, /* block 18 */ 253,253,253,254,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,253,254,253,255,254,254, 254,253,253,253,253,253,253,253,253,254,254,254,254,253,254,254, 255,256,257,113,113,253,253,253,255,255,255,255,255,255,255,255, 255,255,253,253,258,259,260,260,260,260,260,260,260,260,260,260, 261,262,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* block 19 */ 263,264,265,265,120,263,263,263,263,263,263,263,263,120,120,263, 263,120,120,263,263,263,263,263,263,263,263,263,263,263,263,263, 263,263,263,263,263,263,263,263,263,120,263,263,263,263,263,263, 263,120,263,120,120,120,263,263,263,263,120,120,264,263,266,265, 265,264,264,264,264,120,120,265,265,120,120,265,265,264,263,120, 120,120,120,120,120,120,120,266,120,120,120,120,263,263,120,263, 263,263,264,264,120,120,267,267,267,267,267,267,267,267,267,267, 263,263,268,268,269,269,269,269,269,269,270,268,263,271,264,120, /* block 20 */ 120,272,272,273,120,274,274,274,274,274,274,120,120,120,120,274, 274,120,120,274,274,274,274,274,274,274,274,274,274,274,274,274, 274,274,274,274,274,274,274,274,274,120,274,274,274,274,274,274, 274,120,274,274,120,274,274,120,274,274,120,120,272,120,273,273, 273,272,272,120,120,120,120,272,272,120,120,272,272,272,120,120, 120,272,120,120,120,120,120,120,120,274,274,274,274,120,274,120, 120,120,120,120,120,120,275,275,275,275,275,275,275,275,275,275, 272,272,274,274,274,272,276,120,120,120,120,120,120,120,120,120, /* block 21 */ 120,277,277,278,120,279,279,279,279,279,279,279,279,279,120,279, 279,279,120,279,279,279,279,279,279,279,279,279,279,279,279,279, 279,279,279,279,279,279,279,279,279,120,279,279,279,279,279,279, 279,120,279,279,120,279,279,279,279,279,120,120,277,279,278,278, 278,277,277,277,277,277,120,277,277,278,120,278,278,277,120,120, 279,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 279,279,277,277,120,120,280,280,280,280,280,280,280,280,280,280, 281,282,120,120,120,120,120,120,120,279,277,277,277,277,277,277, /* block 22 */ 120,283,284,284,120,285,285,285,285,285,285,285,285,120,120,285, 285,120,120,285,285,285,285,285,285,285,285,285,285,285,285,285, 285,285,285,285,285,285,285,285,285,120,285,285,285,285,285,285, 285,120,285,285,120,285,285,285,285,285,120,120,283,285,286,283, 284,283,283,283,283,120,120,284,284,120,120,284,284,283,120,120, 120,120,120,120,120,283,283,286,120,120,120,120,285,285,120,285, 285,285,283,283,120,120,287,287,287,287,287,287,287,287,287,287, 288,285,289,289,289,289,289,289,120,120,120,120,120,120,120,120, /* block 23 */ 120,120,290,291,120,291,291,291,291,291,291,120,120,120,291,291, 291,120,291,291,291,291,120,120,120,291,291,120,291,120,291,291, 120,120,120,291,291,120,120,120,291,291,291,120,120,120,291,291, 291,291,291,291,291,291,291,291,291,291,120,120,120,120,292,293, 290,293,293,120,120,120,293,293,293,120,293,293,293,290,120,120, 291,120,120,120,120,120,120,292,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,294,294,294,294,294,294,294,294,294,294, 295,295,295,296,297,297,297,297,297,298,297,120,120,120,120,120, /* block 24 */ 299,300,300,300,299,301,301,301,301,301,301,301,301,120,301,301, 301,120,301,301,301,301,301,301,301,301,301,301,301,301,301,301, 301,301,301,301,301,301,301,301,301,120,301,301,301,301,301,301, 301,301,301,301,301,301,301,301,301,301,120,120,299,301,299,299, 299,300,300,300,300,120,299,299,299,120,299,299,299,299,120,120, 120,120,120,120,120,299,299,120,301,301,301,120,120,301,120,120, 301,301,299,299,120,120,302,302,302,302,302,302,302,302,302,302, 120,120,120,120,120,120,120,303,304,304,304,304,304,304,304,305, /* block 25 */ 306,307,308,308,309,306,306,306,306,306,306,306,306,120,306,306, 306,120,306,306,306,306,306,306,306,306,306,306,306,306,306,306, 306,306,306,306,306,306,306,306,306,120,306,306,306,306,306,306, 306,306,306,306,120,306,306,306,306,306,120,120,307,306,308,307, 308,308,310,308,308,120,307,308,308,120,308,308,307,307,120,120, 120,120,120,120,120,310,310,120,120,120,120,120,120,306,306,120, 306,306,307,307,120,120,311,311,311,311,311,311,311,311,311,311, 120,306,306,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 26 */ 312,312,313,313,314,314,314,314,314,314,314,314,314,120,314,314, 314,120,314,314,314,314,314,314,314,314,314,314,314,314,314,314, 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, 314,314,314,314,314,314,314,314,314,314,314,312,312,314,315,313, 313,312,312,312,312,120,313,313,313,120,313,313,313,312,316,317, 120,120,120,120,314,314,314,315,318,318,318,318,318,318,318,314, 314,314,312,312,120,120,319,319,319,319,319,319,319,319,319,319, 318,318,318,318,318,318,318,318,318,317,314,314,314,314,314,314, /* block 27 */ 120,320,321,321,120,322,322,322,322,322,322,322,322,322,322,322, 322,322,322,322,322,322,322,120,120,120,322,322,322,322,322,322, 322,322,322,322,322,322,322,322,322,322,322,322,322,322,322,322, 322,322,120,322,322,322,322,322,322,322,322,322,120,322,120,120, 322,322,322,322,322,322,322,120,120,120,320,120,120,120,120,323, 321,321,320,320,320,120,320,120,321,321,321,321,321,321,321,323, 120,120,120,120,120,120,324,324,324,324,324,324,324,324,324,324, 120,120,321,321,325,120,120,120,120,120,120,120,120,120,120,120, /* block 28 */ 120,326,326,326,326,326,326,326,326,326,326,326,326,326,326,326, 326,326,326,326,326,326,326,326,326,326,326,326,326,326,326,326, 326,326,326,326,326,326,326,326,326,326,326,326,326,326,326,326, 326,327,326,328,327,327,327,327,327,327,327,120,120,120,120, 6, 326,326,326,326,326,326,329,327,327,327,327,327,327,327,327,330, 331,331,331,331,331,331,331,331,331,331,330,330,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 29 */ 120,332,332,120,332,120,332,332,332,332,332,120,332,332,332,332, 332,332,332,332,332,332,332,332,332,332,332,332,332,332,332,332, 332,332,332,332,120,332,120,332,332,332,332,332,332,332,332,332, 332,333,332,334,333,333,333,333,333,333,333,333,333,332,120,120, 332,332,332,332,332,120,335,120,333,333,333,333,333,333,120,120, 336,336,336,336,336,336,336,336,336,336,120,120,332,332,332,332, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 30 */ 337,338,338,338,339,339,339,339,339,339,339,339,339,339,339,339, 339,339,339,338,339,338,338,338,340,340,338,338,338,338,338,338, 341,341,341,341,341,341,341,341,341,341,342,342,342,342,342,342, 342,342,342,342,338,340,338,340,338,340,343,344,343,344,345,345, 337,337,337,337,337,337,337,337,120,337,337,337,337,337,337,337, 337,337,337,337,337,337,337,337,337,337,337,337,337,337,337,337, 337,337,337,337,337,337,337,337,337,337,337,337,337,120,120,120, 120,340,340,340,340,340,340,340,340,340,340,340,340,340,340,345, /* block 31 */ 340,340,340,340,340,339,340,340,337,337,337,337,337,340,340,340, 340,340,340,340,340,340,340,340,120,340,340,340,340,340,340,340, 340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340, 340,340,340,340,340,340,340,340,340,340,340,340,340,120,338,338, 338,338,338,338,338,338,340,338,338,338,338,338,338,120,338,338, 339,339,339,339,339, 20, 20, 20, 20,339,339,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 32 */ 346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346, 346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346, 346,346,346,346,346,346,346,346,346,346,346,347,347,348,348,348, 348,349,348,348,348,348,348,348,347,348,348,349,349,348,348,346, 350,350,350,350,350,350,350,350,350,350,351,351,351,351,351,351, 346,346,346,346,346,346,349,349,348,348,346,346,346,346,348,348, 348,346,347,347,347,346,346,347,347,347,347,347,347,347,346,346, 346,348,348,348,348,346,346,346,346,346,346,346,346,346,346,346, /* block 33 */ 346,346,348,347,349,348,348,347,347,347,347,347,347,348,346,347, 352,352,352,352,352,352,352,352,352,352,347,347,347,348,353,353, 354,354,354,354,354,354,354,354,354,354,354,354,354,354,354,354, 354,354,354,354,354,354,354,354,354,354,354,354,354,354,354,354, 354,354,354,354,354,354,120,354,120,120,120,120,120,354,120,120, 355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,355, 355,355,355,355,355,355,355,355,355,355,355,355,355,355,355,355, 355,355,355,355,355,355,355,355,355,355,355,356,357,355,355,355, /* block 34 */ 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, 359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, /* block 35 */ 359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, 359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, 359,359,359,359,359,359,359,359,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, /* block 36 */ 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,120,361,361,361,361,120,120, 361,361,361,361,361,361,361,120,361,120,361,361,361,361,120,120, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, /* block 37 */ 361,361,361,361,361,361,361,361,361,120,361,361,361,361,120,120, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,120,361,361,361,361,120,120,361,361,361,361,361,361,361,120, 361,120,361,361,361,361,120,120,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, /* block 38 */ 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,120,361,361,361,361,120,120,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,361,361,361,361,120,120,362,362,362, 363,363,363,363,363,363,363,363,363,364,364,364,364,364,364,364, 364,364,364,364,364,364,364,364,364,364,364,364,364,120,120,120, /* block 39 */ 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 365,365,365,365,365,365,365,365,365,365,120,120,120,120,120,120, 366,366,366,366,366,366,366,366,366,366,366,366,366,366,366,366, 366,366,366,366,366,366,366,366,366,366,366,366,366,366,366,366, 366,366,366,366,366,366,366,366,366,366,366,366,366,366,366,366, 366,366,366,366,366,366,366,366,366,366,366,366,366,366,366,366, 366,366,366,366,366,366,366,366,366,366,366,366,366,366,366,366, 367,367,367,367,367,367,120,120,368,368,368,368,368,368,120,120, /* block 40 */ 369,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, /* block 41 */ 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, /* block 42 */ 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,371,372,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, /* block 43 */ 373,374,374,374,374,374,374,374,374,374,374,374,374,374,374,374, 374,374,374,374,374,374,374,374,374,374,374,375,376,120,120,120, 377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377, 377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377, 377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377, 377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377, 377,377,377,377,377,377,377,377,377,377,377, 5, 5, 5,378,378, 378,377,377,377,377,377,377,377,377,120,120,120,120,120,120,120, /* block 44 */ 379,379,379,379,379,379,379,379,379,379,379,379,379,379,379,379, 379,379,380,380,380,381,120,120,120,120,120,120,120,120,120,379, 382,382,382,382,382,382,382,382,382,382,382,382,382,382,382,382, 382,382,383,383,384,385,385,120,120,120,120,120,120,120,120,120, 386,386,386,386,386,386,386,386,386,386,386,386,386,386,386,386, 386,386,387,387,120,120,120,120,120,120,120,120,120,120,120,120, 388,388,388,388,388,388,388,388,388,388,388,388,388,120,388,388, 388,120,389,389,120,120,120,120,120,120,120,120,120,120,120,120, /* block 45 */ 390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390, 390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390, 390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390, 390,390,390,390,391,391,392,391,391,391,391,391,391,391,392,392, 392,392,392,392,392,392,391,392,392,391,391,391,391,391,391,391, 391,391,391,391,393,393,393,394,393,393,393,395,390,391,120,120, 396,396,396,396,396,396,396,396,396,396,120,120,120,120,120,120, 397,397,397,397,397,397,397,397,397,397,120,120,120,120,120,120, /* block 46 */ 398,398,399,399,398,399,400,398,398,398,398,401,401,401,402,401, 403,403,403,403,403,403,403,403,403,403,120,120,120,120,120,120, 404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,405,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,120,120,120,120,120,120,120, /* block 47 */ 404,404,404,404,404,401,401,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, 404,404,404,404,404,404,404,404,404,401,404,120,120,120,120,120, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 370,370,370,370,370,370,120,120,120,120,120,120,120,120,120,120, /* block 48 */ 406,406,406,406,406,406,406,406,406,406,406,406,406,406,406,406, 406,406,406,406,406,406,406,406,406,406,406,406,406,406,406,120, 407,407,407,408,408,408,408,407,407,408,408,408,120,120,120,120, 408,408,407,408,408,408,408,408,408,407,407,407,120,120,120,120, 409,120,120,120,410,410,411,411,411,411,411,411,411,411,411,411, 412,412,412,412,412,412,412,412,412,412,412,412,412,412,412,412, 412,412,412,412,412,412,412,412,412,412,412,412,412,412,120,120, 412,412,412,412,412,120,120,120,120,120,120,120,120,120,120,120, /* block 49 */ 413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, 413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, 413,413,413,413,413,413,413,413,413,413,413,413,120,120,120,120, 413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, 413,413,413,413,413,413,413,413,413,413,120,120,120,120,120,120, 414,414,414,414,414,414,414,414,414,414,415,120,120,120,416,416, 417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417, 417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417, /* block 50 */ 418,418,418,418,418,418,418,418,418,418,418,418,418,418,418,418, 418,418,418,418,418,418,418,419,419,420,420,419,120,120,421,421, 422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, 422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, 422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, 422,422,422,422,422,423,424,423,424,424,424,424,424,424,424,120, 424,425,424,425,425,424,424,424,424,424,424,424,424,423,423,423, 423,423,423,424,424,424,424,424,424,424,424,424,424,120,120,424, /* block 51 */ 426,426,426,426,426,426,426,426,426,426,120,120,120,120,120,120, 426,426,426,426,426,426,426,426,426,426,120,120,120,120,120,120, 427,427,427,427,427,427,427,428,427,427,427,427,427,427,120,120, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,429,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 52 */ 430,430,430,430,431,432,432,432,432,432,432,432,432,432,432,432, 432,432,432,432,432,432,432,432,432,432,432,432,432,432,432,432, 432,432,432,432,432,432,432,432,432,432,432,432,432,432,432,432, 432,432,432,432,430,433,430,430,430,430,430,431,430,431,431,431, 431,431,430,431,431,432,432,432,432,432,432,432,432,120,120,120, 434,434,434,434,434,434,434,434,434,434,435,435,435,435,435,435, 435,436,436,436,436,436,436,436,436,436,436,430,430,430,430,430, 430,430,430,430,436,436,436,436,436,436,436,436,436,435,435,120, /* block 53 */ 437,437,438,439,439,439,439,439,439,439,439,439,439,439,439,439, 439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439, 439,438,437,437,437,437,438,438,437,437,438,437,437,437,439,439, 440,440,440,440,440,440,440,440,440,440,439,439,439,439,439,439, 441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, 441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, 441,441,441,441,441,441,442,443,442,442,443,443,443,442,443,442, 442,442,443,443,120,120,120,120,120,120,120,120,444,444,444,444, /* block 54 */ 445,445,445,445,445,445,445,445,445,445,445,445,445,445,445,445, 445,445,445,445,445,445,445,445,445,445,445,445,445,445,445,445, 445,445,445,445,446,446,446,446,446,446,446,446,447,447,447,447, 447,447,447,447,446,446,447,447,120,120,120,448,448,448,448,448, 449,449,449,449,449,449,449,449,449,449,120,120,120,445,445,445, 450,450,450,450,450,450,450,450,450,450,451,451,451,451,451,451, 451,451,451,451,451,451,451,451,451,451,451,451,451,451,451,451, 451,451,451,451,451,451,451,451,452,452,452,452,452,452,453,453, /* block 55 */ 454,455,456,457,458,459,460,461,462,120,120,120,120,120,120,120, 463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, 463,463,463,463,463,463,463,463,463,463,463,463,463,463,463,463, 463,463,463,463,463,463,463,463,463,463,463,120,120,463,463,463, 464,464,464,464,464,464,464,464,120,120,120,120,120,120,120,120, 465,466,465,467,466,468,468,469,468,469,470,466,469,469,466,466, 469,471,466,466,466,466,466,466,466,472,473,474,474,468,474,474, 474,474,475,476,477,473,473,478,479,479,480,120,120,120,120,120, /* block 56 */ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,128,128,128,128,128,481,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,121,121,121, 121,121,110,110,110,110,121,121,121,121,121, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,482,483, 35, 35, 35,484, 35, 35, /* block 57 */ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,485, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,121, 114,114,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,486,113,487,113,113,113,113,113, /* block 58 */ 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 488,489, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, /* block 59 */ 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 35, 35, 35, 35, 35,490, 35, 35,491, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, /* block 60 */ 492,492,492,492,492,492,492,492,493,493,493,493,493,493,493,493, 492,492,492,492,492,492,120,120,493,493,493,493,493,493,120,120, 492,492,492,492,492,492,492,492,493,493,493,493,493,493,493,493, 492,492,492,492,492,492,492,492,493,493,493,493,493,493,493,493, 492,492,492,492,492,492,120,120,493,493,493,493,493,493,120,120, 128,492,128,492,128,492,128,492,120,493,120,493,120,493,120,493, 492,492,492,492,492,492,492,492,493,493,493,493,493,493,493,493, 494,494,495,495,495,495,496,496,497,497,498,498,499,499,120,120, /* block 61 */ 492,492,492,492,492,492,492,492,500,500,500,500,500,500,500,500, 492,492,492,492,492,492,492,492,500,500,500,500,500,500,500,500, 492,492,492,492,492,492,492,492,500,500,500,500,500,500,500,500, 492,492,128,501,128,120,128,128,493,493,502,502,503,119,504,119, 119,119,128,501,128,120,128,128,505,505,505,505,503,119,119,119, 492,492,128,128,120,120,128,128,493,493,506,506,120,119,119,119, 492,492,128,128,128,169,128,128,493,493,507,507,174,119,119,119, 120,120,128,501,128,120,128,128,508,508,509,509,503,119,119,120, /* block 62 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24,510,511, 24, 24, 10, 10, 10, 10, 10, 10, 5, 5, 23, 27, 7, 23, 23, 27, 7, 23, 5, 5, 5, 5, 5, 5, 5, 5,512,513, 24, 24, 24, 24, 24,514, 5, 5, 5, 5, 5, 5, 5, 5, 5, 23, 27, 5,515, 5, 5, 16, 16, 5, 5, 5, 9, 7, 8, 5, 5,515, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, 16, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 24, 24, 24, 24, 24,516, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25,110,120,120, 25, 25, 25, 25, 25, 25, 9, 9, 9, 7, 8,110, /* block 63 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 9, 9, 9, 7, 8,120, 110,110,110,110,110,110,110,110,110,110,110,110,110,120,120,120, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 113,113,113,113,113,113,113,113,113,113,113,113,113,429,429,429, 429,113,429,429,429,113,113,113,113,113,113,113,113,113,113,113, 517,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 64 */ 20, 20,518, 20, 20, 20, 20,518, 20, 20,519,518,518,518,519,519, 518,518,518,519, 20,518, 20, 20, 9,518,518,518,518,518, 20, 20, 20, 20, 21, 20,518, 20,520, 20,518, 20,521,522,518,518, 20,519, 518,518,523,518,519,524,524,524,524,525, 20, 20,519,519,518,518, 9, 9, 9, 9, 9,518,519,519,519,519, 20, 9, 20, 20,526, 20, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 527,527,527,527,527,527,527,527,527,527,527,527,527,527,527,527, 528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528, /* block 65 */ 529,529,529, 32, 33,529,529,529,529, 25, 20, 20,120,120,120,120, 9, 9, 9, 9,530, 21, 21, 21, 21, 21, 9, 9, 20, 20, 20, 20, 9, 20, 20, 9, 20, 20, 9, 20, 20, 21, 21, 20, 20, 20, 9, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 20, 20, 9, 20, 9, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, /* block 66 */ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, /* block 67 */ 20, 20, 20, 20, 20, 20, 20, 20, 7, 8, 7, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 20, 20, 20, 20, 9, 9, 20, 20, 20, 20, 20, 20, 21, 7, 8, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 20, 20, 20, /* block 68 */ 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 9, 9, 9, 9, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 21, 21, 21, 20, 20, 20, 20, 20, /* block 69 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* block 70 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,531,531,531,531,531,531,531,531,531,531, 531,531,532,531,531,531,531,531,531,531,531,531,531,531,531,531, 533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533, 533,533,533,533,533,533,533,533,533,533, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* block 71 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 72 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 9, 20, 20, 20, 20, 20, 20, 20, 20, 21, 9, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 9,530,530,530,530, 9, /* block 73 */ 21, 21, 21, 21, 21, 21, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,530, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 74 */ 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 75 */ 21, 21, 21, 21, 21, 21, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 21, 20, 21, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 21, 20, 20, 20, 20, 21, 20, 21, 20, 20, 20, 20, 21, 21, 21, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* block 76 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 20, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 9, 9, 9, 9, 9, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, /* block 77 */ 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, /* block 78 */ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,530,530, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, /* block 79 */ 9, 9, 9, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, 9, 9, /* block 80 */ 20, 20, 20, 20, 20, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 20, 20, 9, 9, 9, 9, 9, 9, 20, 20, 20, 21, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 81 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 82 */ 535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, 535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, 535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, 536,536,536,536,536,536,536,536,536,536,536,536,536,536,536,536, 536,536,536,536,536,536,536,536,536,536,536,536,536,536,536,536, 536,536,536,536,536,536,536,536,536,536,536,536,536,536,536,536, 32, 33,537,538,539,540,541, 32, 33, 32, 33, 32, 33,542,543,544, 545, 35, 32, 33, 35, 32, 33, 35, 35, 35, 35, 35,110,110,546,546, /* block 83 */ 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,165,166,165,166,165,166,165,166,165,166,165,166, 165,166,165,166,547,548,548,548,548,548,548,165,166,165,166,549, 549,549,165,166,120,120,120,120,120,550,550,550,550,551,550,550, /* block 84 */ 552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, 552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552, 552,552,552,552,552,552,120,552,120,120,120,120,120,552,120,120, 553,553,553,553,553,553,553,553,553,553,553,553,553,553,553,553, 553,553,553,553,553,553,553,553,553,553,553,553,553,553,553,553, 553,553,553,553,553,553,553,553,553,553,553,553,553,553,553,553, 553,553,553,553,553,553,553,553,120,120,120,120,120,120,120,554, 555,120,120,120,120,120,120,120,120,120,120,120,120,120,120,556, /* block 85 */ 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,361, 361,361,361,361,361,361,361,120,120,120,120,120,120,120,120,120, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,120, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,120, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,120, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,120, 557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, 557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557, /* block 86 */ 5, 5, 23, 27, 23, 27, 5, 5, 5, 23, 27, 5, 23, 27, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 23, 27, 5, 5, 23, 27, 7, 8, 7, 8, 7, 8, 7, 8, 5, 5, 5, 5, 5,111, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 7,558, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 7, 8, 7, 8, 7, 8, 7, 8, 10,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 87 */ 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,120,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,120,120,120,120,120,120,120,120,120,120,120,120, /* block 88 */ 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, /* block 89 */ 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559, 559,559,559,559,559,559,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120, /* block 90 */ 4,560,560,561, 20,562,563,564,565,566,565,566,565,566,565,566, 565,566, 20,567,565,566,565,566,565,566,565,566,568,569,570,570, 20,564,564,564,564,564,564,564,564,564,571,571,571,571,572,572, 573,574,574,574,574,574, 20,567,564,564,564,562,575,576,577,577, 120,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, /* block 91 */ 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,120,120,579,579,580,580,581,581,578, 582,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,560,574,584,584,583, /* block 92 */ 120,120,120,120,120,585,585,585,585,585,585,585,585,585,585,585, 585,585,585,585,585,585,585,585,585,585,585,585,585,585,585,585, 585,585,585,585,585,585,585,585,585,585,585,585,585,585,585,585, 120,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, /* block 93 */ 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,120, 577,577,587,587,587,587,577,577,577,577,577,577,577,577,577,577, 585,585,585,585,585,585,585,585,585,585,585,585,585,585,585,585, 585,585,585,585,585,585,585,585,585,585,585,585,585,585,585,585, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577,577,577,577,120,120,120,120,120,120,120,120,120,120,120,120, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, /* block 94 */ 588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588, 588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,120, 587,587,587,587,587,587,587,587,587,587,577,577,577,577,577,577, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577,577,577,577,577,577,577,577, 25, 25, 25, 25, 25, 25, 25, 25, 20, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 588,588,588,588,588,588,588,588,588,588,588,588,588,588,588,588, 588,588,588,588,588,588,588,588,588,588,588,588,588,588,588, 20, /* block 95 */ 587,587,587,587,587,587,587,587,587,587,577,577,577,577,577,577, 577,577,577,577,577,577,577,589,577,589,577,577,577,577,577,577, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 577,577,577,577,577,577,577,577,577,577,577,577, 20, 20, 20, 20, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,577, /* block 96 */ 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,590,590,590,590,590,590,590,590, 590,590,590,590,590,590,590,590,577,577,577,577,577,577,577,577, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,577,577,577,577,577, /* block 97 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, 20, /* block 98 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, /* block 99 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 100 */ 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,593,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, /* block 101 */ 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, 592,592,592,592,592,592,592,592,592,592,592,592,592,592,592,592, /* block 102 */ 592,592,592,592,592,592,592,592,592,592,592,592,592,120,120,120, 594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594, 594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594, 594,594,594,594,594,594,594,594,594,594,594,594,594,594,594,594, 594,594,594,594,594,594,594,120,120,120,120,120,120,120,120,120, 595,595,595,595,595,595,595,595,595,595,595,595,595,595,595,595, 595,595,595,595,595,595,595,595,595,595,595,595,595,595,595,595, 595,595,595,595,595,595,595,595,596,596,596,596,596,596,597,597, /* block 103 */ 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, /* block 104 */ 598,598,598,598,598,598,598,598,598,598,598,598,599,600,600,600, 598,598,598,598,598,598,598,598,598,598,598,598,598,598,598,598, 601,601,601,601,601,601,601,601,601,601,598,598,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 192,193,192,193,192,193,192,193,192,193,602,603,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,192,193,604,198, 200,200,200,605,557,557,557,557,557,557,557,557,557,557,605,482, /* block 105 */ 192,193,192,193,192,193,192,193,192,193,192,193,192,193,192,193, 192,193,192,193,192,193,192,193,192,193,192,193,482,482,557,557, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,607,607,607,607,607,607,607,607,607,607, 608,608,609,609,609,609,609,609,120,120,120,120,120,120,120,120, /* block 106 */ 610,610,610,610,610,610,610,610, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,111,111,111,111,111,111,111,111,111, 15, 15, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 35, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 110, 35, 35, 35, 35, 35, 35, 35, 35, 32, 33, 32, 33,611, 32, 33, /* block 107 */ 32, 33, 32, 33, 32, 33, 32, 33,111, 15, 15, 32, 33,612, 35, 22, 32, 33, 32, 33,613, 35, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33,614,615,616,617,614, 35, 618,619,620,621, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33, 32, 33,622,623,624, 32, 33, 32, 33,120,120,120,120,120, 32, 33,120, 35,120, 35, 32, 33, 32, 33,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,110,110,110, 32, 33, 22,110,110, 35, 22, 22, 22, 22, 22, /* block 108 */ 625,625,626,625,625,625,626,625,625,625,625,626,625,625,625,625, 625,625,625,625,625,625,625,625,625,625,625,625,625,625,625,625, 625,625,625,627,627,626,626,627,628,628,628,628,626,120,120,120, 629,629,629,630,630,630,631,631,632,631,120,120,120,120,120,120, 633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,633, 633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,633, 633,633,633,633,633,633,633,633,633,633,633,633,633,633,633,633, 633,633,633,633,634,634,634,634,120,120,120,120,120,120,120,120, /* block 109 */ 635,635,636,636,636,636,636,636,636,636,636,636,636,636,636,636, 636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636, 636,636,636,636,636,636,636,636,636,636,636,636,636,636,636,636, 636,636,636,636,635,635,635,635,635,635,635,635,635,635,635,635, 635,635,635,635,637,637,120,120,120,120,120,120,120,120,638,638, 639,639,639,639,639,639,639,639,639,639,120,120,120,120,120,120, 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, 253,640,255,641,255,255,255,255,261,261,261,255,261,255,255,253, /* block 110 */ 642,642,642,642,642,642,642,642,642,642,643,643,643,643,643,643, 643,643,643,643,643,643,643,643,643,643,643,643,643,643,643,643, 643,643,643,643,643,643,644,644,644,644,644,644,644,644,645,646, 647,647,647,647,647,647,647,647,647,647,647,647,647,647,647,647, 647,647,647,647,647,647,647,648,648,648,648,648,648,648,648,648, 648,648,649,649,120,120,120,120,120,120,120,120,120,120,120,650, 358,358,358,358,358,358,358,358,358,358,358,358,358,358,358,358, 358,358,358,358,358,358,358,358,358,358,358,358,358,120,120,120, /* block 111 */ 651,651,651,652,653,653,653,653,653,653,653,653,653,653,653,653, 653,653,653,653,653,653,653,653,653,653,653,653,653,653,653,653, 653,653,653,653,653,653,653,653,653,653,653,653,653,653,653,653, 653,653,653,651,652,652,651,651,651,651,652,652,651,651,652,652, 652,654,654,654,654,654,654,654,654,654,654,654,654,654,120,655, 656,656,656,656,656,656,656,656,656,656,120,120,120,120,654,654, 346,346,346,346,346,348,657,346,346,346,346,346,346,346,346,346, 352,352,352,352,352,352,352,352,352,352,346,346,346,346,346,120, /* block 112 */ 658,658,658,658,658,658,658,658,658,658,658,658,658,658,658,658, 658,658,658,658,658,658,658,658,658,658,658,658,658,658,658,658, 658,658,658,658,658,658,658,658,658,659,659,659,659,659,659,660, 660,659,659,660,660,659,659,120,120,120,120,120,120,120,120,120, 658,658,658,659,658,658,658,658,658,658,658,658,659,660,120,120, 661,661,661,661,661,661,661,661,661,661,120,120,662,662,662,662, 346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346, 657,346,346,346,346,346,346,353,353,353,346,347,348,347,346,346, /* block 113 */ 663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663, 663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663, 663,663,663,663,663,663,663,663,663,663,663,663,663,663,663,663, 664,663,664,664,664,663,663,664,664,663,663,663,663,663,664,664, 663,664,663,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,663,663,665,666,666, 667,667,667,667,667,667,667,667,667,667,667,668,669,669,668,668, 670,670,667,671,671,668,669,120,120,120,120,120,120,120,120,120, /* block 114 */ 120,361,361,361,361,361,361,120,120,361,361,361,361,361,361,120, 120,361,361,361,361,361,361,120,120,120,120,120,120,120,120,120, 361,361,361,361,361,361,361,120,361,361,361,361,361,361,361,120, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,672, 35, 35, 35, 35, 35, 35, 35, 15,110,110,110,110, 35, 35, 35, 35, 35,128, 35, 35, 35,110, 15, 15,120,120,120,120, 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, /* block 115 */ 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, 667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667, 667,667,667,667,667,667,667,667,667,667,667,667,667,667,667,667, 667,667,667,668,668,669,668,668,669,668,668,670,668,669,120,120, 674,674,674,674,674,674,674,674,674,674,120,120,120,120,120,120, /* block 116 */ 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, /* block 117 */ 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, /* block 118 */ 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, /* block 119 */ 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, /* block 120 */ 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, /* block 121 */ 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, /* block 122 */ 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 675,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,675,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,675,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, /* block 123 */ 676,676,676,676,676,676,676,676,675,676,676,676,676,676,676,676, 676,676,676,676,676,676,676,676,676,676,676,676,676,676,676,676, 676,676,676,676,120,120,120,120,120,120,120,120,120,120,120,120, 359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359, 359,359,359,359,359,359,359,120,120,120,120,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,360,360,360,360, 360,360,360,360,360,360,360,360,360,360,360,360,120,120,120,120, /* block 124 */ 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, 677,677,677,677,677,677,677,677,677,677,677,677,677,677,677,677, /* block 125 */ 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, /* block 126 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,120,120, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, /* block 127 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 128 */ 35, 35, 35, 35, 35, 35, 35,120,120,120,120,120,120,120,120,120, 120,120,120,206,206,206,206,206,120,120,120,120,120,214,211,214, 214,214,214,214,214,214,214,214,214,679,214,214,214,214,214,214, 214,214,214,214,214,214,214,120,214,214,214,214,214,120,214,120, 214,214,120,214,214,120,214,214,214,214,214,214,214,214,214,214, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 129 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,252,252,252,252,252,252,252,252,252,252,252,252,252,252, 252,252,252,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 130 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 131 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,680,681, 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, /* block 132 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 120,120,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,120,120,120,120,120,120,120,221, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 225,225,682,225,225,225,225,225,225,225,225,225,219,683,221,221, /* block 133 */ 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 5, 5, 5, 5, 5, 5, 5, 7, 8, 5,120,120,120,120,120,120, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,557,557, 5, 10, 10, 16, 16, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,561,561, 7, 8, 5, 5, 5, 5, 16, 16, 16, 5, 5, 5,120, 5, 5, 5, 5, 10, 7, 8, 7, 8, 7, 8, 5, 5, 5, 9, 10, 9, 9, 9,120, 5, 6, 5, 5,120,120,120,120, 225,225,225,225,225,120,225,225,225,225,225,225,225,225,225,225, /* block 134 */ 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,120,120, 24, /* block 135 */ 120, 5, 5, 5, 6, 5, 5, 5, 7, 8, 5, 9, 5, 10, 5, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 5, 5, 9, 9, 9, 5, 5, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 5, 8, 15, 16, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 7, 9, 8, 9, 7, 8,560,565,566,560,560,583,583,583,583,583,583,583,583,583,583, 574,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, /* block 136 */ 583,583,583,583,583,583,583,583,583,583,583,583,583,583,583,583, 583,583,583,583,583,583,583,583,583,583,583,583,583,583,684,684, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,586, 586,586,586,586,586,586,586,586,586,586,586,586,586,586,586,120, 120,120,586,586,586,586,586,586,120,120,586,586,586,586,586,586, 120,120,586,586,586,586,586,586,120,120,586,586,586,120,120,120, 6, 6, 9, 15, 20, 6, 6,120, 20, 9, 9, 9, 9, 20, 20,120, 516,516,516,516,516,516,516,516,516, 24, 24, 24, 20, 20,120,120, /* block 137 */ 685,685,685,685,685,685,685,685,685,685,685,685,120,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,120,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,120,685,685,120,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,120,120, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 138 */ 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685, 685,685,685,685,685,685,685,685,685,685,685,120,120,120,120,120, /* block 139 */ 686,686,687,120,120,120,120,688,688,688,688,688,688,688,688,688, 688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, 688,688,688,688,688,688,688,688,688,688,688,688,688,688,688,688, 688,688,688,688,120,120,120,689,689,689,689,689,689,689,689,689, 690,690,690,690,690,690,690,690,690,690,690,690,690,690,690,690, 690,690,690,690,690,690,690,690,690,690,690,690,690,690,690,690, 690,690,690,690,690,690,690,690,690,690,690,690,690,690,690,690, 690,690,690,690,690,691,691,691,691,692,692,692,692,692,692,692, /* block 140 */ 692,692,692,692,692,692,692,692,692,692,691,691,692,692,692,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120, 692,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,113,120,120, /* block 141 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 142 */ 693,693,693,693,693,693,693,693,693,693,693,693,693,693,693,693, 693,693,693,693,693,693,693,693,693,693,693,693,693,120,120,120, 694,694,694,694,694,694,694,694,694,694,694,694,694,694,694,694, 694,694,694,694,694,694,694,694,694,694,694,694,694,694,694,694, 694,694,694,694,694,694,694,694,694,694,694,694,694,694,694,694, 694,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 695,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696, 696,696,696,696,696,696,696,696,696,696,696,696,120,120,120,120, /* block 143 */ 697,697,697,697,697,697,697,697,697,697,697,697,697,697,697,697, 697,697,697,697,697,697,697,697,697,697,697,697,697,697,697,697, 698,698,698,698,120,120,120,120,120,120,120,120,120,697,697,697, 699,699,699,699,699,699,699,699,699,699,699,699,699,699,699,699, 699,700,699,699,699,699,699,699,699,699,700,120,120,120,120,120, 701,701,701,701,701,701,701,701,701,701,701,701,701,701,701,701, 701,701,701,701,701,701,701,701,701,701,701,701,701,701,701,701, 701,701,701,701,701,701,702,702,702,702,702,120,120,120,120,120, /* block 144 */ 703,703,703,703,703,703,703,703,703,703,703,703,703,703,703,703, 703,703,703,703,703,703,703,703,703,703,703,703,703,703,120,704, 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, 705,705,705,705,120,120,120,120,705,705,705,705,705,705,705,705, 706,707,707,707,707,707,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 145 */ 708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, 708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708, 708,708,708,708,708,708,708,708,709,709,709,709,709,709,709,709, 709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, 709,709,709,709,709,709,709,709,709,709,709,709,709,709,709,709, 710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710, 710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710, 710,710,710,710,710,710,710,710,710,710,710,710,710,710,710,710, /* block 146 */ 711,711,711,711,711,711,711,711,711,711,711,711,711,711,711,711, 711,711,711,711,711,711,711,711,711,711,711,711,711,711,120,120, 712,712,712,712,712,712,712,712,712,712,120,120,120,120,120,120, 713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713, 713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713, 713,713,713,713,120,120,120,120,714,714,714,714,714,714,714,714, 714,714,714,714,714,714,714,714,714,714,714,714,714,714,714,714, 714,714,714,714,714,714,714,714,714,714,714,714,120,120,120,120, /* block 147 */ 715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, 715,715,715,715,715,715,715,715,715,715,715,715,715,715,715,715, 715,715,715,715,715,715,715,715,120,120,120,120,120,120,120,120, 716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,716, 716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,716, 716,716,716,716,716,716,716,716,716,716,716,716,716,716,716,716, 716,716,716,716,120,120,120,120,120,120,120,120,120,120,120,717, 718,718,718,718,718,718,718,718,718,718,718,120,718,718,718,718, /* block 148 */ 718,718,718,718,718,718,718,718,718,718,718,120,718,718,718,718, 718,718,718,120,718,718,120,719,719,719,719,719,719,719,719,719, 719,719,120,719,719,719,719,719,719,719,719,719,719,719,719,719, 719,719,120,719,719,719,719,719,719,719,120,719,719,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 149 */ 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, /* block 150 */ 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,720,120,120,120,120,120,120,120,120,120, 720,720,720,720,720,720,720,720,720,720,720,720,720,720,720,720, 720,720,720,720,720,720,120,120,120,120,120,120,120,120,120,120, 720,720,720,720,720,720,720,720,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 151 */ 110,110,110,110,110,110,120,110,110,110,110,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, 110,120,110,110,110,110,110,110,110,110,110,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 152 */ 721,721,721,721,721,721,120,120,721,120,721,721,721,721,721,721, 721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,721, 721,721,721,721,721,721,721,721,721,721,721,721,721,721,721,721, 721,721,721,721,721,721,120,721,721,120,120,120,721,120,120,721, 722,722,722,722,722,722,722,722,722,722,722,722,722,722,722,722, 722,722,722,722,722,722,120,723,724,724,724,724,724,724,724,724, 725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725, 725,725,725,725,725,725,725,726,726,727,727,727,727,727,727,727, /* block 153 */ 728,728,728,728,728,728,728,728,728,728,728,728,728,728,728,728, 728,728,728,728,728,728,728,728,728,728,728,728,728,728,728,120, 120,120,120,120,120,120,120,729,729,729,729,729,729,729,729,729, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 730,730,730,730,730,730,730,730,730,730,730,730,730,730,730,730, 730,730,730,120,730,730,120,120,120,120,120,731,731,731,731,731, /* block 154 */ 732,732,732,732,732,732,732,732,732,732,732,732,732,732,732,732, 732,732,732,732,732,732,733,733,733,733,733,733,120,120,120,734, 735,735,735,735,735,735,735,735,735,735,735,735,735,735,735,735, 735,735,735,735,735,735,735,735,735,735,120,120,120,120,120,736, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 155 */ 737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, 737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, 738,738,738,738,738,738,738,738,738,738,738,738,738,738,738,738, 738,738,738,738,738,738,738,738,120,120,120,120,739,739,738,738, 739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, 120,120,739,739,739,739,739,739,739,739,739,739,739,739,739,739, 739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, 739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739, /* block 156 */ 740,741,741,741,120,741,741,120,120,120,120,120,741,741,741,741, 740,740,740,740,120,740,740,740,120,740,740,740,740,740,740,740, 740,740,740,740,740,740,740,740,740,740,740,740,740,740,740,740, 740,740,740,740,740,740,120,120,741,741,741,120,120,120,120,741, 742,742,742,742,742,742,742,742,742,120,120,120,120,120,120,120, 743,743,743,743,743,743,743,743,743,120,120,120,120,120,120,120, 744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744, 744,744,744,744,744,744,744,744,744,744,744,744,744,745,745,746, /* block 157 */ 747,747,747,747,747,747,747,747,747,747,747,747,747,747,747,747, 747,747,747,747,747,747,747,747,747,747,747,747,747,748,748,748, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 749,749,749,749,749,749,749,749,750,749,749,749,749,749,749,749, 749,749,749,749,749,749,749,749,749,749,749,749,749,749,749,749, 749,749,749,749,749,751,751,120,120,120,120,752,752,752,752,752, 753,753,754,753,753,753,753,120,120,120,120,120,120,120,120,120, /* block 158 */ 755,755,755,755,755,755,755,755,755,755,755,755,755,755,755,755, 755,755,755,755,755,755,755,755,755,755,755,755,755,755,755,755, 755,755,755,755,755,755,755,755,755,755,755,755,755,755,755,755, 755,755,755,755,755,755,120,120,120,756,756,756,756,756,756,756, 757,757,757,757,757,757,757,757,757,757,757,757,757,757,757,757, 757,757,757,757,757,757,120,120,758,758,758,758,758,758,758,758, 759,759,759,759,759,759,759,759,759,759,759,759,759,759,759,759, 759,759,759,120,120,120,120,120,760,760,760,760,760,760,760,760, /* block 159 */ 761,761,761,761,761,761,761,761,761,761,761,761,761,761,761,761, 761,761,120,120,120,120,120,120,120,762,762,762,762,120,120,120, 120,120,120,120,120,120,120,120,120,763,763,763,763,763,763,763, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 160 */ 764,764,764,764,764,764,764,764,764,764,764,764,764,764,764,764, 764,764,764,764,764,764,764,764,764,764,764,764,764,764,764,764, 764,764,764,764,764,764,764,764,764,764,764,764,764,764,764,764, 764,764,764,764,764,764,764,764,764,764,764,764,764,764,764,764, 764,764,764,764,764,764,764,764,764,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 161 */ 765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765, 765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765, 765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765, 765,765,765,120,120,120,120,120,120,120,120,120,120,120,120,120, 766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766, 766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766, 766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766, 766,766,766,120,120,120,120,120,120,120,767,767,767,767,767,767, /* block 162 */ 768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,768, 768,768,768,768,768,768,768,768,768,768,768,768,768,768,768,768, 768,768,768,768,769,769,769,769,120,120,120,120,120,120,120,120, 770,770,770,770,770,770,770,770,770,770,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 163 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 771,771,771,771,771,771,771,771,771,771,771,771,771,771,771,771, 771,771,771,771,771,771,771,771,771,771,771,771,771,771,771,120, /* block 164 */ 772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772, 772,772,772,772,772,772,772,772,772,772,772,772,772,772,772,772, 772,772,772,772,772,772,772,772,772,772,120,773,773,774,120,120, 772,772,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 165 */ 775,775,775,775,775,775,775,775,775,775,775,775,775,775,775,775, 775,775,775,775,775,775,775,775,775,775,775,775,775,776,776,776, 776,776,776,776,776,776,776,775,120,120,120,120,120,120,120,120, 777,777,777,777,777,777,777,777,777,777,777,777,777,777,777,777, 777,777,777,777,777,777,778,778,778,778,778,778,778,778,778,778, 778,779,779,779,779,780,780,780,780,780,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781, /* block 166 */ 781,781,782,782,782,782,783,783,783,783,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 784,784,784,784,784,784,784,784,784,784,784,784,784,784,784,784, 784,784,784,784,784,785,785,785,785,785,785,785,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 786,786,786,786,786,786,786,786,786,786,786,786,786,786,786,786, 786,786,786,786,786,786,786,120,120,120,120,120,120,120,120,120, /* block 167 */ 787,788,787,789,789,789,789,789,789,789,789,789,789,789,789,789, 789,789,789,789,789,789,789,789,789,789,789,789,789,789,789,789, 789,789,789,789,789,789,789,789,789,789,789,789,789,789,789,789, 789,789,789,789,789,789,789,789,788,788,788,788,788,788,788,788, 788,788,788,788,788,788,788,790,790,790,790,790,790,790,120,120, 120,120,791,791,791,791,791,791,791,791,791,791,791,791,791,791, 791,791,791,791,791,791,792,792,792,792,792,792,792,792,792,792, 788,789,789,788,788,789,120,120,120,120,120,120,120,120,120,788, /* block 168 */ 793,793,794,795,795,795,795,795,795,795,795,795,795,795,795,795, 795,795,795,795,795,795,795,795,795,795,795,795,795,795,795,795, 795,795,795,795,795,795,795,795,795,795,795,795,795,795,795,795, 794,794,794,793,793,793,793,794,794,793,793,796,796,797,796,796, 796,796,793,120,120,120,120,120,120,120,120,120,120,797,120,120, 798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798, 798,798,798,798,798,798,798,798,798,120,120,120,120,120,120,120, 799,799,799,799,799,799,799,799,799,799,120,120,120,120,120,120, /* block 169 */ 800,800,800,801,801,801,801,801,801,801,801,801,801,801,801,801, 801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801, 801,801,801,801,801,801,801,800,800,800,800,800,802,800,800,800, 800,800,800,800,800,120,803,803,803,803,803,803,803,803,803,803, 804,804,804,804,801,802,802,801,120,120,120,120,120,120,120,120, 805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805, 805,805,805,805,805,805,805,805,805,805,805,805,805,805,805,805, 805,805,805,806,807,807,805,120,120,120,120,120,120,120,120,120, /* block 170 */ 808,808,809,810,810,810,810,810,810,810,810,810,810,810,810,810, 810,810,810,810,810,810,810,810,810,810,810,810,810,810,810,810, 810,810,810,810,810,810,810,810,810,810,810,810,810,810,810,810, 810,810,810,809,809,809,808,808,808,808,808,808,808,808,808,809, 809,810,811,811,810,812,812,812,812,808,808,808,808,812,809,808, 813,813,813,813,813,813,813,813,813,813,810,812,810,812,812,812, 120,814,814,814,814,814,814,814,814,814,814,814,814,814,814,814, 814,814,814,814,814,120,120,120,120,120,120,120,120,120,120,120, /* block 171 */ 815,815,815,815,815,815,815,815,815,815,815,815,815,815,815,815, 815,815,120,815,815,815,815,815,815,815,815,815,815,815,815,815, 815,815,815,815,815,815,815,815,815,815,815,815,816,816,816,817, 817,817,816,816,817,816,817,817,818,818,818,818,818,818,817,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 172 */ 819,819,819,819,819,819,819,120,819,120,819,819,819,819,120,819, 819,819,819,819,819,819,819,819,819,819,819,819,819,819,120,819, 819,819,819,819,819,819,819,819,819,820,120,120,120,120,120,120, 821,821,821,821,821,821,821,821,821,821,821,821,821,821,821,821, 821,821,821,821,821,821,821,821,821,821,821,821,821,821,821,821, 821,821,821,821,821,821,821,821,821,821,821,821,821,821,821,822, 823,823,823,822,822,822,822,822,822,822,822,120,120,120,120,120, 824,824,824,824,824,824,824,824,824,824,120,120,120,120,120,120, /* block 173 */ 825,826,827,828,120,829,829,829,829,829,829,829,829,120,120,829, 829,120,120,829,829,829,829,829,829,829,829,829,829,829,829,829, 829,829,829,829,829,829,829,829,829,120,829,829,829,829,829,829, 829,120,829,829,120,829,829,829,829,829,120,830,826,829,831,827, 825,827,827,827,827,120,120,827,827,120,120,827,827,827,120,120, 829,120,120,120,120,120,120,831,120,120,120,120,120,829,829,829, 829,829,827,827,120,120,825,825,825,825,825,825,825,120,120,120, 825,825,825,825,825,120,120,120,120,120,120,120,120,120,120,120, /* block 174 */ 832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,832, 832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,832, 832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,832, 832,832,832,832,832,833,833,833,834,834,834,834,834,834,834,834, 833,833,834,834,834,833,834,832,832,832,832,835,835,835,835,835, 836,836,836,836,836,836,836,836,836,836,835,835,120,835,834,832, 832,832,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 175 */ 837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837, 837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837, 837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837, 838,839,839,840,840,840,840,840,840,839,840,839,839,838,839,840, 840,839,840,840,837,837,841,837,120,120,120,120,120,120,120,120, 842,842,842,842,842,842,842,842,842,842,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 176 */ 843,843,843,843,843,843,843,843,843,843,843,843,843,843,843,843, 843,843,843,843,843,843,843,843,843,843,843,843,843,843,843,843, 843,843,843,843,843,843,843,843,843,843,843,843,843,843,843,844, 845,845,846,846,846,846,120,120,845,845,845,845,846,846,845,846, 846,847,847,847,847,847,847,847,847,847,847,847,847,847,847,847, 847,847,847,847,847,847,847,847,843,843,843,843,846,846,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 177 */ 848,848,848,848,848,848,848,848,848,848,848,848,848,848,848,848, 848,848,848,848,848,848,848,848,848,848,848,848,848,848,848,848, 848,848,848,848,848,848,848,848,848,848,848,848,848,848,848,848, 849,849,849,850,850,850,850,850,850,850,850,849,849,850,849,850, 850,851,851,851,848,120,120,120,120,120,120,120,120,120,120,120, 852,852,852,852,852,852,852,852,852,852,120,120,120,120,120,120, 398,398,398,398,398,398,398,398,398,398,398,398,398,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 178 */ 853,853,853,853,853,853,853,853,853,853,853,853,853,853,853,853, 853,853,853,853,853,853,853,853,853,853,853,853,853,853,853,853, 853,853,853,853,853,853,853,853,853,853,853,854,855,854,855,855, 854,854,854,854,854,854,855,854,853,856,120,120,120,120,120,120, 857,857,857,857,857,857,857,857,857,857,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 179 */ 858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858, 858,858,858,858,858,858,858,858,858,858,858,120,120,859,859,859, 860,860,859,859,859,859,861,859,859,859,859,859,120,120,120,120, 862,862,862,862,862,862,862,862,862,862,863,863,864,864,864,865, 858,858,858,858,858,858,858,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 180 */ 866,866,866,866,866,866,866,866,866,866,866,866,866,866,866,866, 866,866,866,866,866,866,866,866,866,866,866,866,866,866,866,866, 866,866,866,866,866,866,866,866,866,866,866,866,867,867,867,868, 868,868,868,868,868,868,868,868,867,868,868,869,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 181 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 870,870,870,870,870,870,870,870,870,870,870,870,870,870,870,870, 870,870,870,870,870,870,870,870,870,870,870,870,870,870,870,870, 871,871,871,871,871,871,871,871,871,871,871,871,871,871,871,871, 871,871,871,871,871,871,871,871,871,871,871,871,871,871,871,871, 872,872,872,872,872,872,872,872,872,872,873,873,873,873,873,873, 873,873,873,120,120,120,120,120,120,120,120,120,120,120,120,874, /* block 182 */ 875,875,875,875,875,875,875,120,120,875,120,120,875,875,875,875, 875,875,875,875,120,875,875,120,875,875,875,875,875,875,875,875, 875,875,875,875,875,875,875,875,875,875,875,875,875,875,875,875, 876,877,877,877,877,877,120,877,877,120,120,878,878,877,878,879, 877,879,877,878,880,880,880,120,120,120,120,120,120,120,120,120, 881,881,881,881,881,881,881,881,881,881,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 183 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 882,882,882,882,882,882,882,882,120,120,882,882,882,882,882,882, 882,882,882,882,882,882,882,882,882,882,882,882,882,882,882,882, 882,882,882,882,882,882,882,882,882,882,882,882,882,882,882,882, 882,883,883,883,884,884,884,884,120,120,884,884,883,883,883,883, 884,882,885,882,883,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 184 */ 886,887,887,887,887,887,887,887,887,887,887,886,886,886,886,886, 886,886,886,886,886,886,886,886,886,886,886,886,886,886,886,886, 886,886,886,886,886,886,886,886,886,886,886,886,886,886,886,886, 886,886,886,887,887,887,887,887,887,888,889,887,887,887,887,890, 890,890,890,890,890,890,890,887,120,120,120,120,120,120,120,120, 891,892,892,892,892,892,892,893,893,892,892,892,891,891,891,891, 891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891, 891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891, /* block 185 */ 891,891,891,891,894,894,894,894,894,894,892,892,892,892,892,892, 892,892,892,892,892,892,892,893,892,892,895,895,895,891,895,895, 895,895,895,120,120,120,120,120,120,120,120,120,120,120,120,120, 370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896, 896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896, 896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896, 896,896,896,896,896,896,896,896,896,120,120,120,120,120,120,120, /* block 186 */ 897,897,897,897,897,897,897,897,897,120,897,897,897,897,897,897, 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,898, 899,899,899,899,899,899,899,120,899,899,899,899,899,899,898,899, 897,900,900,900,900,900,120,120,120,120,120,120,120,120,120,120, 901,901,901,901,901,901,901,901,901,901,902,902,902,902,902,902, 902,902,902,902,902,902,902,902,902,902,902,902,902,120,120,120, 903,903,904,904,904,904,904,904,904,904,904,904,904,904,904,904, /* block 187 */ 904,904,904,904,904,904,904,904,904,904,904,904,904,904,904,904, 120,120,905,905,905,905,905,905,905,905,905,905,905,905,905,905, 905,905,905,905,905,905,905,905,120,906,905,905,905,905,905,905, 905,906,905,905,906,905,905,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 188 */ 907,907,907,907,907,907,907,120,907,907,120,907,907,907,907,907, 907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,907, 907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,907, 907,908,908,908,908,908,908,120,120,120,908,120,908,908,120,908, 908,908,908,908,908,908,909,908,120,120,120,120,120,120,120,120, 910,910,910,910,910,910,910,910,910,910,120,120,120,120,120,120, 911,911,911,911,911,911,120,911,911,120,911,911,911,911,911,911, 911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911, /* block 189 */ 911,911,911,911,911,911,911,911,911,911,912,912,912,912,912,120, 913,913,120,912,912,913,912,913,911,120,120,120,120,120,120,120, 914,914,914,914,914,914,914,914,914,914,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 190 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,915, 915,915,915,916,916,917,917,918,918,120,120,120,120,120,120,120, /* block 191 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 595,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 919,919,919,919,919,919,919,919,919,919,919,919,919,919,919,919, 295,295,919,295,919,297,297,297,297,297,297,297,297,298,298,298, 298,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297, 297,297,120,120,120,120,120,120,120,120,120,120,120,120,120,920, /* block 192 */ 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, /* block 193 */ 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 194 */ 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,922, 922,922,922,922,922,922,922,922,922,922,922,922,922,922,922,120, 923,923,923,923,923,120,120,120,120,120,120,120,120,120,120,120, /* block 195 */ 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,921,921,921,921,921,921,921,921,921,921,921,921, 921,921,921,921,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 196 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924, 924,925,925,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 197 */ 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, /* block 198 */ 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,926, 926,926,926,926,926,926,926,926,926,926,926,926,926,926,926,120, 927,927,927,927,927,927,927,927,927,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 199 */ 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, /* block 200 */ 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,928,928,928,928,928,928,928,928,928, 928,928,928,928,928,928,928,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 201 */ 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, /* block 202 */ 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606, 606,606,606,606,606,606,606,606,606,120,120,120,120,120,120,120, 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,120, 930,930,930,930,930,930,930,930,930,930,120,120,120,120,931,931, 932,932,932,932,932,932,932,932,932,932,932,932,932,932,932,932, /* block 203 */ 932,932,932,932,932,932,932,932,932,932,932,932,932,932,932,932, 932,932,932,932,932,932,932,932,932,932,932,932,932,932,932,932, 932,932,932,932,932,932,932,932,932,932,932,932,932,932,932,932, 932,932,932,932,932,932,932,932,932,932,932,932,932,932,932,120, 933,933,933,933,933,933,933,933,933,933,120,120,120,120,120,120, 934,934,934,934,934,934,934,934,934,934,934,934,934,934,934,934, 934,934,934,934,934,934,934,934,934,934,934,934,934,934,120,120, 935,935,935,935,935,936,120,120,120,120,120,120,120,120,120,120, /* block 204 */ 937,937,937,937,937,937,937,937,937,937,937,937,937,937,937,937, 937,937,937,937,937,937,937,937,937,937,937,937,937,937,937,937, 937,937,937,937,937,937,937,937,937,937,937,937,937,937,937,937, 938,938,938,938,938,938,938,939,939,939,939,939,940,940,940,940, 941,941,941,941,939,940,120,120,120,120,120,120,120,120,120,120, 942,942,942,942,942,942,942,942,942,942,120,943,943,943,943,943, 943,943,120,937,937,937,937,937,937,937,937,937,937,937,937,937, 937,937,937,937,937,937,937,937,120,120,120,120,120,937,937,937, /* block 205 */ 937,937,937,937,937,937,937,937,937,937,937,937,937,937,937,937, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 206 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 944,944,944,944,944,944,944,944,944,944,944,944,944,944,944,944, 944,944,944,944,944,944,944,944,944,944,944,944,944,944,944,944, 945,945,945,945,945,945,945,945,945,945,945,945,945,945,945,945, 945,945,945,945,945,945,945,945,945,945,945,945,945,945,945,945, /* block 207 */ 946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946, 946,946,946,946,946,946,946,947,947,947,947,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 208 */ 948,948,948,948,948,948,948,948,948,948,948,948,948,948,948,948, 948,948,948,948,948,948,948,948,948,948,948,948,948,948,948,948, 948,948,948,948,948,948,948,948,948,948,948,948,948,948,948,948, 948,948,948,948,948,948,948,948,948,948,948,948,948,948,948,948, 948,948,948,948,948,948,948,948,948,948,948,120,120,120,120,949, 948,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950, 950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950, 950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950, /* block 209 */ 950,950,950,950,950,950,950,950,120,120,120,120,120,120,120,949, 949,949,949,951,951,951,951,951,951,951,951,951,951,951,951,951, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 952,953,954,562,955,120,120,120,120,120,120,120,120,120,120,120, 956,956,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 210 */ 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, /* block 211 */ 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,957,957,957,957,957,957,957,957, 957,957,957,957,957,957,957,957,120,120,120,120,120,120,120,120, /* block 212 */ 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, /* block 213 */ 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958, 958,958,958,958,958,958,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 214 */ 957,957,957,957,957,957,957,957,957,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 215 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 584,584,584,584,120,584,584,584,584,584,584,584,120,584,584,120, /* block 216 */ 583,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, /* block 217 */ 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, /* block 218 */ 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 578,578,578,578,578,578,578,578,578,578,578,578,578,578,578,578, 583,583,583,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 578,578,578,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,583,583,583,583,120,120,120,120,120,120,120,120, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, /* block 219 */ 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, /* block 220 */ 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959, 959,959,959,959,959,959,959,959,959,959,959,959,120,120,120,120, /* block 221 */ 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960, 960,960,960,960,960,960,960,960,960,960,960,120,120,120,120,120, 960,960,960,960,960,960,960,960,960,960,960,960,960,120,120,120, /* block 222 */ 960,960,960,960,960,960,960,960,960,120,120,120,120,120,120,120, 960,960,960,960,960,960,960,960,960,960,120,120,961,962,962,963, 964,964,964,964,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 223 */ 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,120,120, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,120,120,120,120,120,120,120,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 224 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 225 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120,120,120,120,120,120, /* block 226 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,965,966,113,113,113, 20, 20, 20,966,965,965, 965,965,965, 24, 24, 24, 24, 24, 24, 24, 24,113,113,113,113,113, /* block 227 */ 113,113,113, 20, 20,113,113,113,113,113,113,113, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,113,113,113,113, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 228 */ 692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,692, 692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,692, 692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,692, 692,692,692,692,692,692,692,692,692,692,692,692,692,692,692,692, 692,692,967,967,967,692,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 229 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,120,120,120,120,120,120,120,120,120,120,120,120, /* block 230 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120,120,120,120,120, 587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587, 587,587, 25, 25, 25, 25, 25, 25, 25,120,120,120,120,120,120,120, /* block 231 */ 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,519,519, 519,519,519,519,519,120,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, /* block 232 */ 518,518,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,518,120,518,518, 120,120,518,120,120,518,518,120,120,518,518,518,518,120,518,518, 518,518,518,518,518,518,519,519,519,519,120,519,120,519,519,519, 519,519,519,519,120,519,519,519,519,519,519,519,519,519,519,519, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, /* block 233 */ 519,519,519,519,518,518,120,518,518,518,518,120,120,518,518,518, 518,518,518,518,518,120,518,518,518,518,518,518,518,120,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,518,518,120,518,518,518,518,120, 518,518,518,518,518,120,518,120,120,120,518,518,518,518,518,518, 518,120,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, /* block 234 */ 518,518,518,518,518,518,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, /* block 235 */ 519,519,519,519,519,519,519,519,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, /* block 236 */ 518,518,518,518,518,518,518,518,518,518,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,120,120,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518, 9,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519, 9,519,519,519,519, 519,519,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518, 9,519,519,519,519, /* block 237 */ 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519, 9,519,519,519,519,519,519,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518, 9,519,519,519,519,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 9, 519,519,519,519,519,519,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 9, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, /* block 238 */ 519,519,519,519,519,519,519,519,519, 9,519,519,519,519,519,519, 518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518, 518,518,518,518,518,518,518,518,518, 9,519,519,519,519,519,519, 519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, 519,519,519, 9,519,519,519,519,519,519,518,519,120,120, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, /* block 239 */ 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, 968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968, /* block 240 */ 969,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 969,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 969,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 969,969,969,969,969,969,969,968,968,968,968,969,969,969,969,969, 969,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 969,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 969,969,969,969,969,969,969,969,969,969,969,969,969,968,968,968, 968,968,968,968,968,969,968,968,968,968,968,968,968,968,968,968, /* block 241 */ 968,968,968,968,969,968,968,970,970,970,970,970,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,969,969,969,969,969, 120,969,969,969,969,969,969,969,969,969,969,969,969,969,969,969, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 242 */ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 22, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 243 */ 971,971,971,971,971,971,971,120,971,971,971,971,971,971,971,971, 971,971,971,971,971,971,971,971,971,120,120,971,971,971,971,971, 971,971,120,971,971,120,971,971,971,971,971,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 244 */ 972,972,972,972,972,972,972,972,972,972,972,972,972,972,972,972, 972,972,972,972,972,972,972,972,972,972,972,972,972,972,972,972, 972,972,972,972,972,972,972,972,972,972,972,972,972,120,120,120, 973,973,973,973,973,973,973,974,974,974,974,974,974,974,120,120, 975,975,975,975,975,975,975,975,975,975,120,120,120,120,972,976, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 245 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 977,977,977,977,977,977,977,977,977,977,977,977,977,977,977,977, 977,977,977,977,977,977,977,977,977,977,977,977,977,977,978,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 979,979,979,979,979,979,979,979,979,979,979,979,979,979,979,979, 979,979,979,979,979,979,979,979,979,979,979,979,979,979,979,979, 979,979,979,979,979,979,979,979,979,979,979,979,980,980,980,980, 981,981,981,981,981,981,981,981,981,981,120,120,120,120,120,982, /* block 246 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 361,361,361,361,361,361,361,120,361,361,361,361,120,361,361,120, 361,361,361,361,361,361,361,361,361,361,361,361,361,361,361,120, /* block 247 */ 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, /* block 248 */ 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,983,983,983,983,983,983,983,983,983,983,983, 983,983,983,983,983,120,120,984,984,984,984,984,984,984,984,984, 985,985,985,985,985,985,985,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 249 */ 986,986,986,986,986,986,986,986,986,986,986,986,986,986,986,986, 986,986,986,986,986,986,986,986,986,986,986,986,986,986,986,986, 986,986,987,987,987,987,987,987,987,987,987,987,987,987,987,987, 987,987,987,987,987,987,987,987,987,987,987,987,987,987,987,987, 987,987,987,987,988,988,988,988,988,988,988,989,120,120,120,120, 990,990,990,990,990,990,990,990,990,990,120,120,120,120,991,991, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 250 */ 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* block 251 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 20, 25, 25, 25, 6, 25, 25, 25, 25,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 252 */ 120, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 20, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 253 */ 225,225,225,225,120,225,225,225,225,225,225,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, 120,225,225,120,225,120,120,225,120,225,225,225,225,225,225,225, 225,225,225,120,225,225,225,225,120,225,120,225,120,120,120,120, 120,120,225,120,120,120,120,225,120,225,120,225,120,225,225,225, 120,225,225,120,225,120,120,225,120,225,120,225,120,225,120,225, 120,225,225,120,225,120,120,225,225,225,225,120,225,225,225,225, 225,225,225,120,225,225,225,225,120,225,225,225,225,120,225,120, /* block 254 */ 225,225,225,225,225,225,225,225,225,225,120,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,120,120,120,120, 120,225,225,225,120,225,225,225,225,225,120,225,225,225,225,225, 225,225,225,225,225,225,225,225,225,225,225,225,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 217,217,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 255 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 256 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992, 992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992,992, /* block 257 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, /* block 258 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,993,993,993,993,993,993,993,993,993,993, 993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993, /* block 259 */ 994, 21, 21,992,992,992,992,992,992,992,992,992,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992,992,992,992, 589,589,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, /* block 260 */ 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, /* block 261 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 262 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,995,995,995,995,995, /* block 263 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 264 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 265 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992, /* block 266 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992,992,992,992,992,992,992,992,992, /* block 267 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992, 21,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, /* block 268 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, /* block 269 */ 20, 20, 20, 20, 20, 20, 20, 20,992,992,992,992,992,992,992,992, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,992,992, 21, 21,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, /* block 270 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* block 271 */ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992, 21, 21, 21, 21, 21,992,992,992, 21, 21, 21, 21, 21,992,992,992, /* block 272 */ 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992, 21, 21, 21, 21, 21, 21, 21,992,992,992,992,992,992,992,992,992, /* block 273 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,120,120,120,120,120,120, /* block 274 */ 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,992,992, 992,992,992,992,992,992,992,992,992,992,992,992,992,992,120,120, /* block 275 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 276 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,120,120,120,120,120,120,120, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, /* block 277 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,120,120, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, /* block 278 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, /* block 279 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 280 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 281 */ 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591, 591,591,591,591,591,591,591,591,591,591,591,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, /* block 282 */ 516, 24,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, 996,996,996,996,996,996,996,996,996,996,996,996,996,996,996,996, /* block 283 */ 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, /* block 284 */ 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, /* block 285 */ 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, 516,516,516,516,516,516,516,516,516,516,516,516,516,516,516,516, /* block 286 */ 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,678,678, 678,678,678,678,678,678,678,678,678,678,678,678,678,678,120,120, }; #if UCD_BLOCK_SIZE != 128 #error Please correct UCD_BLOCK_SIZE in pcre2_internal.h #endif #endif /* SUPPORT_UNICODE */ #endif /* PCRE2_PCRE2TEST */ vfu-4.22/vstring/pcre2/pcre2_ucp.h0000644000175000017500000001772414145574056015403 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifndef PCRE2_UCP_H_IDEMPOTENT_GUARD #define PCRE2_UCP_H_IDEMPOTENT_GUARD /* This file contains definitions of the property values that are returned by the UCD access macros. New values that are added for new releases of Unicode should always be at the end of each enum, for backwards compatibility. IMPORTANT: Note also that the specific numeric values of the enums have to be the same as the values that are generated by the maint/MultiStage2.py script, where the equivalent property descriptive names are listed in vectors. ALSO: The specific values of the first two enums are assumed for the table called catposstab in pcre2_compile.c. */ /* These are the general character categories. */ enum { ucp_C, /* Other */ ucp_L, /* Letter */ ucp_M, /* Mark */ ucp_N, /* Number */ ucp_P, /* Punctuation */ ucp_S, /* Symbol */ ucp_Z /* Separator */ }; /* These are the particular character categories. */ enum { ucp_Cc, /* Control */ ucp_Cf, /* Format */ ucp_Cn, /* Unassigned */ ucp_Co, /* Private use */ ucp_Cs, /* Surrogate */ ucp_Ll, /* Lower case letter */ ucp_Lm, /* Modifier letter */ ucp_Lo, /* Other letter */ ucp_Lt, /* Title case letter */ ucp_Lu, /* Upper case letter */ ucp_Mc, /* Spacing mark */ ucp_Me, /* Enclosing mark */ ucp_Mn, /* Non-spacing mark */ ucp_Nd, /* Decimal number */ ucp_Nl, /* Letter number */ ucp_No, /* Other number */ ucp_Pc, /* Connector punctuation */ ucp_Pd, /* Dash punctuation */ ucp_Pe, /* Close punctuation */ ucp_Pf, /* Final punctuation */ ucp_Pi, /* Initial punctuation */ ucp_Po, /* Other punctuation */ ucp_Ps, /* Open punctuation */ ucp_Sc, /* Currency symbol */ ucp_Sk, /* Modifier symbol */ ucp_Sm, /* Mathematical symbol */ ucp_So, /* Other symbol */ ucp_Zl, /* Line separator */ ucp_Zp, /* Paragraph separator */ ucp_Zs /* Space separator */ }; /* These are grapheme break properties. The Extended Pictographic property comes from the emoji-data.txt file. */ enum { ucp_gbCR, /* 0 */ ucp_gbLF, /* 1 */ ucp_gbControl, /* 2 */ ucp_gbExtend, /* 3 */ ucp_gbPrepend, /* 4 */ ucp_gbSpacingMark, /* 5 */ ucp_gbL, /* 6 Hangul syllable type L */ ucp_gbV, /* 7 Hangul syllable type V */ ucp_gbT, /* 8 Hangul syllable type T */ ucp_gbLV, /* 9 Hangul syllable type LV */ ucp_gbLVT, /* 10 Hangul syllable type LVT */ ucp_gbRegionalIndicator, /* 11 */ ucp_gbOther, /* 12 */ ucp_gbZWJ, /* 13 */ ucp_gbExtended_Pictographic /* 14 */ }; /* These are the script identifications. */ enum { ucp_Unknown, ucp_Arabic, ucp_Armenian, ucp_Bengali, ucp_Bopomofo, ucp_Braille, ucp_Buginese, ucp_Buhid, ucp_Canadian_Aboriginal, ucp_Cherokee, ucp_Common, ucp_Coptic, ucp_Cypriot, ucp_Cyrillic, ucp_Deseret, ucp_Devanagari, ucp_Ethiopic, ucp_Georgian, ucp_Glagolitic, ucp_Gothic, ucp_Greek, ucp_Gujarati, ucp_Gurmukhi, ucp_Han, ucp_Hangul, ucp_Hanunoo, ucp_Hebrew, ucp_Hiragana, ucp_Inherited, ucp_Kannada, ucp_Katakana, ucp_Kharoshthi, ucp_Khmer, ucp_Lao, ucp_Latin, ucp_Limbu, ucp_Linear_B, ucp_Malayalam, ucp_Mongolian, ucp_Myanmar, ucp_New_Tai_Lue, ucp_Ogham, ucp_Old_Italic, ucp_Old_Persian, ucp_Oriya, ucp_Osmanya, ucp_Runic, ucp_Shavian, ucp_Sinhala, ucp_Syloti_Nagri, ucp_Syriac, ucp_Tagalog, ucp_Tagbanwa, ucp_Tai_Le, ucp_Tamil, ucp_Telugu, ucp_Thaana, ucp_Thai, ucp_Tibetan, ucp_Tifinagh, ucp_Ugaritic, ucp_Yi, /* New for Unicode 5.0 */ ucp_Balinese, ucp_Cuneiform, ucp_Nko, ucp_Phags_Pa, ucp_Phoenician, /* New for Unicode 5.1 */ ucp_Carian, ucp_Cham, ucp_Kayah_Li, ucp_Lepcha, ucp_Lycian, ucp_Lydian, ucp_Ol_Chiki, ucp_Rejang, ucp_Saurashtra, ucp_Sundanese, ucp_Vai, /* New for Unicode 5.2 */ ucp_Avestan, ucp_Bamum, ucp_Egyptian_Hieroglyphs, ucp_Imperial_Aramaic, ucp_Inscriptional_Pahlavi, ucp_Inscriptional_Parthian, ucp_Javanese, ucp_Kaithi, ucp_Lisu, ucp_Meetei_Mayek, ucp_Old_South_Arabian, ucp_Old_Turkic, ucp_Samaritan, ucp_Tai_Tham, ucp_Tai_Viet, /* New for Unicode 6.0.0 */ ucp_Batak, ucp_Brahmi, ucp_Mandaic, /* New for Unicode 6.1.0 */ ucp_Chakma, ucp_Meroitic_Cursive, ucp_Meroitic_Hieroglyphs, ucp_Miao, ucp_Sharada, ucp_Sora_Sompeng, ucp_Takri, /* New for Unicode 7.0.0 */ ucp_Bassa_Vah, ucp_Caucasian_Albanian, ucp_Duployan, ucp_Elbasan, ucp_Grantha, ucp_Khojki, ucp_Khudawadi, ucp_Linear_A, ucp_Mahajani, ucp_Manichaean, ucp_Mende_Kikakui, ucp_Modi, ucp_Mro, ucp_Nabataean, ucp_Old_North_Arabian, ucp_Old_Permic, ucp_Pahawh_Hmong, ucp_Palmyrene, ucp_Psalter_Pahlavi, ucp_Pau_Cin_Hau, ucp_Siddham, ucp_Tirhuta, ucp_Warang_Citi, /* New for Unicode 8.0.0 */ ucp_Ahom, ucp_Anatolian_Hieroglyphs, ucp_Hatran, ucp_Multani, ucp_Old_Hungarian, ucp_SignWriting, /* New for Unicode 10.0.0 (no update since 8.0.0) */ ucp_Adlam, ucp_Bhaiksuki, ucp_Marchen, ucp_Newa, ucp_Osage, ucp_Tangut, ucp_Masaram_Gondi, ucp_Nushu, ucp_Soyombo, ucp_Zanabazar_Square, /* New for Unicode 11.0.0 */ ucp_Dogra, ucp_Gunjala_Gondi, ucp_Hanifi_Rohingya, ucp_Makasar, ucp_Medefaidrin, ucp_Old_Sogdian, ucp_Sogdian, /* New for Unicode 12.0.0 */ ucp_Elymaic, ucp_Nandinagari, ucp_Nyiakeng_Puachue_Hmong, ucp_Wancho, /* New for Unicode 13.0.0 */ ucp_Chorasmian, ucp_Dives_Akuru, ucp_Khitan_Small_Script, ucp_Yezidi, /* New for Unicode 14.0.0 */ ucp_Cypro_Minoan, ucp_Old_Uyghur, ucp_Tangsa, ucp_Toto, ucp_Vithkuqi }; #endif /* PCRE2_UCP_H_IDEMPOTENT_GUARD */ /* End of pcre2_ucp.h */ vfu-4.22/vstring/pcre2/pcre2_valid_utf.c0000644000175000017500000003114014145574056016550 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains an internal function for validating UTF character strings. This file is also #included by the pcre2test program, which uses macros to change names from _pcre2_xxx to xxxx, thereby avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is defined. */ #ifndef PCRE2_PCRE2TEST /* We're compiling the library */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #endif /* PCRE2_PCRE2TEST */ #ifndef SUPPORT_UNICODE /************************************************* * Dummy function when Unicode is not supported * *************************************************/ /* This function should never be called when Unicode is not supported. */ int PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset) { (void)string; (void)length; (void)erroroffset; return 0; } #else /* UTF is supported */ /************************************************* * Validate a UTF string * *************************************************/ /* This function is called (optionally) at the start of compile or match, to check that a supposed UTF string is actually valid. The early check means that subsequent code can assume it is dealing with a valid string. The check can be turned off for maximum performance, but the consequences of supplying an invalid string are then undefined. Arguments: string points to the string length length of string errp pointer to an error position offset variable Returns: == 0 if the string is a valid UTF string != 0 otherwise, setting the offset of the bad character */ int PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset) { PCRE2_SPTR p; uint32_t c; /* ----------------- Check a UTF-8 string ----------------- */ #if PCRE2_CODE_UNIT_WIDTH == 8 /* Originally, this function checked according to RFC 2279, allowing for values in the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were in the canonical format. Once somebody had pointed out RFC 3629 to me (it obsoletes 2279), additional restrictions were applied. The values are now limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the subrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte characters is still checked. Error returns are as follows: PCRE2_ERROR_UTF8_ERR1 Missing 1 byte at the end of the string PCRE2_ERROR_UTF8_ERR2 Missing 2 bytes at the end of the string PCRE2_ERROR_UTF8_ERR3 Missing 3 bytes at the end of the string PCRE2_ERROR_UTF8_ERR4 Missing 4 bytes at the end of the string PCRE2_ERROR_UTF8_ERR5 Missing 5 bytes at the end of the string PCRE2_ERROR_UTF8_ERR6 2nd-byte's two top bits are not 0x80 PCRE2_ERROR_UTF8_ERR7 3rd-byte's two top bits are not 0x80 PCRE2_ERROR_UTF8_ERR8 4th-byte's two top bits are not 0x80 PCRE2_ERROR_UTF8_ERR9 5th-byte's two top bits are not 0x80 PCRE2_ERROR_UTF8_ERR10 6th-byte's two top bits are not 0x80 PCRE2_ERROR_UTF8_ERR11 5-byte character is not permitted by RFC 3629 PCRE2_ERROR_UTF8_ERR12 6-byte character is not permitted by RFC 3629 PCRE2_ERROR_UTF8_ERR13 4-byte character with value > 0x10ffff is not permitted PCRE2_ERROR_UTF8_ERR14 3-byte character with value 0xd800-0xdfff is not permitted PCRE2_ERROR_UTF8_ERR15 Overlong 2-byte sequence PCRE2_ERROR_UTF8_ERR16 Overlong 3-byte sequence PCRE2_ERROR_UTF8_ERR17 Overlong 4-byte sequence PCRE2_ERROR_UTF8_ERR18 Overlong 5-byte sequence (won't ever occur) PCRE2_ERROR_UTF8_ERR19 Overlong 6-byte sequence (won't ever occur) PCRE2_ERROR_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character) PCRE2_ERROR_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff */ for (p = string; length > 0; p++) { uint32_t ab, d; c = *p; length--; if (c < 128) continue; /* ASCII character */ if (c < 0xc0) /* Isolated 10xx xxxx byte */ { *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR20; } if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */ { *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR21; } ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */ if (length < ab) /* Missing bytes */ { *erroroffset = (PCRE2_SIZE)(p - string); switch(ab - length) { case 1: return PCRE2_ERROR_UTF8_ERR1; case 2: return PCRE2_ERROR_UTF8_ERR2; case 3: return PCRE2_ERROR_UTF8_ERR3; case 4: return PCRE2_ERROR_UTF8_ERR4; case 5: return PCRE2_ERROR_UTF8_ERR5; } } length -= ab; /* Length remaining */ /* Check top bits in the second byte */ if (((d = *(++p)) & 0xc0) != 0x80) { *erroroffset = (int)(p - string) - 1; return PCRE2_ERROR_UTF8_ERR6; } /* For each length, check that the remaining bytes start with the 0x80 bit set and not the 0x40 bit. Then check for an overlong sequence, and for the excluded range 0xd800 to 0xdfff. */ switch (ab) { /* 2-byte character. No further bytes to check for 0x80. Check first byte for for xx00 000x (overlong sequence). */ case 1: if ((c & 0x3e) == 0) { *erroroffset = (int)(p - string) - 1; return PCRE2_ERROR_UTF8_ERR15; } break; /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes for 1110 0000, xx0x xxxx (overlong sequence) or 1110 1101, 1010 xxxx (0xd800 - 0xdfff) */ case 2: if ((*(++p) & 0xc0) != 0x80) /* Third byte */ { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR7; } if (c == 0xe0 && (d & 0x20) == 0) { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR16; } if (c == 0xed && d >= 0xa0) { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR14; } break; /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2 bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a character greater than 0x0010ffff (f4 8f bf bf) */ case 3: if ((*(++p) & 0xc0) != 0x80) /* Third byte */ { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR7; } if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ { *erroroffset = (int)(p - string) - 3; return PCRE2_ERROR_UTF8_ERR8; } if (c == 0xf0 && (d & 0x30) == 0) { *erroroffset = (int)(p - string) - 3; return PCRE2_ERROR_UTF8_ERR17; } if (c > 0xf4 || (c == 0xf4 && d > 0x8f)) { *erroroffset = (int)(p - string) - 3; return PCRE2_ERROR_UTF8_ERR13; } break; /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be rejected by the length test below. However, we do the appropriate tests here so that overlong sequences get diagnosed, and also in case there is ever an option for handling these larger code points. */ /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for 1111 1000, xx00 0xxx */ case 4: if ((*(++p) & 0xc0) != 0x80) /* Third byte */ { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR7; } if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ { *erroroffset = (int)(p - string) - 3; return PCRE2_ERROR_UTF8_ERR8; } if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ { *erroroffset = (int)(p - string) - 4; return PCRE2_ERROR_UTF8_ERR9; } if (c == 0xf8 && (d & 0x38) == 0) { *erroroffset = (int)(p - string) - 4; return PCRE2_ERROR_UTF8_ERR18; } break; /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for 1111 1100, xx00 00xx. */ case 5: if ((*(++p) & 0xc0) != 0x80) /* Third byte */ { *erroroffset = (int)(p - string) - 2; return PCRE2_ERROR_UTF8_ERR7; } if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ { *erroroffset = (int)(p - string) - 3; return PCRE2_ERROR_UTF8_ERR8; } if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ { *erroroffset = (int)(p - string) - 4; return PCRE2_ERROR_UTF8_ERR9; } if ((*(++p) & 0xc0) != 0x80) /* Sixth byte */ { *erroroffset = (int)(p - string) - 5; return PCRE2_ERROR_UTF8_ERR10; } if (c == 0xfc && (d & 0x3c) == 0) { *erroroffset = (int)(p - string) - 5; return PCRE2_ERROR_UTF8_ERR19; } break; } /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are excluded by RFC 3629. The pointer p is currently at the last byte of the character. */ if (ab > 3) { *erroroffset = (int)(p - string) - ab; return (ab == 4)? PCRE2_ERROR_UTF8_ERR11 : PCRE2_ERROR_UTF8_ERR12; } } return 0; /* ----------------- Check a UTF-16 string ----------------- */ #elif PCRE2_CODE_UNIT_WIDTH == 16 /* There's not so much work, nor so many errors, for UTF-16. PCRE2_ERROR_UTF16_ERR1 Missing low surrogate at the end of the string PCRE2_ERROR_UTF16_ERR2 Invalid low surrogate PCRE2_ERROR_UTF16_ERR3 Isolated low surrogate */ for (p = string; length > 0; p++) { c = *p; length--; if ((c & 0xf800) != 0xd800) { /* Normal UTF-16 code point. Neither high nor low surrogate. */ } else if ((c & 0x0400) == 0) { /* High surrogate. Must be a followed by a low surrogate. */ if (length == 0) { *erroroffset = p - string; return PCRE2_ERROR_UTF16_ERR1; } p++; length--; if ((*p & 0xfc00) != 0xdc00) { *erroroffset = p - string - 1; return PCRE2_ERROR_UTF16_ERR2; } } else { /* Isolated low surrogate. Always an error. */ *erroroffset = p - string; return PCRE2_ERROR_UTF16_ERR3; } } return 0; /* ----------------- Check a UTF-32 string ----------------- */ #else /* There is very little to do for a UTF-32 string. PCRE2_ERROR_UTF32_ERR1 Surrogate character PCRE2_ERROR_UTF32_ERR2 Character > 0x10ffff */ for (p = string; length > 0; length--, p++) { c = *p; if ((c & 0xfffff800u) != 0xd800u) { /* Normal UTF-32 code point. Neither high nor low surrogate. */ if (c > 0x10ffffu) { *erroroffset = p - string; return PCRE2_ERROR_UTF32_ERR2; } } else { /* A surrogate */ *erroroffset = p - string; return PCRE2_ERROR_UTF32_ERR1; } } return 0; #endif /* CODE_UNIT_WIDTH */ } #endif /* SUPPORT_UNICODE */ /* End of pcre2_valid_utf.c */ vfu-4.22/vstring/pcre2/pcre2_maketables.c0000644000175000017500000001464314145574056016714 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains the external function pcre2_maketables(), which builds character tables for PCRE2 in the current locale. The file is compiled on its own as part of the PCRE2 library. It is also included in the compilation of pcre2_dftables.c as a freestanding program, in which case the macro PCRE2_DFTABLES is defined. */ #ifndef PCRE2_DFTABLES /* Compiling the library */ # ifdef HAVE_CONFIG_H # include "config.h" # endif # include "pcre2_internal.h" #endif /************************************************* * Create PCRE2 character tables * *************************************************/ /* This function builds a set of character tables for use by PCRE2 and returns a pointer to them. They are build using the ctype functions, and consequently their contents will depend upon the current locale setting. When compiled as part of the library, the store is obtained via a general context malloc, if supplied, but when PCRE2_DFTABLES is defined (when compiling the pcre2_dftables freestanding auxiliary program) malloc() is used, and the function has a different name so as not to clash with the prototype in pcre2.h. Arguments: none when PCRE2_DFTABLES is defined else a PCRE2 general context or NULL Returns: pointer to the contiguous block of data else NULL if memory allocation failed */ #ifdef PCRE2_DFTABLES /* Included in freestanding pcre2_dftables program */ static const uint8_t *maketables(void) { uint8_t *yield = (uint8_t *)malloc(TABLES_LENGTH); #else /* Not PCRE2_DFTABLES, that is, compiling the library */ PCRE2_EXP_DEFN const uint8_t * PCRE2_CALL_CONVENTION pcre2_maketables(pcre2_general_context *gcontext) { uint8_t *yield = (uint8_t *)((gcontext != NULL)? gcontext->memctl.malloc(TABLES_LENGTH, gcontext->memctl.memory_data) : malloc(TABLES_LENGTH)); #endif /* PCRE2_DFTABLES */ int i; uint8_t *p; if (yield == NULL) return NULL; p = yield; /* First comes the lower casing table */ for (i = 0; i < 256; i++) *p++ = tolower(i); /* Next the case-flipping table */ for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); /* Then the character class tables. Don't try to be clever and save effort on exclusive ones - in some locales things may be different. Note that the table for "space" includes everything "isspace" gives, including VT in the default locale. This makes it work for the POSIX class [:space:]. From PCRE1 release 8.34 and for all PCRE2 releases it is also correct for Perl space, because Perl added VT at release 5.18. Note also that it is possible for a character to be alnum or alpha without being lower or upper, such as "male and female ordinals" (\xAA and \xBA) in the fr_FR locale (at least under Debian Linux's locales as of 12/2005). So we must test for alnum specially. */ memset(p, 0, cbit_length); for (i = 0; i < 256; i++) { if (isdigit(i)) p[cbit_digit + i/8] |= 1u << (i&7); if (isupper(i)) p[cbit_upper + i/8] |= 1u << (i&7); if (islower(i)) p[cbit_lower + i/8] |= 1u << (i&7); if (isalnum(i)) p[cbit_word + i/8] |= 1u << (i&7); if (i == '_') p[cbit_word + i/8] |= 1u << (i&7); if (isspace(i)) p[cbit_space + i/8] |= 1u << (i&7); if (isxdigit(i)) p[cbit_xdigit + i/8] |= 1u << (i&7); if (isgraph(i)) p[cbit_graph + i/8] |= 1u << (i&7); if (isprint(i)) p[cbit_print + i/8] |= 1u << (i&7); if (ispunct(i)) p[cbit_punct + i/8] |= 1u << (i&7); if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1u << (i&7); } p += cbit_length; /* Finally, the character type table. In this, we used to exclude VT from the white space chars, because Perl didn't recognize it as such for \s and for comments within regexes. However, Perl changed at release 5.18, so PCRE1 changed at release 8.34 and it's always been this way for PCRE2. */ for (i = 0; i < 256; i++) { int x = 0; if (isspace(i)) x += ctype_space; if (isalpha(i)) x += ctype_letter; if (islower(i)) x += ctype_lcletter; if (isdigit(i)) x += ctype_digit; if (isalnum(i) || i == '_') x += ctype_word; *p++ = x; } return yield; } #ifndef PCRE2_DFTABLES /* Compiling the library */ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_maketables_free(pcre2_general_context *gcontext, const uint8_t *tables) { if (gcontext) gcontext->memctl.free((void *)tables, gcontext->memctl.memory_data); else free((void *)tables); } #endif /* End of pcre2_maketables.c */ vfu-4.22/vstring/pcre2/pcre2_substring.c0000644000175000017500000004350414145574056016622 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Copy named captured string to given buffer * *************************************************/ /* This function copies a single captured substring into a given buffer, identifying it by name. If the regex permits duplicate names, the first substring that is set is chosen. Arguments: match_data points to the match data stringname the name of the required substring buffer where to put the substring sizeptr the size of the buffer, updated to the size of the substring Returns: if successful: zero if not successful, a negative error code: (1) an error from nametable_scan() (2) an error from copy_bynumber() (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_copy_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname, PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr) { PCRE2_SPTR first, last, entry; int failrc, entrysize; if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER) return PCRE2_ERROR_DFA_UFUNC; entrysize = pcre2_substring_nametable_scan(match_data->code, stringname, &first, &last); if (entrysize < 0) return entrysize; failrc = PCRE2_ERROR_UNAVAILABLE; for (entry = first; entry <= last; entry += entrysize) { uint32_t n = GET2(entry, 0); if (n < match_data->oveccount) { if (match_data->ovector[n*2] != PCRE2_UNSET) return pcre2_substring_copy_bynumber(match_data, n, buffer, sizeptr); failrc = PCRE2_ERROR_UNSET; } } return failrc; } /************************************************* * Copy numbered captured string to given buffer * *************************************************/ /* This function copies a single captured substring into a given buffer, identifying it by number. Arguments: match_data points to the match data stringnumber the number of the required substring buffer where to put the substring sizeptr the size of the buffer, updated to the size of the substring Returns: if successful: 0 if not successful, a negative error code: PCRE2_ERROR_NOMEMORY: buffer too small PCRE2_ERROR_NOSUBSTRING: no such substring PCRE2_ERROR_UNAVAILABLE: ovector too small PCRE2_ERROR_UNSET: substring is not set */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_copy_bynumber(pcre2_match_data *match_data, uint32_t stringnumber, PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr) { int rc; PCRE2_SIZE size; rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size); if (rc < 0) return rc; if (size + 1 > *sizeptr) return PCRE2_ERROR_NOMEMORY; memcpy(buffer, match_data->subject + match_data->ovector[stringnumber*2], CU2BYTES(size)); buffer[size] = 0; *sizeptr = size; return 0; } /************************************************* * Extract named captured string * *************************************************/ /* This function copies a single captured substring, identified by name, into new memory. If the regex permits duplicate names, the first substring that is set is chosen. Arguments: match_data pointer to match_data stringname the name of the required substring stringptr where to put the pointer to the new memory sizeptr where to put the length of the substring Returns: if successful: zero if not successful, a negative value: (1) an error from nametable_scan() (2) an error from get_bynumber() (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_get_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr) { PCRE2_SPTR first, last, entry; int failrc, entrysize; if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER) return PCRE2_ERROR_DFA_UFUNC; entrysize = pcre2_substring_nametable_scan(match_data->code, stringname, &first, &last); if (entrysize < 0) return entrysize; failrc = PCRE2_ERROR_UNAVAILABLE; for (entry = first; entry <= last; entry += entrysize) { uint32_t n = GET2(entry, 0); if (n < match_data->oveccount) { if (match_data->ovector[n*2] != PCRE2_UNSET) return pcre2_substring_get_bynumber(match_data, n, stringptr, sizeptr); failrc = PCRE2_ERROR_UNSET; } } return failrc; } /************************************************* * Extract captured string to new memory * *************************************************/ /* This function copies a single captured substring into a piece of new memory. Arguments: match_data points to match data stringnumber the number of the required substring stringptr where to put a pointer to the new memory sizeptr where to put the size of the substring Returns: if successful: 0 if not successful, a negative error code: PCRE2_ERROR_NOMEMORY: failed to get memory PCRE2_ERROR_NOSUBSTRING: no such substring PCRE2_ERROR_UNAVAILABLE: ovector too small PCRE2_ERROR_UNSET: substring is not set */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_get_bynumber(pcre2_match_data *match_data, uint32_t stringnumber, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr) { int rc; PCRE2_SIZE size; PCRE2_UCHAR *yield; rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size); if (rc < 0) return rc; yield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) + (size + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)match_data); if (yield == NULL) return PCRE2_ERROR_NOMEMORY; yield = (PCRE2_UCHAR *)(((char *)yield) + sizeof(pcre2_memctl)); memcpy(yield, match_data->subject + match_data->ovector[stringnumber*2], CU2BYTES(size)); yield[size] = 0; *stringptr = yield; *sizeptr = size; return 0; } /************************************************* * Free memory obtained by get_substring * *************************************************/ /* Argument: the result of a previous pcre2_substring_get_byxxx() Returns: nothing */ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_substring_free(PCRE2_UCHAR *string) { if (string != NULL) { pcre2_memctl *memctl = (pcre2_memctl *)((char *)string - sizeof(pcre2_memctl)); memctl->free(memctl, memctl->memory_data); } } /************************************************* * Get length of a named substring * *************************************************/ /* This function returns the length of a named captured substring. If the regex permits duplicate names, the first substring that is set is chosen. Arguments: match_data pointer to match data stringname the name of the required substring sizeptr where to put the length Returns: 0 if successful, else a negative error number */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_length_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname, PCRE2_SIZE *sizeptr) { PCRE2_SPTR first, last, entry; int failrc, entrysize; if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER) return PCRE2_ERROR_DFA_UFUNC; entrysize = pcre2_substring_nametable_scan(match_data->code, stringname, &first, &last); if (entrysize < 0) return entrysize; failrc = PCRE2_ERROR_UNAVAILABLE; for (entry = first; entry <= last; entry += entrysize) { uint32_t n = GET2(entry, 0); if (n < match_data->oveccount) { if (match_data->ovector[n*2] != PCRE2_UNSET) return pcre2_substring_length_bynumber(match_data, n, sizeptr); failrc = PCRE2_ERROR_UNSET; } } return failrc; } /************************************************* * Get length of a numbered substring * *************************************************/ /* This function returns the length of a captured substring. If the start is beyond the end (which can happen when \K is used in an assertion), it sets the length to zero. Arguments: match_data pointer to match data stringnumber the number of the required substring sizeptr where to put the length, if not NULL Returns: if successful: 0 if not successful, a negative error code: PCRE2_ERROR_NOSUBSTRING: no such substring PCRE2_ERROR_UNAVAILABLE: ovector is too small PCRE2_ERROR_UNSET: substring is not set */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_length_bynumber(pcre2_match_data *match_data, uint32_t stringnumber, PCRE2_SIZE *sizeptr) { PCRE2_SIZE left, right; int count = match_data->rc; if (count == PCRE2_ERROR_PARTIAL) { if (stringnumber > 0) return PCRE2_ERROR_PARTIAL; count = 0; } else if (count < 0) return count; /* Match failed */ if (match_data->matchedby != PCRE2_MATCHEDBY_DFA_INTERPRETER) { if (stringnumber > match_data->code->top_bracket) return PCRE2_ERROR_NOSUBSTRING; if (stringnumber >= match_data->oveccount) return PCRE2_ERROR_UNAVAILABLE; if (match_data->ovector[stringnumber*2] == PCRE2_UNSET) return PCRE2_ERROR_UNSET; } else /* Matched using pcre2_dfa_match() */ { if (stringnumber >= match_data->oveccount) return PCRE2_ERROR_UNAVAILABLE; if (count != 0 && stringnumber >= (uint32_t)count) return PCRE2_ERROR_UNSET; } left = match_data->ovector[stringnumber*2]; right = match_data->ovector[stringnumber*2+1]; if (sizeptr != NULL) *sizeptr = (left > right)? 0 : right - left; return 0; } /************************************************* * Extract all captured strings to new memory * *************************************************/ /* This function gets one chunk of memory and builds a list of pointers and all the captured substrings in it. A NULL pointer is put on the end of the list. The substrings are zero-terminated, but also, if the final argument is non-NULL, a list of lengths is also returned. This allows binary data to be handled. Arguments: match_data points to the match data listptr set to point to the list of pointers lengthsptr set to point to the list of lengths (may be NULL) Returns: if successful: 0 if not successful, a negative error code: PCRE2_ERROR_NOMEMORY: failed to get memory, or a match failure code */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_list_get(pcre2_match_data *match_data, PCRE2_UCHAR ***listptr, PCRE2_SIZE **lengthsptr) { int i, count, count2; PCRE2_SIZE size; PCRE2_SIZE *lensp; pcre2_memctl *memp; PCRE2_UCHAR **listp; PCRE2_UCHAR *sp; PCRE2_SIZE *ovector; if ((count = match_data->rc) < 0) return count; /* Match failed */ if (count == 0) count = match_data->oveccount; /* Ovector too small */ count2 = 2*count; ovector = match_data->ovector; size = sizeof(pcre2_memctl) + sizeof(PCRE2_UCHAR *); /* For final NULL */ if (lengthsptr != NULL) size += sizeof(PCRE2_SIZE) * count; /* For lengths */ for (i = 0; i < count2; i += 2) { size += sizeof(PCRE2_UCHAR *) + CU2BYTES(1); if (ovector[i+1] > ovector[i]) size += CU2BYTES(ovector[i+1] - ovector[i]); } memp = PRIV(memctl_malloc)(size, (pcre2_memctl *)match_data); if (memp == NULL) return PCRE2_ERROR_NOMEMORY; *listptr = listp = (PCRE2_UCHAR **)((char *)memp + sizeof(pcre2_memctl)); lensp = (PCRE2_SIZE *)((char *)listp + sizeof(PCRE2_UCHAR *) * (count + 1)); if (lengthsptr == NULL) { sp = (PCRE2_UCHAR *)lensp; lensp = NULL; } else { *lengthsptr = lensp; sp = (PCRE2_UCHAR *)((char *)lensp + sizeof(PCRE2_SIZE) * count); } for (i = 0; i < count2; i += 2) { size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0; /* Size == 0 includes the case when the capture is unset. Avoid adding PCRE2_UNSET to match_data->subject because it overflows, even though with zero size calling memcpy() is harmless. */ if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size)); *listp++ = sp; if (lensp != NULL) *lensp++ = size; sp += size; *sp++ = 0; } *listp = NULL; return 0; } /************************************************* * Free memory obtained by substring_list_get * *************************************************/ /* Argument: the result of a previous pcre2_substring_list_get() Returns: nothing */ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_substring_list_free(PCRE2_SPTR *list) { if (list != NULL) { pcre2_memctl *memctl = (pcre2_memctl *)((char *)list - sizeof(pcre2_memctl)); memctl->free(memctl, memctl->memory_data); } } /************************************************* * Find (multiple) entries for named string * *************************************************/ /* This function scans the nametable for a given name, using binary chop. It returns either two pointers to the entries in the table, or, if no pointers are given, the number of a unique group with the given name. If duplicate names are permitted, and the name is not unique, an error is generated. Arguments: code the compiled regex stringname the name whose entries required firstptr where to put the pointer to the first entry lastptr where to put the pointer to the last entry Returns: PCRE2_ERROR_NOSUBSTRING if the name is not found otherwise, if firstptr and lastptr are NULL: a group number for a unique substring else PCRE2_ERROR_NOUNIQUESUBSTRING otherwise: the length of each entry, having set firstptr and lastptr */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_nametable_scan(const pcre2_code *code, PCRE2_SPTR stringname, PCRE2_SPTR *firstptr, PCRE2_SPTR *lastptr) { uint16_t bot = 0; uint16_t top = code->name_count; uint16_t entrysize = code->name_entry_size; PCRE2_SPTR nametable = (PCRE2_SPTR)((char *)code + sizeof(pcre2_real_code)); while (top > bot) { uint16_t mid = (top + bot) / 2; PCRE2_SPTR entry = nametable + entrysize*mid; int c = PRIV(strcmp)(stringname, entry + IMM2_SIZE); if (c == 0) { PCRE2_SPTR first; PCRE2_SPTR last; PCRE2_SPTR lastentry; lastentry = nametable + entrysize * (code->name_count - 1); first = last = entry; while (first > nametable) { if (PRIV(strcmp)(stringname, (first - entrysize + IMM2_SIZE)) != 0) break; first -= entrysize; } while (last < lastentry) { if (PRIV(strcmp)(stringname, (last + entrysize + IMM2_SIZE)) != 0) break; last += entrysize; } if (firstptr == NULL) return (first == last)? (int)GET2(entry, 0) : PCRE2_ERROR_NOUNIQUESUBSTRING; *firstptr = first; *lastptr = last; return entrysize; } if (c > 0) bot = mid + 1; else top = mid; } return PCRE2_ERROR_NOSUBSTRING; } /************************************************* * Find number for named string * *************************************************/ /* This function is a convenience wrapper for pcre2_substring_nametable_scan() when it is known that names are unique. If there are duplicate names, it is not defined which number is returned. Arguments: code the compiled regex stringname the name whose number is required Returns: the number of the named parenthesis, or a negative number PCRE2_ERROR_NOSUBSTRING if not found PCRE2_ERROR_NOUNIQUESUBSTRING if not unique */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substring_number_from_name(const pcre2_code *code, PCRE2_SPTR stringname) { return pcre2_substring_nametable_scan(code, stringname, NULL, NULL); } /* End of pcre2_substring.c */ vfu-4.22/vstring/pcre2/pcre2_chartables.c0000644000175000017500000001765114145574056016716 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* This file was automatically written by the pcre2_dftables auxiliary program. It contains character tables that are used when no external tables are passed to PCRE2 by the application that calls it. The tables are used only for characters whose code values are less than 256. */ /* This set of tables was written in the C locale. */ /* The pcre2_ftables program (which is distributed with PCRE2) can be used to build alternative versions of this file. This is necessary if you are running in an EBCDIC environment, or if you want to default to a different encoding, for example ISO-8859-1. When pcre2_dftables is run, it creates these tables in the "C" locale by default. This happens automatically if PCRE2 is configured with --enable-rebuild-chartables. However, you can run pcre2_dftables manually with the -L option to build tables using the LC_ALL locale. */ /* The following #include is present because without it gcc 4.x may remove the array definition from the final binary if PCRE2 is built into a static library and dead code stripping is activated. This leads to link errors. Pulling in the header ensures that the array gets flagged as "someone outside this compilation unit might reference this" and so it will always be supplied to the linker. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" const uint8_t PRIV(default_tables)[] = { /* This table is a lower casing table. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, 104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119, 120,121,122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119, 120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135, 136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151, 152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167, 168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183, 184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199, 200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215, 216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231, 232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247, 248,249,250,251,252,253,254,255, /* This table is a case flipping table. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, 104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119, 120,121,122, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127, 128,129,130,131,132,133,134,135, 136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151, 152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167, 168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183, 184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199, 200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215, 216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231, 232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247, 248,249,250,251,252,253,254,255, /* This table contains bit maps for various character classes. Each map is 32 bytes long and the bits run from the least significant end of each byte. The classes that have their own maps are: space, xdigit, digit, upper, lower, word, graph, print, punct, and cntrl. Other classes are built from combinations. */ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, /* space */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* xdigit */ 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* digit */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* upper */ 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* lower */ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* word */ 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, /* graph */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* print */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, /* punct */ 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* cntrl */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* This table identifies various classes of character by individual bits: 0x01 white space character 0x02 letter 0x04 lower case letter 0x08 decimal digit 0x10 alphanumeric or '_' */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, /* 0 - 7 */ 0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* @ - G */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /* X - _ */ 0x00,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* ` - g */ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* h - o */ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* p - w */ 0x16,0x16,0x16,0x00,0x00,0x00,0x00,0x00, /* x -127 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ /* End of pcre2_chartables.c */ vfu-4.22/vstring/pcre2/pcre2.h0000644000175000017500000013423514145574056014531 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* This is the public header file for the PCRE library, second API, to be #included by applications that call PCRE2 functions. Copyright (c) 2016-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifndef PCRE2_H_IDEMPOTENT_GUARD #define PCRE2_H_IDEMPOTENT_GUARD /* The current PCRE version information. */ #define PCRE2_MAJOR 10 #define PCRE2_MINOR 40 #define PCRE2_PRERELEASE -RC1 #define PCRE2_DATE 2021-11-09 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE2, the appropriate export setting is defined in pcre2_internal.h, which includes this file. So we don't change existing definitions of PCRE2_EXP_DECL. */ #if defined(_WIN32) && !defined(PCRE2_STATIC) # ifndef PCRE2_EXP_DECL # define PCRE2_EXP_DECL extern __declspec(dllimport) # endif #endif /* By default, we use the standard "extern" declarations. */ #ifndef PCRE2_EXP_DECL # ifdef __cplusplus # define PCRE2_EXP_DECL extern "C" # else # define PCRE2_EXP_DECL extern # endif #endif /* When compiling with the MSVC compiler, it is sometimes necessary to include a "calling convention" before exported function names. (This is secondhand information; I know nothing about MSVC myself). For example, something like void __cdecl function(....) might be needed. In order so make this easy, all the exported functions have PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not set, we ensure here that it has no effect. */ #ifndef PCRE2_CALL_CONVENTION #define PCRE2_CALL_CONVENTION #endif /* Have to include limits.h, stdlib.h, and inttypes.h to ensure that size_t and uint8_t, UCHAR_MAX, etc are defined. Some systems that do have inttypes.h do not have stdint.h, which is why we use inttypes.h, which according to the C standard is a superset of stdint.h. If inttypes.h is not available the build will break and the relevant values must be provided by some other means. */ #include #include #include /* Allow for C++ users compiling this directly. */ #ifdef __cplusplus extern "C" { #endif /* The following option bits can be passed to pcre2_compile(), pcre2_match(), or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it is passed. Put these bits at the most significant end of the options word so others can be added next to them */ #define PCRE2_ANCHORED 0x80000000u #define PCRE2_NO_UTF_CHECK 0x40000000u #define PCRE2_ENDANCHORED 0x20000000u /* The following option bits can be passed only to pcre2_compile(). However, they may affect compilation, JIT compilation, and/or interpretive execution. The following tags indicate which: C alters what is compiled by pcre2_compile() J alters what is compiled by pcre2_jit_compile() M is inspected during pcre2_match() execution D is inspected during pcre2_dfa_match() execution */ #define PCRE2_ALLOW_EMPTY_CLASS 0x00000001u /* C */ #define PCRE2_ALT_BSUX 0x00000002u /* C */ #define PCRE2_AUTO_CALLOUT 0x00000004u /* C */ #define PCRE2_CASELESS 0x00000008u /* C */ #define PCRE2_DOLLAR_ENDONLY 0x00000010u /* J M D */ #define PCRE2_DOTALL 0x00000020u /* C */ #define PCRE2_DUPNAMES 0x00000040u /* C */ #define PCRE2_EXTENDED 0x00000080u /* C */ #define PCRE2_FIRSTLINE 0x00000100u /* J M D */ #define PCRE2_MATCH_UNSET_BACKREF 0x00000200u /* C J M */ #define PCRE2_MULTILINE 0x00000400u /* C */ #define PCRE2_NEVER_UCP 0x00000800u /* C */ #define PCRE2_NEVER_UTF 0x00001000u /* C */ #define PCRE2_NO_AUTO_CAPTURE 0x00002000u /* C */ #define PCRE2_NO_AUTO_POSSESS 0x00004000u /* C */ #define PCRE2_NO_DOTSTAR_ANCHOR 0x00008000u /* C */ #define PCRE2_NO_START_OPTIMIZE 0x00010000u /* J M D */ #define PCRE2_UCP 0x00020000u /* C J M D */ #define PCRE2_UNGREEDY 0x00040000u /* C */ #define PCRE2_UTF 0x00080000u /* C J M D */ #define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */ #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ #define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ #define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ #define PCRE2_EXTENDED_MORE 0x01000000u /* C */ #define PCRE2_LITERAL 0x02000000u /* C */ #define PCRE2_MATCH_INVALID_UTF 0x04000000u /* J M D */ /* An additional compile options word is available in the compile context. */ #define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */ #define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */ #define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */ #define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */ #define PCRE2_EXTRA_ESCAPED_CR_IS_LF 0x00000010u /* C */ #define PCRE2_EXTRA_ALT_BSUX 0x00000020u /* C */ #define PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK 0x00000040u /* C */ /* These are for pcre2_jit_compile(). */ #define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */ #define PCRE2_JIT_PARTIAL_SOFT 0x00000002u #define PCRE2_JIT_PARTIAL_HARD 0x00000004u #define PCRE2_JIT_INVALID_UTF 0x00000100u /* These are for pcre2_match(), pcre2_dfa_match(), pcre2_jit_match(), and pcre2_substitute(). Some are allowed only for one of the functions, and in these cases it is noted below. Note that PCRE2_ANCHORED, PCRE2_ENDANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these functions (though pcre2_jit_match() ignores the latter since it bypasses all sanity checks). */ #define PCRE2_NOTBOL 0x00000001u #define PCRE2_NOTEOL 0x00000002u #define PCRE2_NOTEMPTY 0x00000004u /* ) These two must be kept */ #define PCRE2_NOTEMPTY_ATSTART 0x00000008u /* ) adjacent to each other. */ #define PCRE2_PARTIAL_SOFT 0x00000010u #define PCRE2_PARTIAL_HARD 0x00000020u #define PCRE2_DFA_RESTART 0x00000040u /* pcre2_dfa_match() only */ #define PCRE2_DFA_SHORTEST 0x00000080u /* pcre2_dfa_match() only */ #define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* pcre2_substitute() only */ #define PCRE2_NO_JIT 0x00002000u /* Not for pcre2_dfa_match() */ #define PCRE2_COPY_MATCHED_SUBJECT 0x00004000u #define PCRE2_SUBSTITUTE_LITERAL 0x00008000u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_MATCHED 0x00010000u /* pcre2_substitute() only */ #define PCRE2_SUBSTITUTE_REPLACEMENT_ONLY 0x00020000u /* pcre2_substitute() only */ /* Options for pcre2_pattern_convert(). */ #define PCRE2_CONVERT_UTF 0x00000001u #define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u #define PCRE2_CONVERT_POSIX_BASIC 0x00000004u #define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u #define PCRE2_CONVERT_GLOB 0x00000010u #define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u #define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be greater than zero. */ #define PCRE2_NEWLINE_CR 1 #define PCRE2_NEWLINE_LF 2 #define PCRE2_NEWLINE_CRLF 3 #define PCRE2_NEWLINE_ANY 4 #define PCRE2_NEWLINE_ANYCRLF 5 #define PCRE2_NEWLINE_NUL 6 #define PCRE2_BSR_UNICODE 1 #define PCRE2_BSR_ANYCRLF 2 /* Error codes for pcre2_compile(). Some of these are also used by pcre2_pattern_convert(). */ #define PCRE2_ERROR_END_BACKSLASH 101 #define PCRE2_ERROR_END_BACKSLASH_C 102 #define PCRE2_ERROR_UNKNOWN_ESCAPE 103 #define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104 #define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105 #define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106 #define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107 #define PCRE2_ERROR_CLASS_RANGE_ORDER 108 #define PCRE2_ERROR_QUANTIFIER_INVALID 109 #define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110 #define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111 #define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112 #define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113 #define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114 #define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115 #define PCRE2_ERROR_NULL_PATTERN 116 #define PCRE2_ERROR_BAD_OPTIONS 117 #define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118 #define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119 #define PCRE2_ERROR_PATTERN_TOO_LARGE 120 #define PCRE2_ERROR_HEAP_FAILED 121 #define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122 #define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123 #define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124 #define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125 #define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126 #define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127 #define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128 #define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129 #define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130 #define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131 #define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132 #define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133 #define PCRE2_ERROR_CODE_POINT_TOO_BIG 134 #define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135 #define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136 #define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137 #define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138 #define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139 #define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140 #define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141 #define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142 #define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143 #define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144 #define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145 #define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146 #define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147 #define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148 #define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149 #define PCRE2_ERROR_CLASS_INVALID_RANGE 150 #define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151 #define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152 #define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153 #define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154 #define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155 #define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156 #define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157 #define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158 /* Error 159 is obsolete and should now never occur */ #define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159 #define PCRE2_ERROR_VERB_UNKNOWN 160 #define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161 #define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162 #define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163 #define PCRE2_ERROR_INVALID_OCTAL 164 #define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165 #define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166 #define PCRE2_ERROR_INVALID_HEXADECIMAL 167 #define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168 #define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169 #define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170 #define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171 #define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172 #define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173 #define PCRE2_ERROR_UTF_IS_DISABLED 174 #define PCRE2_ERROR_UCP_IS_DISABLED 175 #define PCRE2_ERROR_VERB_NAME_TOO_LONG 176 #define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177 #define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178 #define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179 #define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180 #define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181 #define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182 #define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183 #define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184 #define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185 #define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186 #define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187 #define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188 #define PCRE2_ERROR_INTERNAL_BAD_CODE 189 #define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190 #define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191 #define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192 #define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193 #define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194 #define PCRE2_ERROR_ALPHA_ASSERTION_UNKNOWN 195 #define PCRE2_ERROR_SCRIPT_RUN_NOT_AVAILABLE 196 #define PCRE2_ERROR_TOO_MANY_CAPTURES 197 #define PCRE2_ERROR_CONDITION_ATOMIC_ASSERTION_EXPECTED 198 #define PCRE2_ERROR_BACKSLASH_K_IN_LOOKAROUND 199 /* "Expected" matching error codes: no match and partial match. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) /* Error codes for UTF-8 validity checks */ #define PCRE2_ERROR_UTF8_ERR1 (-3) #define PCRE2_ERROR_UTF8_ERR2 (-4) #define PCRE2_ERROR_UTF8_ERR3 (-5) #define PCRE2_ERROR_UTF8_ERR4 (-6) #define PCRE2_ERROR_UTF8_ERR5 (-7) #define PCRE2_ERROR_UTF8_ERR6 (-8) #define PCRE2_ERROR_UTF8_ERR7 (-9) #define PCRE2_ERROR_UTF8_ERR8 (-10) #define PCRE2_ERROR_UTF8_ERR9 (-11) #define PCRE2_ERROR_UTF8_ERR10 (-12) #define PCRE2_ERROR_UTF8_ERR11 (-13) #define PCRE2_ERROR_UTF8_ERR12 (-14) #define PCRE2_ERROR_UTF8_ERR13 (-15) #define PCRE2_ERROR_UTF8_ERR14 (-16) #define PCRE2_ERROR_UTF8_ERR15 (-17) #define PCRE2_ERROR_UTF8_ERR16 (-18) #define PCRE2_ERROR_UTF8_ERR17 (-19) #define PCRE2_ERROR_UTF8_ERR18 (-20) #define PCRE2_ERROR_UTF8_ERR19 (-21) #define PCRE2_ERROR_UTF8_ERR20 (-22) #define PCRE2_ERROR_UTF8_ERR21 (-23) /* Error codes for UTF-16 validity checks */ #define PCRE2_ERROR_UTF16_ERR1 (-24) #define PCRE2_ERROR_UTF16_ERR2 (-25) #define PCRE2_ERROR_UTF16_ERR3 (-26) /* Error codes for UTF-32 validity checks */ #define PCRE2_ERROR_UTF32_ERR1 (-27) #define PCRE2_ERROR_UTF32_ERR2 (-28) /* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction functions, context functions, and serializing functions. They are in numerical order. Originally they were in alphabetical order too, but now that PCRE2 is released, the numbers must not be changed. */ #define PCRE2_ERROR_BADDATA (-29) #define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */ #define PCRE2_ERROR_BADMAGIC (-31) #define PCRE2_ERROR_BADMODE (-32) #define PCRE2_ERROR_BADOFFSET (-33) #define PCRE2_ERROR_BADOPTION (-34) #define PCRE2_ERROR_BADREPLACEMENT (-35) #define PCRE2_ERROR_BADUTFOFFSET (-36) #define PCRE2_ERROR_CALLOUT (-37) /* Never used by PCRE2 itself */ #define PCRE2_ERROR_DFA_BADRESTART (-38) #define PCRE2_ERROR_DFA_RECURSE (-39) #define PCRE2_ERROR_DFA_UCOND (-40) #define PCRE2_ERROR_DFA_UFUNC (-41) #define PCRE2_ERROR_DFA_UITEM (-42) #define PCRE2_ERROR_DFA_WSSIZE (-43) #define PCRE2_ERROR_INTERNAL (-44) #define PCRE2_ERROR_JIT_BADOPTION (-45) #define PCRE2_ERROR_JIT_STACKLIMIT (-46) #define PCRE2_ERROR_MATCHLIMIT (-47) #define PCRE2_ERROR_NOMEMORY (-48) #define PCRE2_ERROR_NOSUBSTRING (-49) #define PCRE2_ERROR_NOUNIQUESUBSTRING (-50) #define PCRE2_ERROR_NULL (-51) #define PCRE2_ERROR_RECURSELOOP (-52) #define PCRE2_ERROR_DEPTHLIMIT (-53) #define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */ #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) #define PCRE2_ERROR_BADOFFSETLIMIT (-56) #define PCRE2_ERROR_BADREPESCAPE (-57) #define PCRE2_ERROR_REPMISSINGBRACE (-58) #define PCRE2_ERROR_BADSUBSTITUTION (-59) #define PCRE2_ERROR_BADSUBSPATTERN (-60) #define PCRE2_ERROR_TOOMANYREPLACE (-61) #define PCRE2_ERROR_BADSERIALIZEDDATA (-62) #define PCRE2_ERROR_HEAPLIMIT (-63) #define PCRE2_ERROR_CONVERT_SYNTAX (-64) #define PCRE2_ERROR_INTERNAL_DUPMATCH (-65) #define PCRE2_ERROR_DFA_UINVALID_UTF (-66) /* Request types for pcre2_pattern_info() */ #define PCRE2_INFO_ALLOPTIONS 0 #define PCRE2_INFO_ARGOPTIONS 1 #define PCRE2_INFO_BACKREFMAX 2 #define PCRE2_INFO_BSR 3 #define PCRE2_INFO_CAPTURECOUNT 4 #define PCRE2_INFO_FIRSTCODEUNIT 5 #define PCRE2_INFO_FIRSTCODETYPE 6 #define PCRE2_INFO_FIRSTBITMAP 7 #define PCRE2_INFO_HASCRORLF 8 #define PCRE2_INFO_JCHANGED 9 #define PCRE2_INFO_JITSIZE 10 #define PCRE2_INFO_LASTCODEUNIT 11 #define PCRE2_INFO_LASTCODETYPE 12 #define PCRE2_INFO_MATCHEMPTY 13 #define PCRE2_INFO_MATCHLIMIT 14 #define PCRE2_INFO_MAXLOOKBEHIND 15 #define PCRE2_INFO_MINLENGTH 16 #define PCRE2_INFO_NAMECOUNT 17 #define PCRE2_INFO_NAMEENTRYSIZE 18 #define PCRE2_INFO_NAMETABLE 19 #define PCRE2_INFO_NEWLINE 20 #define PCRE2_INFO_DEPTHLIMIT 21 #define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */ #define PCRE2_INFO_SIZE 22 #define PCRE2_INFO_HASBACKSLASHC 23 #define PCRE2_INFO_FRAMESIZE 24 #define PCRE2_INFO_HEAPLIMIT 25 #define PCRE2_INFO_EXTRAOPTIONS 26 /* Request types for pcre2_config(). */ #define PCRE2_CONFIG_BSR 0 #define PCRE2_CONFIG_JIT 1 #define PCRE2_CONFIG_JITTARGET 2 #define PCRE2_CONFIG_LINKSIZE 3 #define PCRE2_CONFIG_MATCHLIMIT 4 #define PCRE2_CONFIG_NEWLINE 5 #define PCRE2_CONFIG_PARENSLIMIT 6 #define PCRE2_CONFIG_DEPTHLIMIT 7 #define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */ #define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */ #define PCRE2_CONFIG_UNICODE 9 #define PCRE2_CONFIG_UNICODE_VERSION 10 #define PCRE2_CONFIG_VERSION 11 #define PCRE2_CONFIG_HEAPLIMIT 12 #define PCRE2_CONFIG_NEVER_BACKSLASH_C 13 #define PCRE2_CONFIG_COMPILED_WIDTHS 14 #define PCRE2_CONFIG_TABLES_LENGTH 15 /* Types for code units in patterns and subject strings. */ typedef uint8_t PCRE2_UCHAR8; typedef uint16_t PCRE2_UCHAR16; typedef uint32_t PCRE2_UCHAR32; typedef const PCRE2_UCHAR8 *PCRE2_SPTR8; typedef const PCRE2_UCHAR16 *PCRE2_SPTR16; typedef const PCRE2_UCHAR32 *PCRE2_SPTR32; /* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2, including pattern offsets for errors and subject offsets after a match. We define special values to indicate zero-terminated strings and unset offsets in the offset vector (ovector). */ #define PCRE2_SIZE size_t #define PCRE2_SIZE_MAX SIZE_MAX #define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0) #define PCRE2_UNSET (~(PCRE2_SIZE)0) /* Generic types for opaque structures and JIT callback functions. These declarations are defined in a macro that is expanded for each width later. */ #define PCRE2_TYPES_LIST \ struct pcre2_real_general_context; \ typedef struct pcre2_real_general_context pcre2_general_context; \ \ struct pcre2_real_compile_context; \ typedef struct pcre2_real_compile_context pcre2_compile_context; \ \ struct pcre2_real_match_context; \ typedef struct pcre2_real_match_context pcre2_match_context; \ \ struct pcre2_real_convert_context; \ typedef struct pcre2_real_convert_context pcre2_convert_context; \ \ struct pcre2_real_code; \ typedef struct pcre2_real_code pcre2_code; \ \ struct pcre2_real_match_data; \ typedef struct pcre2_real_match_data pcre2_match_data; \ \ struct pcre2_real_jit_stack; \ typedef struct pcre2_real_jit_stack pcre2_jit_stack; \ \ typedef pcre2_jit_stack *(*pcre2_jit_callback)(void *); /* The structures for passing out data via callout functions. We use structures so that new fields can be added on the end in future versions, without changing the API of the function, thereby allowing old clients to work without modification. Define the generic versions in a macro; the width-specific versions are generated from this macro below. */ /* Flags for the callout_flags field. These are cleared after a callout. */ #define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */ #define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */ #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ uint32_t version; /* Identifies version of block */ \ /* ------------------------ Version 0 ------------------------------- */ \ uint32_t callout_number; /* Number compiled into pattern */ \ uint32_t capture_top; /* Max current capture */ \ uint32_t capture_last; /* Most recently closed capture */ \ PCRE2_SIZE *offset_vector; /* The offset vector */ \ PCRE2_SPTR mark; /* Pointer to current mark or NULL */ \ PCRE2_SPTR subject; /* The subject being matched */ \ PCRE2_SIZE subject_length; /* The length of the subject */ \ PCRE2_SIZE start_match; /* Offset to start of this match attempt */ \ PCRE2_SIZE current_position; /* Where we currently are in the subject */ \ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \ /* ------------------- Added for Version 1 -------------------------- */ \ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ PCRE2_SPTR callout_string; /* String compiled into pattern */ \ /* ------------------- Added for Version 2 -------------------------- */ \ uint32_t callout_flags; /* See above for list */ \ /* ------------------------------------------------------------------ */ \ } pcre2_callout_block; \ \ typedef struct pcre2_callout_enumerate_block { \ uint32_t version; /* Identifies version of block */ \ /* ------------------------ Version 0 ------------------------------- */ \ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \ uint32_t callout_number; /* Number compiled into pattern */ \ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ PCRE2_SPTR callout_string; /* String compiled into pattern */ \ /* ------------------------------------------------------------------ */ \ } pcre2_callout_enumerate_block; \ \ typedef struct pcre2_substitute_callout_block { \ uint32_t version; /* Identifies version of block */ \ /* ------------------------ Version 0 ------------------------------- */ \ PCRE2_SPTR input; /* Pointer to input subject string */ \ PCRE2_SPTR output; /* Pointer to output buffer */ \ PCRE2_SIZE output_offsets[2]; /* Changed portion of the output */ \ PCRE2_SIZE *ovector; /* Pointer to current ovector */ \ uint32_t oveccount; /* Count of pairs set in ovector */ \ uint32_t subscount; /* Substitution number */ \ /* ------------------------------------------------------------------ */ \ } pcre2_substitute_callout_block; /* List the generic forms of all other functions in macros, which will be expanded for each width below. Start with functions that give general information. */ #define PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); /* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ *pcre2_general_context_copy(pcre2_general_context *); \ PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ *pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ void (*)(void *, void *), void *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_general_context_free(pcre2_general_context *); #define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ *pcre2_compile_context_copy(pcre2_compile_context *); \ PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ *pcre2_compile_context_create(pcre2_general_context *);\ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_compile_context_free(pcre2_compile_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_character_tables(pcre2_compile_context *, const uint8_t *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_newline(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_compile_recursion_guard(pcre2_compile_context *, \ int (*)(uint32_t, void *), void *); #define PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ *pcre2_match_context_copy(pcre2_match_context *); \ PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ *pcre2_match_context_create(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_match_context_free(pcre2_match_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_callout(pcre2_match_context *, \ int (*)(pcre2_callout_block *, void *), void *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_substitute_callout(pcre2_match_context *, \ int (*)(pcre2_substitute_callout_block *, void *), void *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_match_limit(pcre2_match_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_recursion_memory_management(pcre2_match_context *, \ void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *); #define PCRE2_CONVERT_CONTEXT_FUNCTIONS \ PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ *pcre2_convert_context_copy(pcre2_convert_context *); \ PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ *pcre2_convert_context_create(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_convert_context_free(pcre2_convert_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_set_glob_separator(pcre2_convert_context *, uint32_t); /* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ pcre2_compile_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_code_free(pcre2_code *); \ PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ *pcre2_code_copy(const pcre2_code *); \ PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ *pcre2_code_copy_with_tables(const pcre2_code *); /* Functions that give information about a compiled pattern. */ #define PCRE2_PATTERN_INFO_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_callout_enumerate(const pcre2_code *, \ int (*)(pcre2_callout_enumerate_block *, void *), void *); /* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ *pcre2_match_data_create(uint32_t, pcre2_general_context *); \ PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ *pcre2_match_data_create_from_pattern(const pcre2_code *, \ pcre2_general_context *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ uint32_t, pcre2_match_data *, pcre2_match_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_match_data_free(pcre2_match_data *); \ PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \ pcre2_get_mark(pcre2_match_data *); \ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ pcre2_get_match_data_size(pcre2_match_data *); \ PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \ pcre2_get_ovector_count(pcre2_match_data *); \ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ *pcre2_get_ovector_pointer(pcre2_match_data *); \ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ pcre2_get_startchar(pcre2_match_data *); /* Convenience functions for handling matched substrings. */ #define PCRE2_SUBSTRING_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \ PCRE2_SIZE *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \ PCRE2_SIZE *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_substring_free(PCRE2_UCHAR *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \ PCRE2_SIZE *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \ PCRE2_SIZE *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \ PCRE2_SPTR *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_substring_list_free(PCRE2_SPTR *); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **); /* Functions for serializing / deserializing compiled patterns. */ #define PCRE2_SERIALIZE_FUNCTIONS \ PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \ PCRE2_SIZE *, pcre2_general_context *); \ PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \ pcre2_general_context *); \ PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ pcre2_serialize_get_number_of_codes(const uint8_t *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_serialize_free(uint8_t *); /* Convenience function for match + substitute. */ #define PCRE2_SUBSTITUTE_FUNCTION \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \ PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *); /* Functions for converting pattern source strings. */ #define PCRE2_CONVERT_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \ PCRE2_SIZE *, pcre2_convert_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_converted_pattern_free(PCRE2_UCHAR *); /* Functions for JIT processing */ #define PCRE2_JIT_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_jit_compile(pcre2_code *, uint32_t); \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ uint32_t, pcre2_match_data *, pcre2_match_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_free_unused_memory(pcre2_general_context *); \ PCRE2_EXP_DECL pcre2_jit_stack PCRE2_CALL_CONVENTION \ *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_stack_free(pcre2_jit_stack *); /* Other miscellaneous functions. */ #define PCRE2_OTHER_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ *pcre2_maketables(pcre2_general_context *); \ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_maketables_free(pcre2_general_context *, const uint8_t *); /* Define macros that generate width-specific names from generic versions. The three-level macro scheme is necessary to get the macros expanded when we want them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for generating three versions of everything below. After that, PCRE2_SUFFIX will be re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as pcre2_compile are called by application code. */ #define PCRE2_JOIN(a,b) a ## b #define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b) #define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH) /* Data types */ #define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR) #define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR) #define pcre2_code PCRE2_SUFFIX(pcre2_code_) #define pcre2_jit_callback PCRE2_SUFFIX(pcre2_jit_callback_) #define pcre2_jit_stack PCRE2_SUFFIX(pcre2_jit_stack_) #define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_) #define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_) #define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_) #define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_) #define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_) #define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_) #define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) /* Data blocks */ #define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_) #define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) #define pcre2_substitute_callout_block PCRE2_SUFFIX(pcre2_substitute_callout_block_) #define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_) #define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_) #define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_) #define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_) #define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) /* Functions: the complete list in alphabetical order */ #define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) #define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) #define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_) #define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_) #define pcre2_compile PCRE2_SUFFIX(pcre2_compile_) #define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_) #define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_) #define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_) #define pcre2_config PCRE2_SUFFIX(pcre2_config_) #define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_) #define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_) #define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_) #define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_) #define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_) #define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_) #define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_) #define pcre2_general_context_free PCRE2_SUFFIX(pcre2_general_context_free_) #define pcre2_get_error_message PCRE2_SUFFIX(pcre2_get_error_message_) #define pcre2_get_mark PCRE2_SUFFIX(pcre2_get_mark_) #define pcre2_get_match_data_size PCRE2_SUFFIX(pcre2_get_match_data_size_) #define pcre2_get_ovector_pointer PCRE2_SUFFIX(pcre2_get_ovector_pointer_) #define pcre2_get_ovector_count PCRE2_SUFFIX(pcre2_get_ovector_count_) #define pcre2_get_startchar PCRE2_SUFFIX(pcre2_get_startchar_) #define pcre2_jit_compile PCRE2_SUFFIX(pcre2_jit_compile_) #define pcre2_jit_match PCRE2_SUFFIX(pcre2_jit_match_) #define pcre2_jit_free_unused_memory PCRE2_SUFFIX(pcre2_jit_free_unused_memory_) #define pcre2_jit_stack_assign PCRE2_SUFFIX(pcre2_jit_stack_assign_) #define pcre2_jit_stack_create PCRE2_SUFFIX(pcre2_jit_stack_create_) #define pcre2_jit_stack_free PCRE2_SUFFIX(pcre2_jit_stack_free_) #define pcre2_maketables PCRE2_SUFFIX(pcre2_maketables_) #define pcre2_maketables_free PCRE2_SUFFIX(pcre2_maketables_free_) #define pcre2_match PCRE2_SUFFIX(pcre2_match_) #define pcre2_match_context_copy PCRE2_SUFFIX(pcre2_match_context_copy_) #define pcre2_match_context_create PCRE2_SUFFIX(pcre2_match_context_create_) #define pcre2_match_context_free PCRE2_SUFFIX(pcre2_match_context_free_) #define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_) #define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_) #define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_) #define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_) #define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_) #define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_) #define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_) #define pcre2_serialize_free PCRE2_SUFFIX(pcre2_serialize_free_) #define pcre2_serialize_get_number_of_codes PCRE2_SUFFIX(pcre2_serialize_get_number_of_codes_) #define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_) #define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_) #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) #define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) #define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_) #define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_) #define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_) #define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_) #define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_) #define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_) #define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_) #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) #define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_) #define pcre2_set_substitute_callout PCRE2_SUFFIX(pcre2_set_substitute_callout_) #define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) #define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) #define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) #define pcre2_substring_free PCRE2_SUFFIX(pcre2_substring_free_) #define pcre2_substring_get_byname PCRE2_SUFFIX(pcre2_substring_get_byname_) #define pcre2_substring_get_bynumber PCRE2_SUFFIX(pcre2_substring_get_bynumber_) #define pcre2_substring_length_byname PCRE2_SUFFIX(pcre2_substring_length_byname_) #define pcre2_substring_length_bynumber PCRE2_SUFFIX(pcre2_substring_length_bynumber_) #define pcre2_substring_list_get PCRE2_SUFFIX(pcre2_substring_list_get_) #define pcre2_substring_list_free PCRE2_SUFFIX(pcre2_substring_list_free_) #define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_) #define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) /* Keep this old function name for backwards compatibility */ #define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) /* Keep this obsolete function for backwards compatibility: it is now a noop. */ #define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) /* Now generate all three sets of width-specific structures and function prototypes. */ #define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \ PCRE2_TYPES_LIST \ PCRE2_STRUCTURE_LIST \ PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_CONTEXT_FUNCTIONS \ PCRE2_CONVERT_CONTEXT_FUNCTIONS \ PCRE2_CONVERT_FUNCTIONS \ PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_FUNCTIONS \ PCRE2_PATTERN_INFO_FUNCTIONS \ PCRE2_MATCH_FUNCTIONS \ PCRE2_SUBSTRING_FUNCTIONS \ PCRE2_SERIALIZE_FUNCTIONS \ PCRE2_SUBSTITUTE_FUNCTION \ PCRE2_JIT_FUNCTIONS \ PCRE2_OTHER_FUNCTIONS #define PCRE2_LOCAL_WIDTH 8 PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH #define PCRE2_LOCAL_WIDTH 16 PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH #define PCRE2_LOCAL_WIDTH 32 PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH /* Undefine the list macros; they are no longer needed. */ #undef PCRE2_TYPES_LIST #undef PCRE2_STRUCTURE_LIST #undef PCRE2_GENERAL_INFO_FUNCTIONS #undef PCRE2_GENERAL_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_CONTEXT_FUNCTIONS #undef PCRE2_CONVERT_CONTEXT_FUNCTIONS #undef PCRE2_MATCH_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_FUNCTIONS #undef PCRE2_PATTERN_INFO_FUNCTIONS #undef PCRE2_MATCH_FUNCTIONS #undef PCRE2_SUBSTRING_FUNCTIONS #undef PCRE2_SERIALIZE_FUNCTIONS #undef PCRE2_SUBSTITUTE_FUNCTION #undef PCRE2_JIT_FUNCTIONS #undef PCRE2_OTHER_FUNCTIONS #undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS /* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ #undef PCRE2_SUFFIX #ifndef PCRE2_CODE_UNIT_WIDTH #error PCRE2_CODE_UNIT_WIDTH must be defined before including pcre2.h. #error Use 8, 16, or 32; or 0 for a multi-width application. #else /* PCRE2_CODE_UNIT_WIDTH is defined */ #if PCRE2_CODE_UNIT_WIDTH == 8 || \ PCRE2_CODE_UNIT_WIDTH == 16 || \ PCRE2_CODE_UNIT_WIDTH == 32 #define PCRE2_SUFFIX(a) PCRE2_GLUE(a, PCRE2_CODE_UNIT_WIDTH) #elif PCRE2_CODE_UNIT_WIDTH == 0 #undef PCRE2_JOIN #undef PCRE2_GLUE #define PCRE2_SUFFIX(a) a #else #error PCRE2_CODE_UNIT_WIDTH must be 0, 8, 16, or 32. #endif #endif /* PCRE2_CODE_UNIT_WIDTH is defined */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* PCRE2_H_IDEMPOTENT_GUARD */ /* End of pcre2.h */ vfu-4.22/vstring/pcre2/pcre2_jit_compile.c0000644000175000017500000153161114145574056017102 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel This module by Zoltan Herczeg Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2019 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #ifdef SUPPORT_JIT /* All-in-one: Since we use the JIT compiler only from here, we just include it. This way we don't need to touch the build system files. */ #define SLJIT_CONFIG_AUTO 1 #define SLJIT_CONFIG_STATIC 1 #define SLJIT_VERBOSE 0 #ifdef PCRE2_DEBUG #define SLJIT_DEBUG 1 #else #define SLJIT_DEBUG 0 #endif #define SLJIT_MALLOC(size, allocator_data) pcre2_jit_malloc(size, allocator_data) #define SLJIT_FREE(ptr, allocator_data) pcre2_jit_free(ptr, allocator_data) static void * pcre2_jit_malloc(size_t size, void *allocator_data) { pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data); return allocator->malloc(size, allocator->memory_data); } static void pcre2_jit_free(void *ptr, void *allocator_data) { pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data); allocator->free(ptr, allocator->memory_data); } #include "sljit/sljitLir.c" #if defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED #error Unsupported architecture #endif /* Defines for debugging purposes. */ /* 1 - Use unoptimized capturing brackets. 2 - Enable capture_last_ptr (includes option 1). */ /* #define DEBUG_FORCE_UNOPTIMIZED_CBRAS 2 */ /* 1 - Always have a control head. */ /* #define DEBUG_FORCE_CONTROL_HEAD 1 */ /* Allocate memory for the regex stack on the real machine stack. Fast, but limited size. */ #define MACHINE_STACK_SIZE 32768 /* Growth rate for stack allocated by the OS. Should be the multiply of page size. */ #define STACK_GROWTH_RATE 8192 /* Enable to check that the allocation could destroy temporaries. */ #if defined SLJIT_DEBUG && SLJIT_DEBUG #define DESTROY_REGISTERS 1 #endif /* Short summary about the backtracking mechanism empolyed by the jit code generator: The code generator follows the recursive nature of the PERL compatible regular expressions. The basic blocks of regular expressions are condition checkers whose execute different commands depending on the result of the condition check. The relationship between the operators can be horizontal (concatenation) and vertical (sub-expression) (See struct backtrack_common for more details). 'ab' - 'a' and 'b' regexps are concatenated 'a+' - 'a' is the sub-expression of the '+' operator The condition checkers are boolean (true/false) checkers. Machine code is generated for the checker itself and for the actions depending on the result of the checker. The 'true' case is called as the matching path (expected path), and the other is called as the 'backtrack' path. Branch instructions are expesive for all CPUs, so we avoid taken branches on the matching path. Greedy star operator (*) : Matching path: match happens. Backtrack path: match failed. Non-greedy star operator (*?) : Matching path: no need to perform a match. Backtrack path: match is required. The following example shows how the code generated for a capturing bracket with two alternatives. Let A, B, C, D are arbirary regular expressions, and we have the following regular expression: A(B|C)D The generated code will be the following: A matching path '(' matching path (pushing arguments to the stack) B matching path ')' matching path (pushing arguments to the stack) D matching path return with successful match D backtrack path ')' backtrack path (If we arrived from "C" jump to the backtrack of "C") B backtrack path C expected path jump to D matching path C backtrack path A backtrack path Notice, that the order of backtrack code paths are the opposite of the fast code paths. In this way the topmost value on the stack is always belong to the current backtrack code path. The backtrack path must check whether there is a next alternative. If so, it needs to jump back to the matching path eventually. Otherwise it needs to clear out its own stack frame and continue the execution on the backtrack code paths. */ /* Saved stack frames: Atomic blocks and asserts require reloading the values of private data when the backtrack mechanism performed. Because of OP_RECURSE, the data are not necessarly known in compile time, thus we need a dynamic restore mechanism. The stack frames are stored in a chain list, and have the following format: ([ capturing bracket offset ][ start value ][ end value ])+ ... [ 0 ] [ previous head ] Thus we can restore the private data to a particular point in the stack. */ typedef struct jit_arguments { /* Pointers first. */ struct sljit_stack *stack; PCRE2_SPTR str; PCRE2_SPTR begin; PCRE2_SPTR end; pcre2_match_data *match_data; PCRE2_SPTR startchar_ptr; PCRE2_UCHAR *mark_ptr; int (*callout)(pcre2_callout_block *, void *); void *callout_data; /* Everything else after. */ sljit_uw offset_limit; sljit_u32 limit_match; sljit_u32 oveccount; sljit_u32 options; } jit_arguments; #define JIT_NUMBER_OF_COMPILE_MODES 3 typedef struct executable_functions { void *executable_funcs[JIT_NUMBER_OF_COMPILE_MODES]; void *read_only_data_heads[JIT_NUMBER_OF_COMPILE_MODES]; sljit_uw executable_sizes[JIT_NUMBER_OF_COMPILE_MODES]; sljit_u32 top_bracket; sljit_u32 limit_match; } executable_functions; typedef struct jump_list { struct sljit_jump *jump; struct jump_list *next; } jump_list; typedef struct stub_list { struct sljit_jump *start; struct sljit_label *quit; struct stub_list *next; } stub_list; enum frame_types { no_frame = -1, no_stack = -2 }; enum control_types { type_mark = 0, type_then_trap = 1 }; enum early_fail_types { type_skip = 0, type_fail = 1, type_fail_range = 2 }; typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args); /* The following structure is the key data type for the recursive code generator. It is allocated by compile_matchingpath, and contains the arguments for compile_backtrackingpath. Must be the first member of its descendants. */ typedef struct backtrack_common { /* Concatenation stack. */ struct backtrack_common *prev; jump_list *nextbacktracks; /* Internal stack (for component operators). */ struct backtrack_common *top; jump_list *topbacktracks; /* Opcode pointer. */ PCRE2_SPTR cc; } backtrack_common; typedef struct assert_backtrack { backtrack_common common; jump_list *condfailed; /* Less than 0 if a frame is not needed. */ int framesize; /* Points to our private memory word on the stack. */ int private_data_ptr; /* For iterators. */ struct sljit_label *matchingpath; } assert_backtrack; typedef struct bracket_backtrack { backtrack_common common; /* Where to coninue if an alternative is successfully matched. */ struct sljit_label *alternative_matchingpath; /* For rmin and rmax iterators. */ struct sljit_label *recursive_matchingpath; /* For greedy ? operator. */ struct sljit_label *zero_matchingpath; /* Contains the branches of a failed condition. */ union { /* Both for OP_COND, OP_SCOND. */ jump_list *condfailed; assert_backtrack *assert; /* For OP_ONCE. Less than 0 if not needed. */ int framesize; /* For brackets with >3 alternatives. */ struct sljit_put_label *matching_put_label; } u; /* Points to our private memory word on the stack. */ int private_data_ptr; } bracket_backtrack; typedef struct bracketpos_backtrack { backtrack_common common; /* Points to our private memory word on the stack. */ int private_data_ptr; /* Reverting stack is needed. */ int framesize; /* Allocated stack size. */ int stacksize; } bracketpos_backtrack; typedef struct braminzero_backtrack { backtrack_common common; struct sljit_label *matchingpath; } braminzero_backtrack; typedef struct char_iterator_backtrack { backtrack_common common; /* Next iteration. */ struct sljit_label *matchingpath; union { jump_list *backtracks; struct { unsigned int othercasebit; PCRE2_UCHAR chr; BOOL enabled; } charpos; } u; } char_iterator_backtrack; typedef struct ref_iterator_backtrack { backtrack_common common; /* Next iteration. */ struct sljit_label *matchingpath; } ref_iterator_backtrack; typedef struct recurse_entry { struct recurse_entry *next; /* Contains the function entry label. */ struct sljit_label *entry_label; /* Contains the function entry label. */ struct sljit_label *backtrack_label; /* Collects the entry calls until the function is not created. */ jump_list *entry_calls; /* Collects the backtrack calls until the function is not created. */ jump_list *backtrack_calls; /* Points to the starting opcode. */ sljit_sw start; } recurse_entry; typedef struct recurse_backtrack { backtrack_common common; /* Return to the matching path. */ struct sljit_label *matchingpath; /* Recursive pattern. */ recurse_entry *entry; /* Pattern is inlined. */ BOOL inlined_pattern; } recurse_backtrack; #define OP_THEN_TRAP OP_TABLE_LENGTH typedef struct then_trap_backtrack { backtrack_common common; /* If then_trap is not NULL, this structure contains the real then_trap for the backtracking path. */ struct then_trap_backtrack *then_trap; /* Points to the starting opcode. */ sljit_sw start; /* Exit point for the then opcodes of this alternative. */ jump_list *quit; /* Frame size of the current alternative. */ int framesize; } then_trap_backtrack; #define MAX_N_CHARS 12 #define MAX_DIFF_CHARS 5 typedef struct fast_forward_char_data { /* Number of characters in the chars array, 255 for any character. */ sljit_u8 count; /* Number of last UTF-8 characters in the chars array. */ sljit_u8 last_count; /* Available characters in the current position. */ PCRE2_UCHAR chars[MAX_DIFF_CHARS]; } fast_forward_char_data; #define MAX_CLASS_RANGE_SIZE 4 #define MAX_CLASS_CHARS_SIZE 3 typedef struct compiler_common { /* The sljit ceneric compiler. */ struct sljit_compiler *compiler; /* Compiled regular expression. */ pcre2_real_code *re; /* First byte code. */ PCRE2_SPTR start; /* Maps private data offset to each opcode. */ sljit_s32 *private_data_ptrs; /* Chain list of read-only data ptrs. */ void *read_only_data_head; /* Tells whether the capturing bracket is optimized. */ sljit_u8 *optimized_cbracket; /* Tells whether the starting offset is a target of then. */ sljit_u8 *then_offsets; /* Current position where a THEN must jump. */ then_trap_backtrack *then_trap; /* Starting offset of private data for capturing brackets. */ sljit_s32 cbra_ptr; /* Output vector starting point. Must be divisible by 2. */ sljit_s32 ovector_start; /* Points to the starting character of the current match. */ sljit_s32 start_ptr; /* Last known position of the requested byte. */ sljit_s32 req_char_ptr; /* Head of the last recursion. */ sljit_s32 recursive_head_ptr; /* First inspected character for partial matching. (Needed for avoiding zero length partial matches.) */ sljit_s32 start_used_ptr; /* Starting pointer for partial soft matches. */ sljit_s32 hit_start; /* Pointer of the match end position. */ sljit_s32 match_end_ptr; /* Points to the marked string. */ sljit_s32 mark_ptr; /* Recursive control verb management chain. */ sljit_s32 control_head_ptr; /* Points to the last matched capture block index. */ sljit_s32 capture_last_ptr; /* Fast forward skipping byte code pointer. */ PCRE2_SPTR fast_forward_bc_ptr; /* Locals used by fast fail optimization. */ sljit_s32 early_fail_start_ptr; sljit_s32 early_fail_end_ptr; /* Flipped and lower case tables. */ const sljit_u8 *fcc; sljit_sw lcc; /* Mode can be PCRE2_JIT_COMPLETE and others. */ int mode; /* TRUE, when empty match is accepted for partial matching. */ BOOL allow_empty_partial; /* TRUE, when minlength is greater than 0. */ BOOL might_be_empty; /* \K is found in the pattern. */ BOOL has_set_som; /* (*SKIP:arg) is found in the pattern. */ BOOL has_skip_arg; /* (*THEN) is found in the pattern. */ BOOL has_then; /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */ BOOL has_skip_in_assert_back; /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */ BOOL local_quit_available; /* Currently in a positive assertion. */ BOOL in_positive_assertion; /* Newline control. */ int nltype; sljit_u32 nlmax; sljit_u32 nlmin; int newline; int bsr_nltype; sljit_u32 bsr_nlmax; sljit_u32 bsr_nlmin; /* Dollar endonly. */ int endonly; /* Tables. */ sljit_sw ctypes; /* Named capturing brackets. */ PCRE2_SPTR name_table; sljit_sw name_count; sljit_sw name_entry_size; /* Labels and jump lists. */ struct sljit_label *partialmatchlabel; struct sljit_label *quit_label; struct sljit_label *abort_label; struct sljit_label *accept_label; struct sljit_label *ff_newline_shortcut; stub_list *stubs; recurse_entry *entries; recurse_entry *currententry; jump_list *partialmatch; jump_list *quit; jump_list *positive_assertion_quit; jump_list *abort; jump_list *failed_match; jump_list *accept; jump_list *calllimit; jump_list *stackalloc; jump_list *revertframes; jump_list *wordboundary; jump_list *anynewline; jump_list *hspace; jump_list *vspace; jump_list *casefulcmp; jump_list *caselesscmp; jump_list *reset_match; BOOL unset_backref; BOOL alt_circumflex; #ifdef SUPPORT_UNICODE BOOL utf; BOOL invalid_utf; BOOL ucp; /* Points to saving area for iref. */ sljit_s32 iref_ptr; jump_list *getucd; jump_list *getucdtype; #if PCRE2_CODE_UNIT_WIDTH == 8 jump_list *utfreadchar; jump_list *utfreadtype8; jump_list *utfpeakcharback; #endif #if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 jump_list *utfreadchar_invalid; jump_list *utfreadnewline_invalid; jump_list *utfmoveback_invalid; jump_list *utfpeakcharback_invalid; #endif #endif /* SUPPORT_UNICODE */ } compiler_common; /* For byte_sequence_compare. */ typedef struct compare_context { int length; int sourcereg; #if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED int ucharptr; union { sljit_s32 asint; sljit_u16 asushort; #if PCRE2_CODE_UNIT_WIDTH == 8 sljit_u8 asbyte; sljit_u8 asuchars[4]; #elif PCRE2_CODE_UNIT_WIDTH == 16 sljit_u16 asuchars[2]; #elif PCRE2_CODE_UNIT_WIDTH == 32 sljit_u32 asuchars[1]; #endif } c; union { sljit_s32 asint; sljit_u16 asushort; #if PCRE2_CODE_UNIT_WIDTH == 8 sljit_u8 asbyte; sljit_u8 asuchars[4]; #elif PCRE2_CODE_UNIT_WIDTH == 16 sljit_u16 asuchars[2]; #elif PCRE2_CODE_UNIT_WIDTH == 32 sljit_u32 asuchars[1]; #endif } oc; #endif } compare_context; /* Undefine sljit macros. */ #undef CMP /* Used for accessing the elements of the stack. */ #define STACK(i) ((i) * (int)sizeof(sljit_sw)) #ifdef SLJIT_PREF_SHIFT_REG #if SLJIT_PREF_SHIFT_REG == SLJIT_R2 /* Nothing. */ #elif SLJIT_PREF_SHIFT_REG == SLJIT_R3 #define SHIFT_REG_IS_R3 #else #error "Unsupported shift register" #endif #endif #define TMP1 SLJIT_R0 #ifdef SHIFT_REG_IS_R3 #define TMP2 SLJIT_R3 #define TMP3 SLJIT_R2 #else #define TMP2 SLJIT_R2 #define TMP3 SLJIT_R3 #endif #define STR_PTR SLJIT_R1 #define STR_END SLJIT_S0 #define STACK_TOP SLJIT_S1 #define STACK_LIMIT SLJIT_S2 #define COUNT_MATCH SLJIT_S3 #define ARGUMENTS SLJIT_S4 #define RETURN_ADDR SLJIT_R4 #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) #define HAS_VIRTUAL_REGISTERS 1 #else #define HAS_VIRTUAL_REGISTERS 0 #endif /* Local space layout. */ /* These two locals can be used by the current opcode. */ #define LOCALS0 (0 * sizeof(sljit_sw)) #define LOCALS1 (1 * sizeof(sljit_sw)) /* Two local variables for possessive quantifiers (char1 cannot use them). */ #define POSSESSIVE0 (2 * sizeof(sljit_sw)) #define POSSESSIVE1 (3 * sizeof(sljit_sw)) /* Max limit of recursions. */ #define LIMIT_MATCH (4 * sizeof(sljit_sw)) /* The output vector is stored on the stack, and contains pointers to characters. The vector data is divided into two groups: the first group contains the start / end character pointers, and the second is the start pointers when the end of the capturing group has not yet reached. */ #define OVECTOR_START (common->ovector_start) #define OVECTOR(i) (OVECTOR_START + (i) * (sljit_sw)sizeof(sljit_sw)) #define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * (sljit_sw)sizeof(sljit_sw)) #define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start]) #if PCRE2_CODE_UNIT_WIDTH == 8 #define MOV_UCHAR SLJIT_MOV_U8 #define IN_UCHARS(x) (x) #elif PCRE2_CODE_UNIT_WIDTH == 16 #define MOV_UCHAR SLJIT_MOV_U16 #define UCHAR_SHIFT (1) #define IN_UCHARS(x) ((x) * 2) #elif PCRE2_CODE_UNIT_WIDTH == 32 #define MOV_UCHAR SLJIT_MOV_U32 #define UCHAR_SHIFT (2) #define IN_UCHARS(x) ((x) * 4) #else #error Unsupported compiling mode #endif /* Shortcuts. */ #define DEFINE_COMPILER \ struct sljit_compiler *compiler = common->compiler #define OP1(op, dst, dstw, src, srcw) \ sljit_emit_op1(compiler, (op), (dst), (dstw), (src), (srcw)) #define OP2(op, dst, dstw, src1, src1w, src2, src2w) \ sljit_emit_op2(compiler, (op), (dst), (dstw), (src1), (src1w), (src2), (src2w)) #define OP_SRC(op, src, srcw) \ sljit_emit_op_src(compiler, (op), (src), (srcw)) #define LABEL() \ sljit_emit_label(compiler) #define JUMP(type) \ sljit_emit_jump(compiler, (type)) #define JUMPTO(type, label) \ sljit_set_label(sljit_emit_jump(compiler, (type)), (label)) #define JUMPHERE(jump) \ sljit_set_label((jump), sljit_emit_label(compiler)) #define SET_LABEL(jump, label) \ sljit_set_label((jump), (label)) #define CMP(type, src1, src1w, src2, src2w) \ sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)) #define CMPTO(type, src1, src1w, src2, src2w, label) \ sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label)) #define OP_FLAGS(op, dst, dstw, type) \ sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type)) #define CMOV(type, dst_reg, src, srcw) \ sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw)) #define GET_LOCAL_BASE(dst, dstw, offset) \ sljit_get_local_base(compiler, (dst), (dstw), (offset)) #define READ_CHAR_MAX 0x7fffffff #define INVALID_UTF_CHAR -1 #define UNASSIGNED_UTF_CHAR 888 #if defined SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 #define GETCHARINC_INVALID(c, ptr, end, invalid_action) \ { \ if (ptr[0] <= 0x7f) \ c = *ptr++; \ else if (ptr + 1 < end && ptr[1] >= 0x80 && ptr[1] < 0xc0) \ { \ c = ptr[1] - 0x80; \ \ if (ptr[0] >= 0xc2 && ptr[0] <= 0xdf) \ { \ c |= (ptr[0] - 0xc0) << 6; \ ptr += 2; \ } \ else if (ptr + 2 < end && ptr[2] >= 0x80 && ptr[2] < 0xc0) \ { \ c = c << 6 | (ptr[2] - 0x80); \ \ if (ptr[0] >= 0xe0 && ptr[0] <= 0xef) \ { \ c |= (ptr[0] - 0xe0) << 12; \ ptr += 3; \ \ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \ { \ invalid_action; \ } \ } \ else if (ptr + 3 < end && ptr[3] >= 0x80 && ptr[3] < 0xc0) \ { \ c = c << 6 | (ptr[3] - 0x80); \ \ if (ptr[0] >= 0xf0 && ptr[0] <= 0xf4) \ { \ c |= (ptr[0] - 0xf0) << 18; \ ptr += 4; \ \ if (c >= 0x110000 || c < 0x10000) \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } #define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \ { \ c = ptr[-1]; \ if (c <= 0x7f) \ ptr--; \ else if (ptr - 1 > start && ptr[-1] >= 0x80 && ptr[-1] < 0xc0) \ { \ c -= 0x80; \ \ if (ptr[-2] >= 0xc2 && ptr[-2] <= 0xdf) \ { \ c |= (ptr[-2] - 0xc0) << 6; \ ptr -= 2; \ } \ else if (ptr - 2 > start && ptr[-2] >= 0x80 && ptr[-2] < 0xc0) \ { \ c = c << 6 | (ptr[-2] - 0x80); \ \ if (ptr[-3] >= 0xe0 && ptr[-3] <= 0xef) \ { \ c |= (ptr[-3] - 0xe0) << 12; \ ptr -= 3; \ \ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \ { \ invalid_action; \ } \ } \ else if (ptr - 3 > start && ptr[-3] >= 0x80 && ptr[-3] < 0xc0) \ { \ c = c << 6 | (ptr[-3] - 0x80); \ \ if (ptr[-4] >= 0xf0 && ptr[-4] <= 0xf4) \ { \ c |= (ptr[-4] - 0xf0) << 18; \ ptr -= 4; \ \ if (c >= 0x110000 || c < 0x10000) \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } \ else \ { \ invalid_action; \ } \ } #elif PCRE2_CODE_UNIT_WIDTH == 16 #define GETCHARINC_INVALID(c, ptr, end, invalid_action) \ { \ if (ptr[0] < 0xd800 || ptr[0] >= 0xe000) \ c = *ptr++; \ else if (ptr[0] < 0xdc00 && ptr + 1 < end && ptr[1] >= 0xdc00 && ptr[1] < 0xe000) \ { \ c = (((ptr[0] - 0xd800) << 10) | (ptr[1] - 0xdc00)) + 0x10000; \ ptr += 2; \ } \ else \ { \ invalid_action; \ } \ } #define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \ { \ c = ptr[-1]; \ if (c < 0xd800 || c >= 0xe000) \ ptr--; \ else if (c >= 0xdc00 && ptr - 1 > start && ptr[-2] >= 0xd800 && ptr[-2] < 0xdc00) \ { \ c = (((ptr[-2] - 0xd800) << 10) | (c - 0xdc00)) + 0x10000; \ ptr -= 2; \ } \ else \ { \ invalid_action; \ } \ } #elif PCRE2_CODE_UNIT_WIDTH == 32 #define GETCHARINC_INVALID(c, ptr, end, invalid_action) \ { \ if (ptr[0] < 0xd800 || (ptr[0] >= 0xe000 && ptr[0] < 0x110000)) \ c = *ptr++; \ else \ { \ invalid_action; \ } \ } #define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \ { \ c = ptr[-1]; \ if (ptr[-1] < 0xd800 || (ptr[-1] >= 0xe000 && ptr[-1] < 0x110000)) \ ptr--; \ else \ { \ invalid_action; \ } \ } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ #endif /* SUPPORT_UNICODE */ static PCRE2_SPTR bracketend(PCRE2_SPTR cc) { SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); do cc += GET(cc, 1); while (*cc == OP_ALT); SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS); cc += 1 + LINK_SIZE; return cc; } static int no_alternatives(PCRE2_SPTR cc) { int count = 0; SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); do { cc += GET(cc, 1); count++; } while (*cc == OP_ALT); SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS); return count; } /* Functions whose might need modification for all new supported opcodes: next_opcode check_opcode_types set_private_data_ptrs get_framesize init_frame get_recurse_data_length copy_recurse_data compile_matchingpath compile_backtrackingpath */ static PCRE2_SPTR next_opcode(compiler_common *common, PCRE2_SPTR cc) { SLJIT_UNUSED_ARG(common); switch(*cc) { case OP_SOD: case OP_SOM: case OP_SET_SOM: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_NOTPROP: case OP_PROP: case OP_ANYNL: case OP_NOT_HSPACE: case OP_HSPACE: case OP_NOT_VSPACE: case OP_VSPACE: case OP_EXTUNI: case OP_EODN: case OP_EOD: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSSTAR: case OP_CRPOSPLUS: case OP_CRPOSQUERY: case OP_CRPOSRANGE: case OP_CLASS: case OP_NCLASS: case OP_REF: case OP_REFI: case OP_DNREF: case OP_DNREFI: case OP_RECURSE: case OP_CALLOUT: case OP_ALT: case OP_KET: case OP_KETRMAX: case OP_KETRMIN: case OP_KETRPOS: case OP_REVERSE: case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRA: case OP_BRAPOS: case OP_CBRA: case OP_CBRAPOS: case OP_COND: case OP_SBRA: case OP_SBRAPOS: case OP_SCBRA: case OP_SCBRAPOS: case OP_SCOND: case OP_CREF: case OP_DNCREF: case OP_RREF: case OP_DNRREF: case OP_FALSE: case OP_TRUE: case OP_BRAZERO: case OP_BRAMINZERO: case OP_BRAPOSZERO: case OP_PRUNE: case OP_SKIP: case OP_THEN: case OP_COMMIT: case OP_FAIL: case OP_ACCEPT: case OP_ASSERT_ACCEPT: case OP_CLOSE: case OP_SKIPZERO: return cc + PRIV(OP_lengths)[*cc]; case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_QUERY: case OP_MINQUERY: case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_QUERYI: case OP_MINQUERYI: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: cc += PRIV(OP_lengths)[*cc]; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif return cc; /* Special cases. */ case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: case OP_TYPEPOSUPTO: return cc + PRIV(OP_lengths)[*cc] - 1; case OP_ANYBYTE: #ifdef SUPPORT_UNICODE if (common->utf) return NULL; #endif return cc + 1; case OP_CALLOUT_STR: return cc + GET(cc, 1 + 2*LINK_SIZE); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: return cc + GET(cc, 1); #endif case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: return cc + 1 + 2 + cc[1]; default: SLJIT_UNREACHABLE(); return NULL; } } static BOOL check_opcode_types(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend) { int count; PCRE2_SPTR slot; PCRE2_SPTR assert_back_end = cc - 1; PCRE2_SPTR assert_na_end = cc - 1; /* Calculate important variables (like stack size) and checks whether all opcodes are supported. */ while (cc < ccend) { switch(*cc) { case OP_SET_SOM: common->has_set_som = TRUE; common->might_be_empty = TRUE; cc += 1; break; case OP_REFI: #ifdef SUPPORT_UNICODE if (common->iref_ptr == 0) { common->iref_ptr = common->ovector_start; common->ovector_start += 3 * sizeof(sljit_sw); } #endif /* SUPPORT_UNICODE */ /* Fall through. */ case OP_REF: common->optimized_cbracket[GET2(cc, 1)] = 0; cc += 1 + IMM2_SIZE; break; case OP_ASSERT_NA: case OP_ASSERTBACK_NA: slot = bracketend(cc); if (slot > assert_na_end) assert_na_end = slot; cc += 1 + LINK_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] = 0; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_COND: case OP_SCOND: /* Only AUTO_CALLOUT can insert this opcode. We do not intend to support this case. */ if (cc[1 + LINK_SIZE] == OP_CALLOUT || cc[1 + LINK_SIZE] == OP_CALLOUT_STR) return FALSE; cc += 1 + LINK_SIZE; break; case OP_CREF: common->optimized_cbracket[GET2(cc, 1)] = 0; cc += 1 + IMM2_SIZE; break; case OP_DNREF: case OP_DNREFI: case OP_DNCREF: count = GET2(cc, 1 + IMM2_SIZE); slot = common->name_table + GET2(cc, 1) * common->name_entry_size; while (count-- > 0) { common->optimized_cbracket[GET2(slot, 0)] = 0; slot += common->name_entry_size; } cc += 1 + 2 * IMM2_SIZE; break; case OP_RECURSE: /* Set its value only once. */ if (common->recursive_head_ptr == 0) { common->recursive_head_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } cc += 1 + LINK_SIZE; break; case OP_CALLOUT: case OP_CALLOUT_STR: if (common->capture_last_ptr == 0) { common->capture_last_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } cc += (*cc == OP_CALLOUT) ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2*LINK_SIZE); break; case OP_ASSERTBACK: slot = bracketend(cc); if (slot > assert_back_end) assert_back_end = slot; cc += 1 + LINK_SIZE; break; case OP_THEN_ARG: common->has_then = TRUE; common->control_head_ptr = 1; /* Fall through. */ case OP_COMMIT_ARG: case OP_PRUNE_ARG: if (cc < assert_na_end) return FALSE; /* Fall through */ case OP_MARK: if (common->mark_ptr == 0) { common->mark_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } cc += 1 + 2 + cc[1]; break; case OP_THEN: common->has_then = TRUE; common->control_head_ptr = 1; cc += 1; break; case OP_SKIP: if (cc < assert_back_end) common->has_skip_in_assert_back = TRUE; if (cc < assert_na_end) return FALSE; cc += 1; break; case OP_SKIP_ARG: common->control_head_ptr = 1; common->has_skip_arg = TRUE; if (cc < assert_back_end) common->has_skip_in_assert_back = TRUE; if (cc < assert_na_end) return FALSE; cc += 1 + 2 + cc[1]; break; case OP_PRUNE: case OP_COMMIT: case OP_ASSERT_ACCEPT: if (cc < assert_na_end) return FALSE; cc++; break; default: cc = next_opcode(common, cc); if (cc == NULL) return FALSE; break; } } return TRUE; } #define EARLY_FAIL_ENHANCE_MAX (1 + 3) /* start: 0 - skip / early fail allowed 1 - only early fail with range allowed >1 - (start - 1) early fail is processed return: current number of iterators enhanced with fast fail */ static int detect_early_fail(compiler_common *common, PCRE2_SPTR cc, int *private_data_start, sljit_s32 depth, int start, BOOL fast_forward_allowed) { PCRE2_SPTR begin = cc; PCRE2_SPTR next_alt; PCRE2_SPTR end; PCRE2_SPTR accelerated_start; BOOL prev_fast_forward_allowed; int result = 0; int count; SLJIT_ASSERT(*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA); SLJIT_ASSERT(*cc != OP_CBRA || common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] != 0); SLJIT_ASSERT(start < EARLY_FAIL_ENHANCE_MAX); next_alt = cc + GET(cc, 1); if (*next_alt == OP_ALT) fast_forward_allowed = FALSE; do { count = start; cc += 1 + LINK_SIZE + ((*cc == OP_CBRA) ? IMM2_SIZE : 0); while (TRUE) { accelerated_start = NULL; switch(*cc) { case OP_SOD: case OP_SOM: case OP_SET_SOM: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_EODN: case OP_EOD: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: /* Zero width assertions. */ cc++; continue; case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_ANYBYTE: case OP_NOT_HSPACE: case OP_HSPACE: case OP_NOT_VSPACE: case OP_VSPACE: fast_forward_allowed = FALSE; cc++; continue; case OP_ANYNL: case OP_EXTUNI: fast_forward_allowed = FALSE; if (count == 0) count = 1; cc++; continue; case OP_NOTPROP: case OP_PROP: fast_forward_allowed = FALSE; cc += 1 + 2; continue; case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: fast_forward_allowed = FALSE; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif continue; case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: /* The type or prop opcode is skipped in the next iteration. */ cc += 1; if (cc[0] != OP_ANYNL && cc[0] != OP_EXTUNI) { accelerated_start = cc - 1; break; } if (count == 0) count = 1; fast_forward_allowed = FALSE; continue; case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSUPTO: cc += IMM2_SIZE; /* Fall through */ case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSQUERY: /* The type or prop opcode is skipped in the next iteration. */ fast_forward_allowed = FALSE; if (count == 0) count = 1; cc += 1; continue; case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_POSSTAR: case OP_POSPLUS: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_POSSTARI: case OP_POSPLUSI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: accelerated_start = cc; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSUPTO: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSUPTOI: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSUPTO: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSUPTOI: cc += IMM2_SIZE; /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: case OP_QUERYI: case OP_MINQUERYI: case OP_POSQUERYI: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTPOSQUERY: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTPOSQUERYI: fast_forward_allowed = FALSE; if (count == 0) count = 1; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif continue; case OP_CLASS: case OP_NCLASS: #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: accelerated_start = cc; cc += ((*cc == OP_XCLASS) ? GET(cc, 1) : (unsigned int)(1 + (32 / sizeof(PCRE2_UCHAR)))); #else accelerated_start = cc; cc += (1 + (32 / sizeof(PCRE2_UCHAR))); #endif switch (*cc) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSSTAR: case OP_CRPOSPLUS: cc++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: cc += 2 * IMM2_SIZE; /* Fall through */ case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSQUERY: cc++; if (count == 0) count = 1; /* Fall through */ default: accelerated_start = NULL; fast_forward_allowed = FALSE; continue; } break; case OP_ONCE: case OP_BRA: case OP_CBRA: end = cc + GET(cc, 1); prev_fast_forward_allowed = fast_forward_allowed; fast_forward_allowed = FALSE; if (depth >= 4) break; end = bracketend(cc) - (1 + LINK_SIZE); if (*end != OP_KET || (*cc == OP_CBRA && common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)) break; count = detect_early_fail(common, cc, private_data_start, depth + 1, count, prev_fast_forward_allowed); if (PRIVATE_DATA(cc) != 0) common->private_data_ptrs[begin - common->start] = 1; if (count < EARLY_FAIL_ENHANCE_MAX) { cc = end + (1 + LINK_SIZE); continue; } break; case OP_KET: SLJIT_ASSERT(PRIVATE_DATA(cc) == 0); if (cc >= next_alt) break; cc += 1 + LINK_SIZE; continue; } if (accelerated_start != NULL) { if (count == 0) { count++; if (fast_forward_allowed) { common->fast_forward_bc_ptr = accelerated_start; common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_skip; *private_data_start += sizeof(sljit_sw); } else { common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail; if (common->early_fail_start_ptr == 0) common->early_fail_start_ptr = *private_data_start; *private_data_start += sizeof(sljit_sw); common->early_fail_end_ptr = *private_data_start; if (*private_data_start > SLJIT_MAX_LOCAL_SIZE) return EARLY_FAIL_ENHANCE_MAX; } } else { common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail_range; if (common->early_fail_start_ptr == 0) common->early_fail_start_ptr = *private_data_start; *private_data_start += 2 * sizeof(sljit_sw); common->early_fail_end_ptr = *private_data_start; if (*private_data_start > SLJIT_MAX_LOCAL_SIZE) return EARLY_FAIL_ENHANCE_MAX; } /* Cannot be part of a repeat. */ common->private_data_ptrs[begin - common->start] = 1; count++; if (count < EARLY_FAIL_ENHANCE_MAX) continue; } break; } if (*cc != OP_ALT && *cc != OP_KET) result = EARLY_FAIL_ENHANCE_MAX; else if (result < count) result = count; cc = next_alt; next_alt = cc + GET(cc, 1); } while (*cc == OP_ALT); return result; } static int get_class_iterator_size(PCRE2_SPTR cc) { sljit_u32 min; sljit_u32 max; switch(*cc) { case OP_CRSTAR: case OP_CRPLUS: return 2; case OP_CRMINSTAR: case OP_CRMINPLUS: case OP_CRQUERY: case OP_CRMINQUERY: return 1; case OP_CRRANGE: case OP_CRMINRANGE: min = GET2(cc, 1); max = GET2(cc, 1 + IMM2_SIZE); if (max == 0) return (*cc == OP_CRRANGE) ? 2 : 1; max -= min; if (max > 2) max = 2; return max; default: return 0; } } static BOOL detect_repeat(compiler_common *common, PCRE2_SPTR begin) { PCRE2_SPTR end = bracketend(begin); PCRE2_SPTR next; PCRE2_SPTR next_end; PCRE2_SPTR max_end; PCRE2_UCHAR type; sljit_sw length = end - begin; sljit_s32 min, max, i; /* Detect fixed iterations first. */ if (end[-(1 + LINK_SIZE)] != OP_KET || PRIVATE_DATA(begin) != 0) return FALSE; /* /(?:AB){4,6}/ is currently converted to /(?:AB){3}(?AB){1,3}/ * Skip the check of the second part. */ if (PRIVATE_DATA(end - LINK_SIZE) == 0) return TRUE; next = end; min = 1; while (1) { if (*next != *begin) break; next_end = bracketend(next); if (next_end - next != length || memcmp(begin, next, IN_UCHARS(length)) != 0) break; next = next_end; min++; } if (min == 2) return FALSE; max = 0; max_end = next; if (*next == OP_BRAZERO || *next == OP_BRAMINZERO) { type = *next; while (1) { if (next[0] != type || next[1] != OP_BRA || next[2 + LINK_SIZE] != *begin) break; next_end = bracketend(next + 2 + LINK_SIZE); if (next_end - next != (length + 2 + LINK_SIZE) || memcmp(begin, next + 2 + LINK_SIZE, IN_UCHARS(length)) != 0) break; next = next_end; max++; } if (next[0] == type && next[1] == *begin && max >= 1) { next_end = bracketend(next + 1); if (next_end - next == (length + 1) && memcmp(begin, next + 1, IN_UCHARS(length)) == 0) { for (i = 0; i < max; i++, next_end += 1 + LINK_SIZE) if (*next_end != OP_KET) break; if (i == max) { common->private_data_ptrs[max_end - common->start - LINK_SIZE] = next_end - max_end; common->private_data_ptrs[max_end - common->start - LINK_SIZE + 1] = (type == OP_BRAZERO) ? OP_UPTO : OP_MINUPTO; /* +2 the original and the last. */ common->private_data_ptrs[max_end - common->start - LINK_SIZE + 2] = max + 2; if (min == 1) return TRUE; min--; max_end -= (1 + LINK_SIZE) + GET(max_end, -LINK_SIZE); } } } } if (min >= 3) { common->private_data_ptrs[end - common->start - LINK_SIZE] = max_end - end; common->private_data_ptrs[end - common->start - LINK_SIZE + 1] = OP_EXACT; common->private_data_ptrs[end - common->start - LINK_SIZE + 2] = min; return TRUE; } return FALSE; } #define CASE_ITERATOR_PRIVATE_DATA_1 \ case OP_MINSTAR: \ case OP_MINPLUS: \ case OP_QUERY: \ case OP_MINQUERY: \ case OP_MINSTARI: \ case OP_MINPLUSI: \ case OP_QUERYI: \ case OP_MINQUERYI: \ case OP_NOTMINSTAR: \ case OP_NOTMINPLUS: \ case OP_NOTQUERY: \ case OP_NOTMINQUERY: \ case OP_NOTMINSTARI: \ case OP_NOTMINPLUSI: \ case OP_NOTQUERYI: \ case OP_NOTMINQUERYI: #define CASE_ITERATOR_PRIVATE_DATA_2A \ case OP_STAR: \ case OP_PLUS: \ case OP_STARI: \ case OP_PLUSI: \ case OP_NOTSTAR: \ case OP_NOTPLUS: \ case OP_NOTSTARI: \ case OP_NOTPLUSI: #define CASE_ITERATOR_PRIVATE_DATA_2B \ case OP_UPTO: \ case OP_MINUPTO: \ case OP_UPTOI: \ case OP_MINUPTOI: \ case OP_NOTUPTO: \ case OP_NOTMINUPTO: \ case OP_NOTUPTOI: \ case OP_NOTMINUPTOI: #define CASE_ITERATOR_TYPE_PRIVATE_DATA_1 \ case OP_TYPEMINSTAR: \ case OP_TYPEMINPLUS: \ case OP_TYPEQUERY: \ case OP_TYPEMINQUERY: #define CASE_ITERATOR_TYPE_PRIVATE_DATA_2A \ case OP_TYPESTAR: \ case OP_TYPEPLUS: #define CASE_ITERATOR_TYPE_PRIVATE_DATA_2B \ case OP_TYPEUPTO: \ case OP_TYPEMINUPTO: static void set_private_data_ptrs(compiler_common *common, int *private_data_start, PCRE2_SPTR ccend) { PCRE2_SPTR cc = common->start; PCRE2_SPTR alternative; PCRE2_SPTR end = NULL; int private_data_ptr = *private_data_start; int space, size, bracketlen; BOOL repeat_check = TRUE; while (cc < ccend) { space = 0; size = 0; bracketlen = 0; if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE) break; /* When the bracket is prefixed by a zero iteration, skip the repeat check (at this point). */ if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) { if (detect_repeat(common, cc)) { /* These brackets are converted to repeats, so no global based single character repeat is allowed. */ if (cc >= end) end = bracketend(cc); } } repeat_check = TRUE; switch(*cc) { case OP_KET: if (common->private_data_ptrs[cc + 1 - common->start] != 0) { common->private_data_ptrs[cc - common->start] = private_data_ptr; private_data_ptr += sizeof(sljit_sw); cc += common->private_data_ptrs[cc + 1 - common->start]; } cc += 1 + LINK_SIZE; break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: case OP_SCOND: common->private_data_ptrs[cc - common->start] = private_data_ptr; private_data_ptr += sizeof(sljit_sw); bracketlen = 1 + LINK_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: common->private_data_ptrs[cc - common->start] = private_data_ptr; private_data_ptr += sizeof(sljit_sw); bracketlen = 1 + LINK_SIZE + IMM2_SIZE; break; case OP_COND: /* Might be a hidden SCOND. */ common->private_data_ptrs[cc - common->start] = 0; alternative = cc + GET(cc, 1); if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) { common->private_data_ptrs[cc - common->start] = private_data_ptr; private_data_ptr += sizeof(sljit_sw); } bracketlen = 1 + LINK_SIZE; break; case OP_BRA: bracketlen = 1 + LINK_SIZE; break; case OP_CBRA: case OP_SCBRA: bracketlen = 1 + LINK_SIZE + IMM2_SIZE; break; case OP_BRAZERO: case OP_BRAMINZERO: case OP_BRAPOSZERO: size = 1; repeat_check = FALSE; break; CASE_ITERATOR_PRIVATE_DATA_1 size = -2; space = 1; break; CASE_ITERATOR_PRIVATE_DATA_2A size = -2; space = 2; break; CASE_ITERATOR_PRIVATE_DATA_2B size = -(2 + IMM2_SIZE); space = 2; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_1 size = 1; space = 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2A size = 1; if (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI) space = 2; break; case OP_TYPEUPTO: size = 1 + IMM2_SIZE; if (cc[1 + IMM2_SIZE] != OP_ANYNL && cc[1 + IMM2_SIZE] != OP_EXTUNI) space = 2; break; case OP_TYPEMINUPTO: size = 1 + IMM2_SIZE; space = 2; break; case OP_CLASS: case OP_NCLASS: size = 1 + 32 / sizeof(PCRE2_UCHAR); space = get_class_iterator_size(cc + size); break; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: size = GET(cc, 1); space = get_class_iterator_size(cc + size); break; #endif default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); break; } /* Character iterators, which are not inside a repeated bracket, gets a private slot instead of allocating it on the stack. */ if (space > 0 && cc >= end) { common->private_data_ptrs[cc - common->start] = private_data_ptr; private_data_ptr += sizeof(sljit_sw) * space; } if (size != 0) { if (size < 0) { cc += -size; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif } else cc += size; } if (bracketlen > 0) { if (cc >= end) { end = bracketend(cc); if (end[-1 - LINK_SIZE] == OP_KET) end = NULL; } cc += bracketlen; } } *private_data_start = private_data_ptr; } /* Returns with a frame_types (always < 0) if no need for frame. */ static int get_framesize(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL recursive, BOOL *needs_control_head) { int length = 0; int possessive = 0; BOOL stack_restore = FALSE; BOOL setsom_found = recursive; BOOL setmark_found = recursive; /* The last capture is a local variable even for recursions. */ BOOL capture_last_found = FALSE; #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD SLJIT_ASSERT(common->control_head_ptr != 0); *needs_control_head = TRUE; #else *needs_control_head = FALSE; #endif if (ccend == NULL) { ccend = bracketend(cc) - (1 + LINK_SIZE); if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS)) { possessive = length = (common->capture_last_ptr != 0) ? 5 : 3; /* This is correct regardless of common->capture_last_ptr. */ capture_last_found = TRUE; } cc = next_opcode(common, cc); } SLJIT_ASSERT(cc != NULL); while (cc < ccend) switch(*cc) { case OP_SET_SOM: SLJIT_ASSERT(common->has_set_som); stack_restore = TRUE; if (!setsom_found) { length += 2; setsom_found = TRUE; } cc += 1; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); stack_restore = TRUE; if (!setmark_found) { length += 2; setmark_found = TRUE; } if (common->control_head_ptr != 0) *needs_control_head = TRUE; cc += 1 + 2 + cc[1]; break; case OP_RECURSE: stack_restore = TRUE; if (common->has_set_som && !setsom_found) { length += 2; setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { length += 2; setmark_found = TRUE; } if (common->capture_last_ptr != 0 && !capture_last_found) { length += 2; capture_last_found = TRUE; } cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_CBRAPOS: case OP_SCBRA: case OP_SCBRAPOS: stack_restore = TRUE; if (common->capture_last_ptr != 0 && !capture_last_found) { length += 2; capture_last_found = TRUE; } length += 3; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_THEN: stack_restore = TRUE; if (common->control_head_ptr != 0) *needs_control_head = TRUE; cc ++; break; default: stack_restore = TRUE; /* Fall through. */ case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_ANYBYTE: case OP_NOTPROP: case OP_PROP: case OP_ANYNL: case OP_NOT_HSPACE: case OP_HSPACE: case OP_NOT_VSPACE: case OP_VSPACE: case OP_EXTUNI: case OP_EODN: case OP_EOD: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: case OP_TYPEEXACT: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: case OP_TYPEPOSUPTO: case OP_CLASS: case OP_NCLASS: case OP_XCLASS: case OP_CALLOUT: case OP_CALLOUT_STR: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); break; } /* Possessive quantifiers can use a special case. */ if (SLJIT_UNLIKELY(possessive == length)) return stack_restore ? no_frame : no_stack; if (length > 0) return length + 1; return stack_restore ? no_frame : no_stack; } static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop) { DEFINE_COMPILER; BOOL setsom_found = FALSE; BOOL setmark_found = FALSE; /* The last capture is a local variable even for recursions. */ BOOL capture_last_found = FALSE; int offset; /* >= 1 + shortest item size (2) */ SLJIT_UNUSED_ARG(stacktop); SLJIT_ASSERT(stackpos >= stacktop + 2); stackpos = STACK(stackpos); if (ccend == NULL) { ccend = bracketend(cc) - (1 + LINK_SIZE); if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS) cc = next_opcode(common, cc); } SLJIT_ASSERT(cc != NULL); while (cc < ccend) switch(*cc) { case OP_SET_SOM: SLJIT_ASSERT(common->has_set_som); if (!setsom_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } cc += 1; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); if (!setmark_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } cc += 1 + 2 + cc[1]; break; case OP_RECURSE: if (common->has_set_som && !setsom_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } if (common->capture_last_ptr != 0 && !capture_last_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_CBRAPOS: case OP_SCBRA: case OP_SCBRAPOS: if (common->capture_last_ptr != 0 && !capture_last_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } offset = (GET2(cc, 1 + LINK_SIZE)) << 1; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); stackpos -= (int)sizeof(sljit_sw); cc += 1 + LINK_SIZE + IMM2_SIZE; break; default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); break; } OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0); SLJIT_ASSERT(stackpos == STACK(stacktop)); } #define RECURSE_TMP_REG_COUNT 3 typedef struct delayed_mem_copy_status { struct sljit_compiler *compiler; int store_bases[RECURSE_TMP_REG_COUNT]; int store_offsets[RECURSE_TMP_REG_COUNT]; int tmp_regs[RECURSE_TMP_REG_COUNT]; int saved_tmp_regs[RECURSE_TMP_REG_COUNT]; int next_tmp_reg; } delayed_mem_copy_status; static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common) { int i; for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) { SLJIT_ASSERT(status->tmp_regs[i] >= 0); SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]); status->store_bases[i] = -1; } status->next_tmp_reg = 0; status->compiler = common->compiler; } static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset, int store_base, sljit_sw store_offset) { struct sljit_compiler *compiler = status->compiler; int next_tmp_reg = status->next_tmp_reg; int tmp_reg = status->tmp_regs[next_tmp_reg]; SLJIT_ASSERT(load_base > 0 && store_base > 0); if (status->store_bases[next_tmp_reg] == -1) { /* Preserve virtual registers. */ if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0) OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0); } else OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset); status->store_bases[next_tmp_reg] = store_base; status->store_offsets[next_tmp_reg] = store_offset; status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; } static void delayed_mem_copy_finish(delayed_mem_copy_status *status) { struct sljit_compiler *compiler = status->compiler; int next_tmp_reg = status->next_tmp_reg; int tmp_reg, saved_tmp_reg, i; for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) { if (status->store_bases[next_tmp_reg] != -1) { tmp_reg = status->tmp_regs[next_tmp_reg]; saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg]; OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); /* Restore virtual registers. */ if (sljit_get_register_index(saved_tmp_reg) < 0) OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0); } next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; } } #undef RECURSE_TMP_REG_COUNT static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL *needs_control_head, BOOL *has_quit, BOOL *has_accept) { int length = 1; int size; PCRE2_SPTR alternative; BOOL quit_found = FALSE; BOOL accept_found = FALSE; BOOL setsom_found = FALSE; BOOL setmark_found = FALSE; BOOL capture_last_found = FALSE; BOOL control_head_found = FALSE; #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD SLJIT_ASSERT(common->control_head_ptr != 0); control_head_found = TRUE; #endif /* Calculate the sum of the private machine words. */ while (cc < ccend) { size = 0; switch(*cc) { case OP_SET_SOM: SLJIT_ASSERT(common->has_set_som); setsom_found = TRUE; cc += 1; break; case OP_RECURSE: if (common->has_set_som) setsom_found = TRUE; if (common->mark_ptr != 0) setmark_found = TRUE; if (common->capture_last_ptr != 0) capture_last_found = TRUE; cc += 1 + LINK_SIZE; break; case OP_KET: if (PRIVATE_DATA(cc) != 0) { length++; SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); cc += PRIVATE_DATA(cc + 1); } cc += 1 + LINK_SIZE; break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: case OP_SCOND: length++; SLJIT_ASSERT(PRIVATE_DATA(cc) != 0); cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_SCBRA: length += 2; if (common->capture_last_ptr != 0) capture_last_found = TRUE; if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) length++; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: length += 2 + 2; if (common->capture_last_ptr != 0) capture_last_found = TRUE; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_COND: /* Might be a hidden SCOND. */ alternative = cc + GET(cc, 1); if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) length++; cc += 1 + LINK_SIZE; break; CASE_ITERATOR_PRIVATE_DATA_1 if (PRIVATE_DATA(cc) != 0) length++; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_PRIVATE_DATA_2A if (PRIVATE_DATA(cc) != 0) length += 2; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_PRIVATE_DATA_2B if (PRIVATE_DATA(cc) != 0) length += 2; cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_TYPE_PRIVATE_DATA_1 if (PRIVATE_DATA(cc) != 0) length++; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2A if (PRIVATE_DATA(cc) != 0) length += 2; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2B if (PRIVATE_DATA(cc) != 0) length += 2; cc += 1 + IMM2_SIZE; break; case OP_CLASS: case OP_NCLASS: #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); #else size = 1 + 32 / (int)sizeof(PCRE2_UCHAR); #endif if (PRIVATE_DATA(cc) != 0) length += get_class_iterator_size(cc + size); cc += size; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); if (!setmark_found) setmark_found = TRUE; if (common->control_head_ptr != 0) control_head_found = TRUE; if (*cc != OP_MARK) quit_found = TRUE; cc += 1 + 2 + cc[1]; break; case OP_PRUNE: case OP_SKIP: case OP_COMMIT: quit_found = TRUE; cc++; break; case OP_SKIP_ARG: quit_found = TRUE; cc += 1 + 2 + cc[1]; break; case OP_THEN: SLJIT_ASSERT(common->control_head_ptr != 0); quit_found = TRUE; if (!control_head_found) control_head_found = TRUE; cc++; break; case OP_ACCEPT: case OP_ASSERT_ACCEPT: accept_found = TRUE; cc++; break; default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); break; } } SLJIT_ASSERT(cc == ccend); if (control_head_found) length++; if (capture_last_found) length++; if (quit_found) { if (setsom_found) length++; if (setmark_found) length++; } *needs_control_head = control_head_found; *has_quit = quit_found; *has_accept = accept_found; return length; } enum copy_recurse_data_types { recurse_copy_from_global, recurse_copy_private_to_global, recurse_copy_shared_to_global, recurse_copy_kept_shared_to_global, recurse_swap_global }; static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int type, int stackptr, int stacktop, BOOL has_quit) { delayed_mem_copy_status status; PCRE2_SPTR alternative; sljit_sw private_srcw[2]; sljit_sw shared_srcw[3]; sljit_sw kept_shared_srcw[2]; int private_count, shared_count, kept_shared_count; int from_sp, base_reg, offset, i; BOOL setsom_found = FALSE; BOOL setmark_found = FALSE; BOOL capture_last_found = FALSE; BOOL control_head_found = FALSE; #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD SLJIT_ASSERT(common->control_head_ptr != 0); control_head_found = TRUE; #endif switch (type) { case recurse_copy_from_global: from_sp = TRUE; base_reg = STACK_TOP; break; case recurse_copy_private_to_global: case recurse_copy_shared_to_global: case recurse_copy_kept_shared_to_global: from_sp = FALSE; base_reg = STACK_TOP; break; default: SLJIT_ASSERT(type == recurse_swap_global); from_sp = FALSE; base_reg = TMP2; break; } stackptr = STACK(stackptr); stacktop = STACK(stacktop); status.tmp_regs[0] = TMP1; status.saved_tmp_regs[0] = TMP1; if (base_reg != TMP2) { status.tmp_regs[1] = TMP2; status.saved_tmp_regs[1] = TMP2; } else { status.saved_tmp_regs[1] = RETURN_ADDR; if (HAS_VIRTUAL_REGISTERS) status.tmp_regs[1] = STR_PTR; else status.tmp_regs[1] = RETURN_ADDR; } status.saved_tmp_regs[2] = TMP3; if (HAS_VIRTUAL_REGISTERS) status.tmp_regs[2] = STR_END; else status.tmp_regs[2] = TMP3; delayed_mem_copy_init(&status, common); if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) { SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); if (!from_sp) delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr); if (from_sp || type == recurse_swap_global) delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr); } stackptr += sizeof(sljit_sw); #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD if (type != recurse_copy_shared_to_global) { if (!from_sp) delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr); if (from_sp || type == recurse_swap_global) delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr); } stackptr += sizeof(sljit_sw); #endif while (cc < ccend) { private_count = 0; shared_count = 0; kept_shared_count = 0; switch(*cc) { case OP_SET_SOM: SLJIT_ASSERT(common->has_set_som); if (has_quit && !setsom_found) { kept_shared_srcw[0] = OVECTOR(0); kept_shared_count = 1; setsom_found = TRUE; } cc += 1; break; case OP_RECURSE: if (has_quit) { if (common->has_set_som && !setsom_found) { kept_shared_srcw[0] = OVECTOR(0); kept_shared_count = 1; setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { kept_shared_srcw[kept_shared_count] = common->mark_ptr; kept_shared_count++; setmark_found = TRUE; } } if (common->capture_last_ptr != 0 && !capture_last_found) { shared_srcw[0] = common->capture_last_ptr; shared_count = 1; capture_last_found = TRUE; } cc += 1 + LINK_SIZE; break; case OP_KET: if (PRIVATE_DATA(cc) != 0) { private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); cc += PRIVATE_DATA(cc + 1); } cc += 1 + LINK_SIZE; break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: case OP_SCOND: private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_SCBRA: offset = (GET2(cc, 1 + LINK_SIZE)) << 1; shared_srcw[0] = OVECTOR(offset); shared_srcw[1] = OVECTOR(offset + 1); shared_count = 2; if (common->capture_last_ptr != 0 && !capture_last_found) { shared_srcw[2] = common->capture_last_ptr; shared_count = 3; capture_last_found = TRUE; } if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) { private_count = 1; private_srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); } cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: offset = (GET2(cc, 1 + LINK_SIZE)) << 1; shared_srcw[0] = OVECTOR(offset); shared_srcw[1] = OVECTOR(offset + 1); shared_count = 2; if (common->capture_last_ptr != 0 && !capture_last_found) { shared_srcw[2] = common->capture_last_ptr; shared_count = 3; capture_last_found = TRUE; } private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_COND: /* Might be a hidden SCOND. */ alternative = cc + GET(cc, 1); if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) { private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); } cc += 1 + LINK_SIZE; break; CASE_ITERATOR_PRIVATE_DATA_1 if (PRIVATE_DATA(cc)) { private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); } cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_PRIVATE_DATA_2A if (PRIVATE_DATA(cc)) { private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); } cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_PRIVATE_DATA_2B if (PRIVATE_DATA(cc)) { private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); } cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif break; CASE_ITERATOR_TYPE_PRIVATE_DATA_1 if (PRIVATE_DATA(cc)) { private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); } cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2A if (PRIVATE_DATA(cc)) { private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); } cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2B if (PRIVATE_DATA(cc)) { private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); } cc += 1 + IMM2_SIZE; break; case OP_CLASS: case OP_NCLASS: #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); #else i = 1 + 32 / (int)sizeof(PCRE2_UCHAR); #endif if (PRIVATE_DATA(cc) != 0) switch(get_class_iterator_size(cc + i)) { case 1: private_count = 1; private_srcw[0] = PRIVATE_DATA(cc); break; case 2: private_count = 2; private_srcw[0] = PRIVATE_DATA(cc); private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); break; default: SLJIT_UNREACHABLE(); break; } cc += i; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); if (has_quit && !setmark_found) { kept_shared_srcw[0] = common->mark_ptr; kept_shared_count = 1; setmark_found = TRUE; } if (common->control_head_ptr != 0 && !control_head_found) { private_srcw[0] = common->control_head_ptr; private_count = 1; control_head_found = TRUE; } cc += 1 + 2 + cc[1]; break; case OP_THEN: SLJIT_ASSERT(common->control_head_ptr != 0); if (!control_head_found) { private_srcw[0] = common->control_head_ptr; private_count = 1; control_head_found = TRUE; } cc++; break; default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); break; } if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) { SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); for (i = 0; i < private_count; i++) { SLJIT_ASSERT(private_srcw[i] != 0); if (!from_sp) delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]); if (from_sp || type == recurse_swap_global) delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr); stackptr += sizeof(sljit_sw); } } else stackptr += sizeof(sljit_sw) * private_count; if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global) { SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global); for (i = 0; i < shared_count; i++) { SLJIT_ASSERT(shared_srcw[i] != 0); if (!from_sp) delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]); if (from_sp || type == recurse_swap_global) delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr); stackptr += sizeof(sljit_sw); } } else stackptr += sizeof(sljit_sw) * shared_count; if (type != recurse_copy_private_to_global && type != recurse_swap_global) { SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global); for (i = 0; i < kept_shared_count; i++) { SLJIT_ASSERT(kept_shared_srcw[i] != 0); if (!from_sp) delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]); if (from_sp || type == recurse_swap_global) delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr); stackptr += sizeof(sljit_sw); } } else stackptr += sizeof(sljit_sw) * kept_shared_count; } SLJIT_ASSERT(cc == ccend && stackptr == stacktop); delayed_mem_copy_finish(&status); } static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset) { PCRE2_SPTR end = bracketend(cc); BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT; /* Assert captures then. */ if (*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) current_offset = NULL; /* Conditional block does not. */ if (*cc == OP_COND || *cc == OP_SCOND) has_alternatives = FALSE; cc = next_opcode(common, cc); if (has_alternatives) current_offset = common->then_offsets + (cc - common->start); while (cc < end) { if ((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND)) cc = set_then_offsets(common, cc, current_offset); else { if (*cc == OP_ALT && has_alternatives) current_offset = common->then_offsets + (cc + 1 + LINK_SIZE - common->start); if (*cc >= OP_THEN && *cc <= OP_THEN_ARG && current_offset != NULL) *current_offset = 1; cc = next_opcode(common, cc); } } return end; } #undef CASE_ITERATOR_PRIVATE_DATA_1 #undef CASE_ITERATOR_PRIVATE_DATA_2A #undef CASE_ITERATOR_PRIVATE_DATA_2B #undef CASE_ITERATOR_TYPE_PRIVATE_DATA_1 #undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2A #undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2B static SLJIT_INLINE BOOL is_powerof2(unsigned int value) { return (value & (value - 1)) == 0; } static SLJIT_INLINE void set_jumps(jump_list *list, struct sljit_label *label) { while (list) { /* sljit_set_label is clever enough to do nothing if either the jump or the label is NULL. */ SET_LABEL(list->jump, label); list = list->next; } } static SLJIT_INLINE void add_jump(struct sljit_compiler *compiler, jump_list **list, struct sljit_jump *jump) { jump_list *list_item = sljit_alloc_memory(compiler, sizeof(jump_list)); if (list_item) { list_item->next = *list; list_item->jump = jump; *list = list_item; } } static void add_stub(compiler_common *common, struct sljit_jump *start) { DEFINE_COMPILER; stub_list *list_item = sljit_alloc_memory(compiler, sizeof(stub_list)); if (list_item) { list_item->start = start; list_item->quit = LABEL(); list_item->next = common->stubs; common->stubs = list_item; } } static void flush_stubs(compiler_common *common) { DEFINE_COMPILER; stub_list *list_item = common->stubs; while (list_item) { JUMPHERE(list_item->start); add_jump(compiler, &common->stackalloc, JUMP(SLJIT_FAST_CALL)); JUMPTO(SLJIT_JUMP, list_item->quit); list_item = list_item->next; } common->stubs = NULL; } static SLJIT_INLINE void count_match(compiler_common *common) { DEFINE_COMPILER; OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO)); } static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) { /* May destroy all locals and registers except TMP2. */ DEFINE_COMPILER; SLJIT_ASSERT(size > 0); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); #ifdef DESTROY_REGISTERS OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0); #endif add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0)); } static SLJIT_INLINE void free_stack(compiler_common *common, int size) { DEFINE_COMPILER; SLJIT_ASSERT(size > 0); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); } static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size) { DEFINE_COMPILER; sljit_uw *result; if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; result = (sljit_uw *)SLJIT_MALLOC(size + sizeof(sljit_uw), compiler->allocator_data); if (SLJIT_UNLIKELY(result == NULL)) { sljit_set_compiler_memory_error(compiler); return NULL; } *(void**)result = common->read_only_data_head; common->read_only_data_head = (void *)result; return result + 1; } static SLJIT_INLINE void reset_ovector(compiler_common *common, int length) { DEFINE_COMPILER; struct sljit_label *loop; sljit_s32 i; /* At this point we can freely use all temporary registers. */ SLJIT_ASSERT(length > 1); /* TMP1 returns with begin - 1. */ OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), SLJIT_OFFSETOF(jit_arguments, begin), SLJIT_IMM, IN_UCHARS(1)); if (length < 8) { for (i = 1; i < length; i++) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), SLJIT_R0, 0); } else { if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) { GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); loop = LABEL(); sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } else { GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); loop = LABEL(); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0); OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } } } static SLJIT_INLINE void reset_early_fail(compiler_common *common) { DEFINE_COMPILER; sljit_u32 size = (sljit_u32)(common->early_fail_end_ptr - common->early_fail_start_ptr); sljit_u32 uncleared_size; sljit_s32 src = SLJIT_IMM; sljit_s32 i; struct sljit_label *loop; SLJIT_ASSERT(common->early_fail_start_ptr < common->early_fail_end_ptr); if (size == sizeof(sljit_sw)) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->early_fail_start_ptr, SLJIT_IMM, 0); return; } if (sljit_get_register_index(TMP3) >= 0 && !sljit_has_cpu_feature(SLJIT_HAS_ZERO_REGISTER)) { OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); src = TMP3; } if (size <= 6 * sizeof(sljit_sw)) { for (i = common->early_fail_start_ptr; i < common->early_fail_end_ptr; i += sizeof(sljit_sw)) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), i, src, 0); return; } GET_LOCAL_BASE(TMP1, 0, common->early_fail_start_ptr); uncleared_size = ((size / sizeof(sljit_sw)) % 3) * sizeof(sljit_sw); OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, size - uncleared_size); loop = LABEL(); OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -2 * (sljit_sw)sizeof(sljit_sw), src, 0); OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -1 * (sljit_sw)sizeof(sljit_sw), src, 0); CMPTO(SLJIT_LESS, TMP1, 0, TMP2, 0, loop); if (uncleared_size >= sizeof(sljit_sw)) OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0); if (uncleared_size >= 2 * sizeof(sljit_sw)) OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), sizeof(sljit_sw), src, 0); } static SLJIT_INLINE void do_reset_match(compiler_common *common, int length) { DEFINE_COMPILER; struct sljit_label *loop; int i; SLJIT_ASSERT(length > 1); /* OVECTOR(1) contains the "string begin - 1" constant. */ if (length > 2) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); if (length < 8) { for (i = 2; i < length; i++) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), TMP1, 0); } else { if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) { GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); loop = LABEL(); sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } else { GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw)); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); loop = LABEL(); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw)); OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } } if (!HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, stack)); else OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0); if (common->control_head_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); if (HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end)); } static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg) { while (current != NULL) { switch (current[1]) { case type_then_trap: break; case type_mark: if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0) return current[3]; break; default: SLJIT_UNREACHABLE(); break; } SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]); current = (sljit_sw*)current[0]; } return 0; } static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket) { DEFINE_COMPILER; struct sljit_label *loop; BOOL has_pre; /* At this point we can freely use all registers. */ OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(1), STR_PTR, 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, oveccount)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R2, 0); OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data), SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE)); } else { OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, match_data)); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, oveccount)); OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R0, 0); OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE)); } has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0)); OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? SLJIT_R0 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); loop = LABEL(); if (has_pre) sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); else { OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0); OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); } OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE)); OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0); /* Copy the integer value to the output buffer */ #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8); OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); /* Calculate the return value, which is the maximum ovector value. */ if (topbracket > 1) { if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) { GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); /* OVECTOR(0) is never equal to SLJIT_S2. */ loop = LABEL(); sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); } else { GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); /* OVECTOR(0) is never equal to SLJIT_S2. */ loop = LABEL(); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0); OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * (sljit_sw)sizeof(sljit_sw)); OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); } } else OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); } static SLJIT_INLINE void return_with_partial_match(compiler_common *common, struct sljit_label *quit) { DEFINE_COMPILER; sljit_s32 mov_opcode; sljit_s32 arguments_reg = !HAS_VIRTUAL_REGISTERS ? ARGUMENTS : SLJIT_R1; SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0); SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0 && (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0)); if (arguments_reg != ARGUMENTS) OP1(SLJIT_MOV, arguments_reg, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start : common->start_ptr); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL); /* Store match begin and end. */ OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, begin)); OP1(SLJIT_MOV, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, match_data)); mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV; OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0); OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector) + sizeof(PCRE2_SIZE), STR_END, 0); JUMPTO(SLJIT_JUMP, quit); } static SLJIT_INLINE void check_start_used_ptr(compiler_common *common) { /* May destroy TMP1. */ DEFINE_COMPILER; struct sljit_jump *jump; if (common->mode == PCRE2_JIT_PARTIAL_SOFT) { /* The value of -1 must be kept for start_used_ptr! */ OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, 1); /* Jumps if start_used_ptr < STR_PTR, or start_used_ptr == -1. Although overwriting is not necessary if start_used_ptr == STR_PTR, it does not hurt as well. */ jump = CMP(SLJIT_LESS_EQUAL, TMP1, 0, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); JUMPHERE(jump); } else if (common->mode == PCRE2_JIT_PARTIAL_HARD) { jump = CMP(SLJIT_LESS_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); JUMPHERE(jump); } } static SLJIT_INLINE BOOL char_has_othercase(compiler_common *common, PCRE2_SPTR cc) { /* Detects if the character has an othercase. */ unsigned int c; #ifdef SUPPORT_UNICODE if (common->utf || common->ucp) { if (common->utf) { GETCHAR(c, cc); } else c = *cc; if (c > 127) return c != UCD_OTHERCASE(c); return common->fcc[c] != c; } else #endif c = *cc; return MAX_255(c) ? common->fcc[c] != c : FALSE; } static SLJIT_INLINE unsigned int char_othercase(compiler_common *common, unsigned int c) { /* Returns with the othercase. */ #ifdef SUPPORT_UNICODE if ((common->utf || common->ucp) && c > 127) return UCD_OTHERCASE(c); #endif return TABLE_GET(c, common->fcc, c); } static unsigned int char_get_othercase_bit(compiler_common *common, PCRE2_SPTR cc) { /* Detects if the character and its othercase has only 1 bit difference. */ unsigned int c, oc, bit; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 int n; #endif #ifdef SUPPORT_UNICODE if (common->utf || common->ucp) { if (common->utf) { GETCHAR(c, cc); } else c = *cc; if (c <= 127) oc = common->fcc[c]; else oc = UCD_OTHERCASE(c); } else { c = *cc; oc = TABLE_GET(c, common->fcc, c); } #else c = *cc; oc = TABLE_GET(c, common->fcc, c); #endif SLJIT_ASSERT(c != oc); bit = c ^ oc; /* Optimized for English alphabet. */ if (c <= 127 && bit == 0x20) return (0 << 8) | 0x20; /* Since c != oc, they must have at least 1 bit difference. */ if (!is_powerof2(bit)) return 0; #if PCRE2_CODE_UNIT_WIDTH == 8 #ifdef SUPPORT_UNICODE if (common->utf && c > 127) { n = GET_EXTRALEN(*cc); while ((bit & 0x3f) == 0) { n--; bit >>= 6; } return (n << 8) | bit; } #endif /* SUPPORT_UNICODE */ return (0 << 8) | bit; #elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #ifdef SUPPORT_UNICODE if (common->utf && c > 65535) { if (bit >= (1u << 10)) bit >>= 10; else return (bit < 256) ? ((2 << 8) | bit) : ((3 << 8) | (bit >> 8)); } #endif /* SUPPORT_UNICODE */ return (bit < 256) ? ((0u << 8) | bit) : ((1u << 8) | (bit >> 8)); #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ } static void check_partial(compiler_common *common, BOOL force) { /* Checks whether a partial matching is occurred. Does not modify registers. */ DEFINE_COMPILER; struct sljit_jump *jump = NULL; SLJIT_ASSERT(!force || common->mode != PCRE2_JIT_COMPLETE); if (common->mode == PCRE2_JIT_COMPLETE) return; if (!force && !common->allow_empty_partial) jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); else if (common->mode == PCRE2_JIT_PARTIAL_SOFT) jump = CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1); if (common->mode == PCRE2_JIT_PARTIAL_SOFT) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0); else { if (common->partialmatchlabel != NULL) JUMPTO(SLJIT_JUMP, common->partialmatchlabel); else add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); } if (jump != NULL) JUMPHERE(jump); } static void check_str_end(compiler_common *common, jump_list **end_reached) { /* Does not affect registers. Usually used in a tight spot. */ DEFINE_COMPILER; struct sljit_jump *jump; if (common->mode == PCRE2_JIT_COMPLETE) { add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); return; } jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); if (common->mode == PCRE2_JIT_PARTIAL_SOFT) { add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0); add_jump(compiler, end_reached, JUMP(SLJIT_JUMP)); } else { add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0)); if (common->partialmatchlabel != NULL) JUMPTO(SLJIT_JUMP, common->partialmatchlabel); else add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); } JUMPHERE(jump); } static void detect_partial_match(compiler_common *common, jump_list **backtracks) { DEFINE_COMPILER; struct sljit_jump *jump; if (common->mode == PCRE2_JIT_COMPLETE) { add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); return; } /* Partial matching mode. */ jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); if (!common->allow_empty_partial) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0)); else if (common->mode == PCRE2_JIT_PARTIAL_SOFT) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1)); if (common->mode == PCRE2_JIT_PARTIAL_SOFT) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); } else { if (common->partialmatchlabel != NULL) JUMPTO(SLJIT_JUMP, common->partialmatchlabel); else add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP)); } JUMPHERE(jump); } static void process_partial_match(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; /* Partial matching mode. */ if (common->mode == PCRE2_JIT_PARTIAL_SOFT) { jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0); JUMPHERE(jump); } else if (common->mode == PCRE2_JIT_PARTIAL_HARD) { if (common->partialmatchlabel != NULL) CMPTO(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0, common->partialmatchlabel); else add_jump(compiler, &common->partialmatch, CMP(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0)); } } static void detect_partial_match_to(compiler_common *common, struct sljit_label *label) { DEFINE_COMPILER; CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, label); process_partial_match(common); } static void peek_char(compiler_common *common, sljit_u32 max, sljit_s32 dst, sljit_sw dstw, jump_list **backtracks) { /* Reads the character into TMP1, keeps STR_PTR. Does not check STR_END. TMP2, dst, RETURN_ADDR Destroyed. */ DEFINE_COMPILER; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_jump *jump; #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */ SLJIT_UNUSED_ARG(max); SLJIT_UNUSED_ARG(dst); SLJIT_UNUSED_ARG(dstw); SLJIT_UNUSED_ARG(backtracks); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { if (max < 128) return; jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80); OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); add_jump(compiler, common->invalid_utf ? &common->utfreadchar_invalid : &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw); if (backtracks && common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (common->utf) { if (max < 0xd800) return; OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); if (common->invalid_utf) { jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800); OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw); if (backtracks && common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); } else { /* TMP2 contains the high surrogate. */ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); } JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 32 if (common->invalid_utf) { if (max < 0xd800) return; if (backtracks != NULL) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800)); } else { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x110000); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); } } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ #endif /* SUPPORT_UNICODE */ } static void peek_char_back(compiler_common *common, sljit_u32 max, jump_list **backtracks) { /* Reads one character back without moving STR_PTR. TMP2 must contain the start of the subject buffer. Affects TMP1, TMP2, and RETURN_ADDR. */ DEFINE_COMPILER; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_jump *jump; #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */ SLJIT_UNUSED_ARG(max); SLJIT_UNUSED_ARG(backtracks); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { if (max < 128) return; jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80); if (common->invalid_utf) { add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); } else add_jump(compiler, &common->utfpeakcharback, JUMP(SLJIT_FAST_CALL)); JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (common->utf) { if (max < 0xd800) return; if (common->invalid_utf) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); } else { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xdc00); jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xdc00); /* TMP2 contains the low surrogate. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 10); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); } JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 32 if (common->invalid_utf) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800)); } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ #endif /* SUPPORT_UNICODE */ } #define READ_CHAR_UPDATE_STR_PTR 0x1 #define READ_CHAR_UTF8_NEWLINE 0x2 #define READ_CHAR_NEWLINE (READ_CHAR_UPDATE_STR_PTR | READ_CHAR_UTF8_NEWLINE) #define READ_CHAR_VALID_UTF 0x4 static void read_char(compiler_common *common, sljit_u32 min, sljit_u32 max, jump_list **backtracks, sljit_u32 options) { /* Reads the precise value of a character into TMP1, if the character is between min and max (c >= min && c <= max). Otherwise it returns with a value outside the range. Does not check STR_END. */ DEFINE_COMPILER; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_jump *jump; #endif #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 struct sljit_jump *jump2; #endif SLJIT_UNUSED_ARG(min); SLJIT_UNUSED_ARG(max); SLJIT_UNUSED_ARG(backtracks); SLJIT_UNUSED_ARG(options); SLJIT_ASSERT(min <= max); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { if (max < 128 && !(options & READ_CHAR_UPDATE_STR_PTR)) return; if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF)) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80); if (options & READ_CHAR_UTF8_NEWLINE) add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL)); else add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); JUMPHERE(jump); return; } jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0); if (min >= 0x10000) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xf0); if (options & READ_CHAR_UPDATE_STR_PTR) OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x7); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); if (!(options & READ_CHAR_UPDATE_STR_PTR)) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump2); if (options & READ_CHAR_UPDATE_STR_PTR) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0); } else if (min >= 0x800 && max <= 0xffff) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xe0); if (options & READ_CHAR_UPDATE_STR_PTR) OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0xf); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); if (!(options & READ_CHAR_UPDATE_STR_PTR)) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump2); if (options & READ_CHAR_UPDATE_STR_PTR) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0); } else if (max >= 0x800) { add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); } else if (max < 128) { OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); } else { OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (!(options & READ_CHAR_UPDATE_STR_PTR)) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); else OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); if (options & READ_CHAR_UPDATE_STR_PTR) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0); } JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (common->utf) { if (max < 0xd800 && !(options & READ_CHAR_UPDATE_STR_PTR)) return; if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF)) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800); if (options & READ_CHAR_UTF8_NEWLINE) add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL)); else add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); JUMPHERE(jump); return; } if (max >= 0x10000) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800); /* TMP2 contains the high surrogate. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump); return; } /* Skip low surrogate if necessary. */ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS) { if (options & READ_CHAR_UPDATE_STR_PTR) OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x400); if (options & READ_CHAR_UPDATE_STR_PTR) CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0); if (max >= 0xd800) CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, 0x10000); } else { jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400); if (options & READ_CHAR_UPDATE_STR_PTR) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (max >= 0xd800) OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000); JUMPHERE(jump); } } #elif PCRE2_CODE_UNIT_WIDTH == 32 if (common->invalid_utf) { if (backtracks != NULL) { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800)); } else { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x110000); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); } } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ #endif /* SUPPORT_UNICODE */ } #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 static BOOL is_char7_bitset(const sljit_u8 *bitset, BOOL nclass) { /* Tells whether the character codes below 128 are enough to determine a match. */ const sljit_u8 value = nclass ? 0xff : 0; const sljit_u8 *end = bitset + 32; bitset += 16; do { if (*bitset++ != value) return FALSE; } while (bitset < end); return TRUE; } static void read_char7_type(compiler_common *common, jump_list **backtracks, BOOL negated) { /* Reads the precise character type of a character into TMP1, if the character is less than 128. Otherwise it returns with zero. Does not check STR_END. The full_read argument tells whether characters above max are accepted or not. */ DEFINE_COMPILER; struct sljit_jump *jump; SLJIT_ASSERT(common->utf); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); /* All values > 127 are zero in ctypes. */ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); if (negated) { jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80); if (common->invalid_utf) { add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL)); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); } else { OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); } JUMPHERE(jump); } } #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */ static void read_char8_type(compiler_common *common, jump_list **backtracks, BOOL negated) { /* Reads the character type into TMP1, updates STR_PTR. Does not check STR_END. */ DEFINE_COMPILER; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 struct sljit_jump *jump; #endif #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 struct sljit_jump *jump2; #endif SLJIT_UNUSED_ARG(backtracks); SLJIT_UNUSED_ARG(negated); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { /* The result of this read may be unused, but saves an "else" part. */ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80); if (!negated) { if (common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2); if (common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe0 - 0xc2)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80); if (common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); JUMPHERE(jump2); } else if (common->invalid_utf) { add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, TMP2, 0, TMP1, 0); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); JUMPHERE(jump2); } else add_jump(compiler, &common->utfreadtype8, JUMP(SLJIT_FAST_CALL)); JUMPHERE(jump); return; } #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */ #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32 if (common->invalid_utf && negated) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x110000)); #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32 */ #if PCRE2_CODE_UNIT_WIDTH != 8 /* The ctypes array contains only 256 values. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); jump = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255); #endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 if (common->utf && negated) { /* Skip low surrogate if necessary. */ if (!common->invalid_utf) { OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800); if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS) { OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x400); CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0); } else { jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPHERE(jump); } return; } OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800); jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400)); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400)); JUMPHERE(jump); return; } #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 */ } static void move_back(compiler_common *common, jump_list **backtracks, BOOL must_be_valid) { /* Goes one character back. Affects STR_PTR and TMP1. If must_be_valid is TRUE, TMP2 is not used. Otherwise TMP2 must contain the start of the subject buffer, and it is destroyed. Does not modify STR_PTR for invalid character sequences. */ DEFINE_COMPILER; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_jump *jump; #endif #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 struct sljit_label *label; if (common->utf) { if (!must_be_valid && common->invalid_utf) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80); add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0)); JUMPHERE(jump); return; } label = LABEL(); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, label); return; } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (common->utf) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (!must_be_valid && common->invalid_utf) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000 - 0xd800); add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL)); if (backtracks != NULL) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0)); JUMPHERE(jump); return; } /* Skip low surrogate if necessary. */ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); return; } #elif PCRE2_CODE_UNIT_WIDTH == 32 if (common->invalid_utf && !must_be_valid) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); if (backtracks != NULL) { add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); return; } OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x110000); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); return; } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ #endif /* SUPPORT_UNICODE */ SLJIT_UNUSED_ARG(backtracks); SLJIT_UNUSED_ARG(must_be_valid); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } static void check_newlinechar(compiler_common *common, int nltype, jump_list **backtracks, BOOL jumpifmatch) { /* Character comes in TMP1. Checks if it is a newline. TMP2 may be destroyed. */ DEFINE_COMPILER; struct sljit_jump *jump; if (nltype == NLTYPE_ANY) { add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO)); } else if (nltype == NLTYPE_ANYCRLF) { if (jumpifmatch) { add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR)); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL)); } else { jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL)); JUMPHERE(jump); } } else { SLJIT_ASSERT(nltype == NLTYPE_FIXED && common->newline < 256); add_jump(compiler, backtracks, CMP(jumpifmatch ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); } } #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 static void do_utfreadchar(compiler_common *common) { /* Fast decoding a UTF-8 character. TMP1 contains the first byte of the character (>= 0xc0). Return char value in TMP1. */ DEFINE_COMPILER; struct sljit_jump *jump; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Searching for the first zero. */ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3000); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); jump = JUMP(SLJIT_NOT_ZERO); /* Three byte sequence. */ OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0000); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Four byte sequence. */ JUMPHERE(jump); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0000); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfreadtype8(compiler_common *common) { /* Fast decoding a UTF-8 character type. TMP2 contains the first byte of the character (>= 0xc0). Return value in TMP1. */ DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_jump *compare; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x1f); /* The upper 5 bits are known at this point. */ compare = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x3); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(compare); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* We only have types for characters less than 256. */ JUMPHERE(jump); OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfreadchar_invalid(compiler_common *common) { /* Slow decoding a UTF-8 character. TMP1 contains the first byte of the character (>= 0xc0). Return char value in TMP1. STR_PTR is undefined for invalid characters. */ DEFINE_COMPILER; sljit_s32 i; sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV); struct sljit_jump *jump; struct sljit_jump *buffer_end_close; struct sljit_label *three_byte_entry; struct sljit_label *exit_invalid_label; struct sljit_jump *exit_invalid[11]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc2); /* Usually more than 3 characters remained in the subject buffer. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); /* Not a valid start of a multi-byte sequence, no more bytes read. */ exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xf5 - 0xc2); buffer_end_close = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); /* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump); /* Three-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x40); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0x20000); exit_invalid[2] = NULL; } else exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); jump = JUMP(SLJIT_NOT_ZERO); three_byte_entry = LABEL(); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2d800); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0xd800); exit_invalid[3] = NULL; } else exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); exit_invalid[4] = NULL; } else exit_invalid[4] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump); /* Four-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x40); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0); exit_invalid[5] = NULL; } else exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc10000); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x100000); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000); exit_invalid[6] = NULL; } else exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(buffer_end_close); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); exit_invalid[7] = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0); /* Two-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); /* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); exit_invalid[8] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Three-byte sequence. */ JUMPHERE(jump); exit_invalid[9] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x40); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); exit_invalid[10] = NULL; } else exit_invalid[10] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); /* One will be substracted from STR_PTR later. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); /* Four byte sequences are not possible. */ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x30000, three_byte_entry); exit_invalid_label = LABEL(); for (i = 0; i < 11; i++) sljit_set_label(exit_invalid[i], exit_invalid_label); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfreadnewline_invalid(compiler_common *common) { /* Slow decoding a UTF-8 character, specialized for newlines. TMP1 contains the first byte of the character (>= 0xc0). Return char value in TMP1. */ DEFINE_COMPILER; struct sljit_label *loop; struct sljit_label *skip_start; struct sljit_label *three_byte_exit; struct sljit_jump *jump[5]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); if (common->nltype != NLTYPE_ANY) { SLJIT_ASSERT(common->nltype != NLTYPE_FIXED || common->newline < 128); /* All newlines are ascii, just skip intermediate octets. */ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); loop = LABEL(); if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)) == SLJIT_SUCCESS) sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); else { OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0); CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPHERE(jump[0]); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); return; } jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump[1] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xc2); jump[2] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xe2); skip_start = LABEL(); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0); jump[3] = CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80); /* Skip intermediate octets. */ loop = LABEL(); jump[4] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0); CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop); JUMPHERE(jump[3]); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); three_byte_exit = LABEL(); JUMPHERE(jump[0]); JUMPHERE(jump[4]); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Two byte long newline: 0x85. */ JUMPHERE(jump[1]); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x85, skip_start); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x85); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Three byte long newlines: 0x2028 and 0x2029. */ JUMPHERE(jump[2]); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, skip_start); CMPTO(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0, three_byte_exit); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP1, 0, TMP2, 0, SLJIT_IMM, 0x80); CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40, skip_start); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0x2000); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfmoveback_invalid(compiler_common *common) { /* Goes one character back. */ DEFINE_COMPILER; sljit_s32 i; struct sljit_jump *jump; struct sljit_jump *buffer_start_close; struct sljit_label *exit_ok_label; struct sljit_label *exit_invalid_label; struct sljit_jump *exit_invalid[7]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0); /* Two-byte sequence. */ buffer_start_close = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x20); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Three-byte sequence. */ JUMPHERE(jump); exit_invalid[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0); jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x10); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Four-byte sequence. */ JUMPHERE(jump); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80); exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0); exit_invalid[3] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x05); exit_ok_label = LABEL(); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); /* Two-byte sequence. */ JUMPHERE(buffer_start_close); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); exit_invalid[4] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20, exit_ok_label); /* Three-byte sequence. */ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); exit_invalid[5] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40); exit_invalid[6] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0); CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10, exit_ok_label); /* Four-byte sequences are not possible. */ exit_invalid_label = LABEL(); sljit_set_label(exit_invalid[5], exit_invalid_label); sljit_set_label(exit_invalid[6], exit_invalid_label); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(exit_invalid[4]); /* -2 + 4 = 2 */ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); exit_invalid_label = LABEL(); for (i = 0; i < 4; i++) sljit_set_label(exit_invalid[i], exit_invalid_label); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(4)); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfpeakcharback(compiler_common *common) { /* Peak a character back. Does not modify STR_PTR. */ DEFINE_COMPILER; struct sljit_jump *jump[2]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0); jump[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump[1]); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump[0]); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfpeakcharback_invalid(compiler_common *common) { /* Peak a character back. Does not modify STR_PTR. */ DEFINE_COMPILER; sljit_s32 i; sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV); struct sljit_jump *jump[2]; struct sljit_label *two_byte_entry; struct sljit_label *three_byte_entry; struct sljit_label *exit_invalid_label; struct sljit_jump *exit_invalid[8]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(3)); exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0); jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0); /* Two-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2); jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x1e); two_byte_entry = LABEL(); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); /* If TMP1 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump[1]); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Three-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0); jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x10); three_byte_entry = LABEL(); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, -0xd800); exit_invalid[2] = NULL; } else exit_invalid[2] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR); exit_invalid[3] = NULL; } else exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump[1]); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0 - 0x80); exit_invalid[4] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Four-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 18); /* ADD is used instead of OR because of the SUB 0x10000 above. */ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); if (has_cmov) { OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x100000); CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000); exit_invalid[5] = NULL; } else exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump[0]); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0); /* Two-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2); CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80); exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Three-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0); CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x10, three_byte_entry); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump[0]); exit_invalid[7] = CMP(SLJIT_GREATER, TMP2, 0, STR_PTR, 0); /* Two-byte sequence. */ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2); CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry); exit_invalid_label = LABEL(); for (i = 0; i < 8; i++) sljit_set_label(exit_invalid[i], exit_invalid_label); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ #if PCRE2_CODE_UNIT_WIDTH == 16 static void do_utfreadchar_invalid(compiler_common *common) { /* Slow decoding a UTF-16 character. TMP1 contains the first half of the character (>= 0xd800). Return char value in TMP1. STR_PTR is undefined for invalid characters. */ DEFINE_COMPILER; struct sljit_jump *exit_invalid[3]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); /* TMP2 contains the high surrogate. */ exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000); exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(exit_invalid[0]); JUMPHERE(exit_invalid[1]); JUMPHERE(exit_invalid[2]); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfreadnewline_invalid(compiler_common *common) { /* Slow decoding a UTF-16 character, specialized for newlines. TMP1 contains the first half of the character (>= 0xd800). Return char value in TMP1. */ DEFINE_COMPILER; struct sljit_jump *exit_invalid[2]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); /* TMP2 contains the high surrogate. */ exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x400); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(exit_invalid[0]); JUMPHERE(exit_invalid[1]); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfmoveback_invalid(compiler_common *common) { /* Goes one character back. */ DEFINE_COMPILER; struct sljit_jump *exit_invalid[3]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(exit_invalid[0]); JUMPHERE(exit_invalid[1]); JUMPHERE(exit_invalid[2]); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_utfpeakcharback_invalid(compiler_common *common) { /* Peak a character back. Does not modify STR_PTR. */ DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_jump *exit_invalid[3]; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xdc00); exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00); OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800); exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); JUMPHERE(jump); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(exit_invalid[0]); JUMPHERE(exit_invalid[1]); JUMPHERE(exit_invalid[2]); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } #endif /* PCRE2_CODE_UNIT_WIDTH == 16 */ /* UCD_BLOCK_SIZE must be 128 (see the assert below). */ #define UCD_BLOCK_MASK 127 #define UCD_BLOCK_SHIFT 7 static void do_getucd(compiler_common *common) { /* Search the UCD record for the character comes in TMP1. Returns chartype in TMP1 and UCD offset in TMP2. */ DEFINE_COMPILER; #if PCRE2_CODE_UNIT_WIDTH == 32 struct sljit_jump *jump; #endif #if defined SLJIT_DEBUG && SLJIT_DEBUG /* dummy_ucd_record */ const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR); SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther); SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0); #endif SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12); sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); #if PCRE2_CODE_UNIT_WIDTH == 32 if (!common->utf) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR); JUMPHERE(jump); } #endif OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2)); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_getucdtype(compiler_common *common) { /* Search the UCD record for the character comes in TMP1. Returns chartype in TMP1 and UCD offset in TMP2. */ DEFINE_COMPILER; #if PCRE2_CODE_UNIT_WIDTH == 32 struct sljit_jump *jump; #endif #if defined SLJIT_DEBUG && SLJIT_DEBUG /* dummy_ucd_record */ const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR); SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther); SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0); #endif SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12); sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); #if PCRE2_CODE_UNIT_WIDTH == 32 if (!common->utf) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR); JUMPHERE(jump); } #endif OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2)); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1); /* TMP2 is multiplied by 12. Same as (TMP2 << 2) + ((TMP2 << 2) << 1). */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 1); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } #endif /* SUPPORT_UNICODE */ static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *mainloop; struct sljit_label *newlinelabel = NULL; struct sljit_jump *start; struct sljit_jump *end = NULL; struct sljit_jump *end2 = NULL; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 struct sljit_label *loop; struct sljit_jump *jump; #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */ jump_list *newline = NULL; sljit_u32 overall_options = common->re->overall_options; BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0; BOOL newlinecheck = FALSE; BOOL readuchar = FALSE; if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0) && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) newlinecheck = TRUE; SLJIT_ASSERT(common->abort_label == NULL); if ((overall_options & PCRE2_FIRSTLINE) != 0) { /* Search for the end of the first line. */ SLJIT_ASSERT(common->match_end_ptr != 0); OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); if (common->nltype == NLTYPE_FIXED && common->newline > 255) { mainloop = LABEL(); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop); JUMPHERE(end); OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } else { end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); mainloop = LABEL(); /* Continual stores does not cause data dependency. */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0); read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE); check_newlinechar(common, common->nltype, &newline, TRUE); CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop); JUMPHERE(end); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0); set_jumps(newline, LABEL()); } OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); } else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0) { /* Check whether offset limit is set and valid. */ SLJIT_ASSERT(common->match_end_ptr != 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, offset_limit)); } else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, offset_limit)); OP1(SLJIT_MOV, TMP2, 0, STR_END, 0); end = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw) PCRE2_UNSET); if (HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); else OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */ if (HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); end2 = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0); OP1(SLJIT_MOV, TMP2, 0, STR_END, 0); JUMPHERE(end2); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0)); JUMPHERE(end); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0); } start = JUMP(SLJIT_JUMP); if (newlinecheck) { newlinelabel = LABEL(); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); end2 = JUMP(SLJIT_JUMP); } mainloop = LABEL(); /* Increasing the STR_PTR here requires one less jump in the most common case. */ #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf && !common->invalid_utf) readuchar = TRUE; #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */ if (newlinecheck) readuchar = TRUE; if (readuchar) OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); if (newlinecheck) CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, newlinelabel); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->invalid_utf) { /* Skip continuation code units. */ loop = LABEL(); jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80); CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x40, loop); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPHERE(jump); } else if (common->utf) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); JUMPHERE(jump); } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (common->invalid_utf) { /* Skip continuation code units. */ loop = LABEL(); jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00); CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400, loop); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPHERE(jump); } else if (common->utf) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800); if (sljit_has_cpu_feature(SLJIT_HAS_CMOV)) { OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); CMOV(SLJIT_LESS, STR_PTR, TMP2, 0); } else { OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); } } #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */ #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */ JUMPHERE(start); if (newlinecheck) { JUMPHERE(end); JUMPHERE(end2); } return mainloop; } static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last) { sljit_u32 i, count = chars->count; if (count == 255) return; if (count == 0) { chars->count = 1; chars->chars[0] = chr; if (last) chars->last_count = 1; return; } for (i = 0; i < count; i++) if (chars->chars[i] == chr) return; if (count >= MAX_DIFF_CHARS) { chars->count = 255; return; } chars->chars[count] = chr; chars->count = count + 1; if (last) chars->last_count++; } static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count) { /* Recursive function, which scans prefix literals. */ BOOL last, any, class, caseless; int len, repeat, len_save, consumed = 0; sljit_u32 chr; /* Any unicode character. */ sljit_u8 *bytes, *bytes_end, byte; PCRE2_SPTR alternative, cc_save, oc; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 PCRE2_UCHAR othercase[4]; #elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 PCRE2_UCHAR othercase[2]; #else PCRE2_UCHAR othercase[1]; #endif repeat = 1; while (TRUE) { if (*rec_count == 0) return 0; (*rec_count)--; last = TRUE; any = FALSE; class = FALSE; caseless = FALSE; switch (*cc) { case OP_CHARI: caseless = TRUE; /* Fall through */ case OP_CHAR: last = FALSE; cc++; break; case OP_SOD: case OP_SOM: case OP_SET_SOM: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_EODN: case OP_EOD: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: /* Zero width assertions. */ cc++; continue; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: cc = bracketend(cc); continue; case OP_PLUSI: case OP_MINPLUSI: case OP_POSPLUSI: caseless = TRUE; /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: cc++; break; case OP_EXACTI: caseless = TRUE; /* Fall through */ case OP_EXACT: repeat = GET2(cc, 1); last = FALSE; cc += 1 + IMM2_SIZE; break; case OP_QUERYI: case OP_MINQUERYI: case OP_POSQUERYI: caseless = TRUE; /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: len = 1; cc++; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc); #endif max_chars = scan_prefix(common, cc + len, chars, max_chars, rec_count); if (max_chars == 0) return consumed; last = FALSE; break; case OP_KET: cc += 1 + LINK_SIZE; continue; case OP_ALT: cc += GET(cc, 1); continue; case OP_ONCE: case OP_BRA: case OP_BRAPOS: case OP_CBRA: case OP_CBRAPOS: alternative = cc + GET(cc, 1); while (*alternative == OP_ALT) { max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, max_chars, rec_count); if (max_chars == 0) return consumed; alternative += GET(alternative, 1); } if (*cc == OP_CBRA || *cc == OP_CBRAPOS) cc += IMM2_SIZE; cc += 1 + LINK_SIZE; continue; case OP_CLASS: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && !is_char7_bitset((const sljit_u8 *)(cc + 1), FALSE)) return consumed; #endif class = TRUE; break; case OP_NCLASS: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif class = TRUE; break; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif any = TRUE; cc += GET(cc, 1); break; #endif case OP_DIGIT: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_digit, FALSE)) return consumed; #endif any = TRUE; cc++; break; case OP_WHITESPACE: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_space, FALSE)) return consumed; #endif any = TRUE; cc++; break; case OP_WORDCHAR: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_word, FALSE)) return consumed; #endif any = TRUE; cc++; break; case OP_NOT: case OP_NOTI: cc++; /* Fall through. */ case OP_NOT_DIGIT: case OP_NOT_WHITESPACE: case OP_NOT_WORDCHAR: case OP_ANY: case OP_ALLANY: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif any = TRUE; cc++; break; #ifdef SUPPORT_UNICODE case OP_NOTPROP: case OP_PROP: #if PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif any = TRUE; cc += 1 + 2; break; #endif case OP_TYPEEXACT: repeat = GET2(cc, 1); cc += 1 + IMM2_SIZE; continue; case OP_NOTEXACT: case OP_NOTEXACTI: #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) return consumed; #endif any = TRUE; repeat = GET2(cc, 1); cc += 1 + IMM2_SIZE + 1; break; default: return consumed; } if (any) { do { chars->count = 255; consumed++; if (--max_chars == 0) return consumed; chars++; } while (--repeat > 0); repeat = 1; continue; } if (class) { bytes = (sljit_u8*) (cc + 1); cc += 1 + 32 / sizeof(PCRE2_UCHAR); switch (*cc) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPOSSTAR: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSQUERY: max_chars = scan_prefix(common, cc + 1, chars, max_chars, rec_count); if (max_chars == 0) return consumed; break; default: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRPOSPLUS: break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: repeat = GET2(cc, 1); if (repeat <= 0) return consumed; break; } do { if (bytes[31] & 0x80) chars->count = 255; else if (chars->count != 255) { bytes_end = bytes + 32; chr = 0; do { byte = *bytes++; SLJIT_ASSERT((chr & 0x7) == 0); if (byte == 0) chr += 8; else { do { if ((byte & 0x1) != 0) add_prefix_char(chr, chars, TRUE); byte >>= 1; chr++; } while (byte != 0); chr = (chr + 7) & ~7; } } while (chars->count != 255 && bytes < bytes_end); bytes = bytes_end - 32; } consumed++; if (--max_chars == 0) return consumed; chars++; } while (--repeat > 0); switch (*cc) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPOSSTAR: return consumed; case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSQUERY: cc++; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: if (GET2(cc, 1) != GET2(cc, 1 + IMM2_SIZE)) return consumed; cc += 1 + 2 * IMM2_SIZE; break; } repeat = 1; continue; } len = 1; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc); #endif if (caseless && char_has_othercase(common, cc)) { #ifdef SUPPORT_UNICODE if (common->utf) { GETCHAR(chr, cc); if ((int)PRIV(ord2utf)(char_othercase(common, chr), othercase) != len) return consumed; } else #endif { chr = *cc; #ifdef SUPPORT_UNICODE if (common->ucp && chr > 127) othercase[0] = UCD_OTHERCASE(chr); else #endif othercase[0] = TABLE_GET(chr, common->fcc, chr); } } else { caseless = FALSE; othercase[0] = 0; /* Stops compiler warning - PH */ } len_save = len; cc_save = cc; while (TRUE) { oc = othercase; do { len--; consumed++; chr = *cc; add_prefix_char(*cc, chars, len == 0); if (caseless) add_prefix_char(*oc, chars, len == 0); if (--max_chars == 0) return consumed; chars++; cc++; oc++; } while (len > 0); if (--repeat == 0) break; len = len_save; cc = cc_save; } repeat = 1; if (last) return consumed; } } #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label) { #if PCRE2_CODE_UNIT_WIDTH == 8 OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0); CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label); #elif PCRE2_CODE_UNIT_WIDTH == 16 OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00); CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label); #else #error "Unknown code width" #endif } #endif #include "pcre2_jit_simd_inc.h" #ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD static BOOL check_fast_forward_char_pair_simd(compiler_common *common, fast_forward_char_data *chars, int max) { sljit_s32 i, j, max_i = 0, max_j = 0; sljit_u32 max_pri = 0; PCRE2_UCHAR a1, a2, a_pri, b1, b2, b_pri; for (i = max - 1; i >= 1; i--) { if (chars[i].last_count > 2) { a1 = chars[i].chars[0]; a2 = chars[i].chars[1]; a_pri = chars[i].last_count; j = i - max_fast_forward_char_pair_offset(); if (j < 0) j = 0; while (j < i) { b_pri = chars[j].last_count; if (b_pri > 2 && a_pri + b_pri >= max_pri) { b1 = chars[j].chars[0]; b2 = chars[j].chars[1]; if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2) { max_pri = a_pri + b_pri; max_i = i; max_j = j; } } j++; } } } if (max_pri == 0) return FALSE; fast_forward_char_pair_simd(common, max_i, chars[max_i].chars[0], chars[max_i].chars[1], max_j, chars[max_j].chars[0], chars[max_j].chars[1]); return TRUE; } #endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */ static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset) { DEFINE_COMPILER; struct sljit_label *start; struct sljit_jump *match; struct sljit_jump *partial_quit; PCRE2_UCHAR mask; BOOL has_match_end = (common->match_end_ptr != 0); SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0); if (has_match_end) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); if (offset > 0) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); if (has_match_end) { OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } #ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD if (JIT_HAS_FAST_FORWARD_CHAR_SIMD) { fast_forward_char_simd(common, char1, char2, offset); if (offset > 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); if (has_match_end) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); return; } #endif start = LABEL(); partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, &common->failed_match, partial_quit); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (char1 == char2) CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start); else { mask = char1 ^ char2; if (is_powerof2(mask)) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start); } else { match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start); JUMPHERE(match); } } #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf && offset > 0) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1))); jumpto_if_not_utf_char_start(compiler, TMP1, start); } #endif OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); if (common->mode != PCRE2_JIT_COMPLETE) JUMPHERE(partial_quit); if (has_match_end) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *start; struct sljit_jump *match; fast_forward_char_data chars[MAX_N_CHARS]; sljit_s32 offset; PCRE2_UCHAR mask; PCRE2_UCHAR *char_set, *char_set_end; int i, max, from; int range_right = -1, range_len; sljit_u8 *update_table = NULL; BOOL in_range; sljit_u32 rec_count; for (i = 0; i < MAX_N_CHARS; i++) { chars[i].count = 0; chars[i].last_count = 0; } rec_count = 10000; max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); if (max < 1) return FALSE; /* Convert last_count to priority. */ for (i = 0; i < max; i++) { SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count); if (chars[i].count == 1) { chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5; /* Simplifies algorithms later. */ chars[i].chars[1] = chars[i].chars[0]; } else if (chars[i].count == 2) { SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]); if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1])) chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4; else chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2; } else chars[i].last_count = (chars[i].count == 255) ? 0 : 1; } #ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && check_fast_forward_char_pair_simd(common, chars, max)) return TRUE; #endif in_range = FALSE; /* Prevent compiler "uninitialized" warning */ from = 0; range_len = 4 /* minimum length */ - 1; for (i = 0; i <= max; i++) { if (in_range && (i - from) > range_len && (chars[i - 1].count < 255)) { range_len = i - from; range_right = i - 1; } if (i < max && chars[i].count < 255) { SLJIT_ASSERT(chars[i].count > 0); if (!in_range) { in_range = TRUE; from = i; } } else in_range = FALSE; } if (range_right >= 0) { update_table = (sljit_u8 *)allocate_read_only_data(common, 256); if (update_table == NULL) return TRUE; memset(update_table, IN_UCHARS(range_len), 256); for (i = 0; i < range_len; i++) { SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255); char_set = chars[range_right - i].chars; char_set_end = char_set + chars[range_right - i].count; do { if (update_table[(*char_set) & 0xff] > IN_UCHARS(i)) update_table[(*char_set) & 0xff] = IN_UCHARS(i); char_set++; } while (char_set < char_set_end); } } offset = -1; /* Scan forward. */ for (i = 0; i < max; i++) { if (range_right == i) continue; if (offset == -1) { if (chars[i].last_count >= 2) offset = i; } else if (chars[offset].last_count < chars[i].last_count) offset = i; } SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2)); if (range_right < 0) { if (offset < 0) return FALSE; /* Works regardless the value is 1 or 2. */ fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset); return TRUE; } SLJIT_ASSERT(range_right != offset); if (common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS)); OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } else { OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS)); } SLJIT_ASSERT(range_right >= 0); if (!HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table); start = LABEL(); add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0)); #if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right)); #else OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1); #endif if (!HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0); else OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start); if (offset >= 0) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (chars[offset].count == 1) CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start); else { mask = chars[offset].chars[0] ^ chars[offset].chars[1]; if (is_powerof2(mask)) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | mask, start); } else { match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start); JUMPHERE(match); } } } #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf && offset != 0) { if (offset < 0) { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } else OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); jumpto_if_not_utf_char_start(compiler, TMP1, start); if (offset < 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } #endif if (offset >= 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); else OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); return TRUE; } static SLJIT_INLINE void fast_forward_first_char(compiler_common *common) { PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit); PCRE2_UCHAR oc; oc = first_char; if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0) { oc = TABLE_GET(first_char, common->fcc, first_char); #if defined SUPPORT_UNICODE if (first_char > 127 && (common->utf || common->ucp)) oc = UCD_OTHERCASE(first_char); #endif } fast_forward_first_char2(common, first_char, oc, 0); } static SLJIT_INLINE void fast_forward_newline(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *loop; struct sljit_jump *lastchar = NULL; struct sljit_jump *firstchar; struct sljit_jump *quit = NULL; struct sljit_jump *foundcr = NULL; struct sljit_jump *notfoundnl; jump_list *newline = NULL; if (common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); } if (common->nltype == NLTYPE_FIXED && common->newline > 255) { #ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && common->mode == PCRE2_JIT_COMPLETE) { if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); } firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_NOT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); fast_forward_char_pair_simd(common, 1, common->newline & 0xff, common->newline & 0xff, 0, (common->newline >> 8) & 0xff, (common->newline >> 8) & 0xff); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); } else #endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */ { lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); } firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2)); OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); loop = LABEL(); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, loop); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, loop); JUMPHERE(quit); JUMPHERE(lastchar); } JUMPHERE(firstchar); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); return; } if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); } else OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str)); /* Example: match /^/ to \r\n from offset 1. */ firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); if (common->nltype == NLTYPE_ANY) move_back(common, NULL, FALSE); else OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); loop = LABEL(); common->ff_newline_shortcut = loop; #ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD if (JIT_HAS_FAST_FORWARD_CHAR_SIMD && (common->nltype == NLTYPE_FIXED || common->nltype == NLTYPE_ANYCRLF)) { if (common->nltype == NLTYPE_ANYCRLF) { fast_forward_char_simd(common, CHAR_CR, CHAR_LF, 0); if (common->mode != PCRE2_JIT_COMPLETE) lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); quit = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); } else { fast_forward_char_simd(common, common->newline, common->newline, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (common->mode != PCRE2_JIT_COMPLETE) { OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0); } } } else #endif /* JIT_HAS_FAST_FORWARD_CHAR_SIMD */ { read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE); lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) foundcr = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); check_newlinechar(common, common->nltype, &newline, FALSE); set_jumps(newline, loop); } if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) { if (quit == NULL) { quit = JUMP(SLJIT_JUMP); JUMPHERE(foundcr); } notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); JUMPHERE(notfoundnl); JUMPHERE(quit); } if (lastchar) JUMPHERE(lastchar); JUMPHERE(firstchar); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks); static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common) { DEFINE_COMPILER; const sljit_u8 *start_bits = common->re->start_bitmap; struct sljit_label *start; struct sljit_jump *partial_quit; #if PCRE2_CODE_UNIT_WIDTH != 8 struct sljit_jump *found = NULL; #endif jump_list *matches = NULL; if (common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } start = LABEL(); partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, &common->failed_match, partial_quit); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches)) { #if PCRE2_CODE_UNIT_WIDTH != 8 if ((start_bits[31] & 0x80) != 0) found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255); else CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start); #elif defined SUPPORT_UNICODE if (common->utf && is_char7_bitset(start_bits, FALSE)) CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start); #endif OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits); if (!HAS_VIRTUAL_REGISTERS) { OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP3, 0); } else { OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); } JUMPTO(SLJIT_ZERO, start); } else set_jumps(matches, start); #if PCRE2_CODE_UNIT_WIDTH != 8 if (found != NULL) JUMPHERE(found); #endif OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (common->mode != PCRE2_JIT_COMPLETE) JUMPHERE(partial_quit); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0); } static SLJIT_INLINE jump_list *search_requested_char(compiler_common *common, PCRE2_UCHAR req_char, BOOL caseless, BOOL has_firstchar) { DEFINE_COMPILER; struct sljit_label *loop; struct sljit_jump *toolong; struct sljit_jump *already_found; struct sljit_jump *found; struct sljit_jump *found_oc = NULL; jump_list *not_found = NULL; sljit_u32 oc, bit; SLJIT_ASSERT(common->req_char_ptr != 0); OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(REQ_CU_MAX) * 100); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr); toolong = CMP(SLJIT_LESS, TMP2, 0, STR_END, 0); already_found = CMP(SLJIT_LESS, STR_PTR, 0, TMP1, 0); if (has_firstchar) OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); else OP1(SLJIT_MOV, TMP1, 0, STR_PTR, 0); oc = req_char; if (caseless) { oc = TABLE_GET(req_char, common->fcc, req_char); #if defined SUPPORT_UNICODE if (req_char > 127 && (common->utf || common->ucp)) oc = UCD_OTHERCASE(req_char); #endif } #ifdef JIT_HAS_FAST_REQUESTED_CHAR_SIMD if (JIT_HAS_FAST_REQUESTED_CHAR_SIMD) { not_found = fast_requested_char_simd(common, req_char, oc); } else #endif { loop = LABEL(); add_jump(compiler, ¬_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0)); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(TMP1), 0); if (req_char == oc) found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char); else { bit = req_char ^ oc; if (is_powerof2(bit)) { OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, bit); found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char | bit); } else { found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char); found_oc = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, oc); } } OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_JUMP, loop); JUMPHERE(found); if (found_oc) JUMPHERE(found_oc); } OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, TMP1, 0); JUMPHERE(already_found); JUMPHERE(toolong); return not_found; } static void do_revertframes(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *mainloop; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); GET_LOCAL_BASE(TMP1, 0, 0); /* Drop frames until we reach STACK_TOP. */ mainloop = LABEL(); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw)); jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); } else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); GET_LOCAL_BASE(TMP1, 0, 0); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0); } JUMPTO(SLJIT_JUMP, mainloop); JUMPHERE(jump); jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0); /* End of reverting values. */ OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); JUMPHERE(jump); OP1(SLJIT_NEG, TMP2, 0, TMP2, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); } else { OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0); } JUMPTO(SLJIT_JUMP, mainloop); } static void check_wordboundary(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *skipread; jump_list *skipread_list = NULL; #ifdef SUPPORT_UNICODE struct sljit_label *valid_utf; jump_list *invalid_utf1 = NULL; #endif /* SUPPORT_UNICODE */ jump_list *invalid_utf2 = NULL; #if PCRE2_CODE_UNIT_WIDTH != 8 || defined SUPPORT_UNICODE struct sljit_jump *jump; #endif /* PCRE2_CODE_UNIT_WIDTH != 8 || SUPPORT_UNICODE */ SLJIT_COMPILE_ASSERT(ctype_word == 0x10, ctype_word_must_be_16); sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); /* Get type of the previous char, and put it to TMP3. */ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); skipread = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); #ifdef SUPPORT_UNICODE if (common->invalid_utf) { peek_char_back(common, READ_CHAR_MAX, &invalid_utf1); if (common->mode != PCRE2_JIT_COMPLETE) { OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); move_back(common, NULL, TRUE); check_start_used_ptr(common); OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); OP1(SLJIT_MOV, STR_PTR, 0, TMP2, 0); } } else #endif /* SUPPORT_UNICODE */ { if (common->mode == PCRE2_JIT_COMPLETE) peek_char_back(common, READ_CHAR_MAX, NULL); else { move_back(common, NULL, TRUE); check_start_used_ptr(common); read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR); } } /* Testing char type. */ #ifdef SUPPORT_UNICODE if (common->ucp) { OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); OP1(SLJIT_MOV, TMP3, 0, TMP2, 0); } else #endif /* SUPPORT_UNICODE */ { #if PCRE2_CODE_UNIT_WIDTH != 8 jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); #elif defined SUPPORT_UNICODE /* Here TMP3 has already been zeroed. */ jump = NULL; if (common->utf) jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */); OP2(SLJIT_AND, TMP3, 0, TMP1, 0, SLJIT_IMM, 1); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #elif defined SUPPORT_UNICODE if (jump != NULL) JUMPHERE(jump); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ } JUMPHERE(skipread); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); check_str_end(common, &skipread_list); peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, &invalid_utf2); /* Testing char type. This is a code duplication. */ #ifdef SUPPORT_UNICODE valid_utf = LABEL(); if (common->ucp) { OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); } else #endif /* SUPPORT_UNICODE */ { #if PCRE2_CODE_UNIT_WIDTH != 8 /* TMP2 may be destroyed by peek_char. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); #elif defined SUPPORT_UNICODE OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); jump = NULL; if (common->utf) jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); #endif OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes); OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #elif defined SUPPORT_UNICODE if (jump != NULL) JUMPHERE(jump); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ } set_jumps(skipread_list, LABEL()); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_XOR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, TMP3, 0); OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); #ifdef SUPPORT_UNICODE if (common->invalid_utf) { set_jumps(invalid_utf1, LABEL()); peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, NULL); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR, valid_utf); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, -1); OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); set_jumps(invalid_utf2, LABEL()); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP1(SLJIT_MOV, TMP2, 0, TMP3, 0); OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); } #endif /* SUPPORT_UNICODE */ } static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) { /* May destroy TMP1. */ DEFINE_COMPILER; int ranges[MAX_CLASS_RANGE_SIZE]; sljit_u8 bit, cbit, all; int i, byte, length = 0; bit = bits[0] & 0x1; /* All bits will be zero or one (since bit is zero or one). */ all = -bit; for (i = 0; i < 256; ) { byte = i >> 3; if ((i & 0x7) == 0 && bits[byte] == all) i += 8; else { cbit = (bits[byte] >> (i & 0x7)) & 0x1; if (cbit != bit) { if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = i; length++; bit = cbit; all = -cbit; } i++; } } if (((bit == 0) && nclass) || ((bit == 1) && !nclass)) { if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = 256; length++; } if (length < 0 || length > 4) return FALSE; bit = bits[0] & 0x1; if (invert) bit ^= 0x1; /* No character is accepted. */ if (length == 0 && bit == 0) add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); switch(length) { case 0: /* When bit != 0, all characters are accepted. */ return TRUE; case 1: add_jump(compiler, backtracks, CMP(bit == 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0])); return TRUE; case 2: if (ranges[0] + 1 != ranges[1]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]); add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0])); } else add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0])); return TRUE; case 3: if (bit != 0) { add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2])); if (ranges[0] + 1 != ranges[1]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0])); } else add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0])); return TRUE; } add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[0])); if (ranges[1] + 1 != ranges[2]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1]); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1])); } else add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1])); return TRUE; case 4: if ((ranges[1] - ranges[0]) == (ranges[3] - ranges[2]) && (ranges[0] | (ranges[2] - ranges[0])) == ranges[2] && (ranges[1] & (ranges[2] - ranges[0])) == 0 && is_powerof2(ranges[2] - ranges[0])) { SLJIT_ASSERT((ranges[0] & (ranges[2] - ranges[0])) == 0 && (ranges[2] & ranges[3] & (ranges[2] - ranges[0])) != 0); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[0]); if (ranges[2] + 1 != ranges[3]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2]); add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2])); } else add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2])); return TRUE; } if (bit != 0) { i = 0; if (ranges[0] + 1 != ranges[1]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0])); i = ranges[0]; } else add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0])); if (ranges[2] + 1 != ranges[3]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - i); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2])); } else add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2] - i)); return TRUE; } OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]); add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[0])); if (ranges[1] + 1 != ranges[2]) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1])); } else add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0])); return TRUE; default: SLJIT_UNREACHABLE(); return FALSE; } } static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) { /* May destroy TMP1. */ DEFINE_COMPILER; uint16_t char_list[MAX_CLASS_CHARS_SIZE]; uint8_t byte; sljit_s32 type; int i, j, k, len, c; if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV)) return FALSE; len = 0; for (i = 0; i < 32; i++) { byte = bits[i]; if (nclass) byte = ~byte; j = 0; while (byte != 0) { if (byte & 0x1) { c = i * 8 + j; k = len; if ((c & 0x20) != 0) { for (k = 0; k < len; k++) if (char_list[k] == c - 0x20) { char_list[k] |= 0x120; break; } } if (k == len) { if (len >= MAX_CLASS_CHARS_SIZE) return FALSE; char_list[len++] = (uint16_t) c; } } byte >>= 1; j++; } } if (len == 0) return FALSE; /* Should never occur, but stops analyzers complaining. */ i = 0; j = 0; if (char_list[0] == 0) { i++; OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO); } else OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); while (i < len) { if ((char_list[i] & 0x100) != 0) j++; else { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i]); CMOV(SLJIT_ZERO, TMP2, TMP1, 0); } i++; } if (j != 0) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20); for (i = 0; i < len; i++) if ((char_list[i] & 0x100) != 0) { j--; OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff); CMOV(SLJIT_ZERO, TMP2, TMP1, 0); } } if (invert) nclass = !nclass; type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL; add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0)); return TRUE; } static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) { /* May destroy TMP1. */ if (optimize_class_ranges(common, bits, nclass, invert, backtracks)) return TRUE; return optimize_class_chars(common, bits, nclass, invert, backtracks); } static void check_anynewline(compiler_common *common) { /* Check whether TMP1 contains a newline character. TMP2 destroyed. */ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void check_hspace(compiler_common *common) { /* Check whether TMP1 contains a newline character. TMP2 destroyed. */ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void check_vspace(compiler_common *common) { /* Check whether TMP1 contains a newline character. TMP2 destroyed. */ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0); } static void do_casefulcmp(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *label; int char1_reg; int char2_reg; if (HAS_VIRTUAL_REGISTERS) { char1_reg = STR_END; char2_reg = STACK_TOP; } else { char1_reg = TMP3; char2_reg = RETURN_ADDR; } sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); if (char1_reg == STR_END) { OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0); OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0); } if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) { label = LABEL(); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); } else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); label = LABEL(); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } else { label = LABEL(); OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); } if (char1_reg == STR_END) { OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0); OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0); } OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); } static void do_caselesscmp(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *label; int char1_reg = STR_END; int char2_reg; int lcc_table; int opt_type = 0; if (HAS_VIRTUAL_REGISTERS) { char2_reg = STACK_TOP; lcc_table = STACK_LIMIT; } else { char2_reg = RETURN_ADDR; lcc_table = TMP3; } if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) opt_type = 1; else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) opt_type = 2; sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0); if (char2_reg == STACK_TOP) { OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0); OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0); } OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc); if (opt_type == 1) { label = LABEL(); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); } else if (opt_type == 2) { OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); label = LABEL(); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); } else { label = LABEL(); OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); } #if PCRE2_CODE_UNIT_WIDTH != 8 jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255); #endif OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255); #endif OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #endif if (opt_type == 0) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); if (opt_type == 2) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (char2_reg == STACK_TOP) { OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0); OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0); } OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); } static PCRE2_SPTR byte_sequence_compare(compiler_common *common, BOOL caseless, PCRE2_SPTR cc, compare_context *context, jump_list **backtracks) { DEFINE_COMPILER; unsigned int othercasebit = 0; PCRE2_SPTR othercasechar = NULL; #ifdef SUPPORT_UNICODE int utflength; #endif if (caseless && char_has_othercase(common, cc)) { othercasebit = char_get_othercase_bit(common, cc); SLJIT_ASSERT(othercasebit); /* Extracting bit difference info. */ #if PCRE2_CODE_UNIT_WIDTH == 8 othercasechar = cc + (othercasebit >> 8); othercasebit &= 0xff; #elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 /* Note that this code only handles characters in the BMP. If there ever are characters outside the BMP whose othercase differs in only one bit from itself (there currently are none), this code will need to be revised for PCRE2_CODE_UNIT_WIDTH == 32. */ othercasechar = cc + (othercasebit >> 9); if ((othercasebit & 0x100) != 0) othercasebit = (othercasebit & 0xff) << 8; else othercasebit &= 0xff; #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ } if (context->sourcereg == -1) { #if PCRE2_CODE_UNIT_WIDTH == 8 #if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED if (context->length >= 4) OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); else if (context->length >= 2) OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); else #endif OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); #elif PCRE2_CODE_UNIT_WIDTH == 16 #if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED if (context->length >= 4) OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); else #endif OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); #elif PCRE2_CODE_UNIT_WIDTH == 32 OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */ context->sourcereg = TMP2; } #ifdef SUPPORT_UNICODE utflength = 1; if (common->utf && HAS_EXTRALEN(*cc)) utflength += GET_EXTRALEN(*cc); do { #endif context->length -= IN_UCHARS(1); #if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED) && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16) /* Unaligned read is supported. */ if (othercasebit != 0 && othercasechar == cc) { context->c.asuchars[context->ucharptr] = *cc | othercasebit; context->oc.asuchars[context->ucharptr] = othercasebit; } else { context->c.asuchars[context->ucharptr] = *cc; context->oc.asuchars[context->ucharptr] = 0; } context->ucharptr++; #if PCRE2_CODE_UNIT_WIDTH == 8 if (context->ucharptr >= 4 || context->length == 0 || (context->ucharptr == 2 && context->length == 1)) #else if (context->ucharptr >= 2 || context->length == 0) #endif { if (context->length >= 4) OP1(SLJIT_MOV_S32, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); else if (context->length >= 2) OP1(SLJIT_MOV_U16, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); #if PCRE2_CODE_UNIT_WIDTH == 8 else if (context->length >= 1) OP1(SLJIT_MOV_U8, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; switch(context->ucharptr) { case 4 / sizeof(PCRE2_UCHAR): if (context->oc.asint != 0) OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asint); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asint | context->oc.asint)); break; case 2 / sizeof(PCRE2_UCHAR): if (context->oc.asushort != 0) OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asushort); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asushort | context->oc.asushort)); break; #if PCRE2_CODE_UNIT_WIDTH == 8 case 1: if (context->oc.asbyte != 0) OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asbyte); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asbyte | context->oc.asbyte)); break; #endif default: SLJIT_UNREACHABLE(); break; } context->ucharptr = 0; } #else /* Unaligned read is unsupported or in 32 bit mode. */ if (context->length >= 1) OP1(MOV_UCHAR, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; if (othercasebit != 0 && othercasechar == cc) { OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, othercasebit); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc | othercasebit)); } else add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc)); #endif cc++; #ifdef SUPPORT_UNICODE utflength--; } while (utflength > 0); #endif return cc; } #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 #define SET_TYPE_OFFSET(value) \ if ((value) != typeoffset) \ { \ if ((value) < typeoffset) \ OP2(SLJIT_ADD, typereg, 0, typereg, 0, SLJIT_IMM, typeoffset - (value)); \ else \ OP2(SLJIT_SUB, typereg, 0, typereg, 0, SLJIT_IMM, (value) - typeoffset); \ } \ typeoffset = (value); #define SET_CHAR_OFFSET(value) \ if ((value) != charoffset) \ { \ if ((value) < charoffset) \ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(charoffset - (value))); \ else \ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)((value) - charoffset)); \ } \ charoffset = (value); static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr); static void compile_xclass_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks) { DEFINE_COMPILER; jump_list *found = NULL; jump_list **list = (cc[0] & XCL_NOT) == 0 ? &found : backtracks; sljit_uw c, charoffset, max = 256, min = READ_CHAR_MAX; struct sljit_jump *jump = NULL; PCRE2_SPTR ccbegin; int compares, invertcmp, numberofcmps; #if defined SUPPORT_UNICODE && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16) BOOL utf = common->utf; #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == [8|16] */ #ifdef SUPPORT_UNICODE BOOL needstype = FALSE, needsscript = FALSE, needschar = FALSE; BOOL charsaved = FALSE; int typereg = TMP1; const sljit_u32 *other_cases; sljit_uw typeoffset; #endif /* SUPPORT_UNICODE */ /* Scanning the necessary info. */ cc++; ccbegin = cc; compares = 0; if (cc[-1] & XCL_MAP) { min = 0; cc += 32 / sizeof(PCRE2_UCHAR); } while (*cc != XCL_END) { compares++; if (*cc == XCL_SINGLE) { cc ++; GETCHARINCTEST(c, cc); if (c > max) max = c; if (c < min) min = c; #ifdef SUPPORT_UNICODE needschar = TRUE; #endif /* SUPPORT_UNICODE */ } else if (*cc == XCL_RANGE) { cc ++; GETCHARINCTEST(c, cc); if (c < min) min = c; GETCHARINCTEST(c, cc); if (c > max) max = c; #ifdef SUPPORT_UNICODE needschar = TRUE; #endif /* SUPPORT_UNICODE */ } #ifdef SUPPORT_UNICODE else { SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP); cc++; if (*cc == PT_CLIST) { other_cases = PRIV(ucd_caseless_sets) + cc[1]; while (*other_cases != NOTACHAR) { if (*other_cases > max) max = *other_cases; if (*other_cases < min) min = *other_cases; other_cases++; } } else { max = READ_CHAR_MAX; min = 0; } switch(*cc) { case PT_ANY: /* Any either accepts everything or ignored. */ if (cc[-1] == XCL_PROP) { compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE); if (list == backtracks) add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); return; } break; case PT_LAMP: case PT_GC: case PT_PC: case PT_ALNUM: needstype = TRUE; break; case PT_SC: needsscript = TRUE; break; case PT_SPACE: case PT_PXSPACE: case PT_WORD: case PT_PXGRAPH: case PT_PXPRINT: case PT_PXPUNCT: needstype = TRUE; needschar = TRUE; break; case PT_CLIST: case PT_UCNC: needschar = TRUE; break; default: SLJIT_UNREACHABLE(); break; } cc += 2; } #endif /* SUPPORT_UNICODE */ } SLJIT_ASSERT(compares > 0); /* We are not necessary in utf mode even in 8 bit mode. */ cc = ccbegin; if ((cc[-1] & XCL_NOT) != 0) read_char(common, min, max, backtracks, READ_CHAR_UPDATE_STR_PTR); else { #ifdef SUPPORT_UNICODE read_char(common, min, max, (needstype || needsscript) ? backtracks : NULL, 0); #else /* !SUPPORT_UNICODE */ read_char(common, min, max, NULL, 0); #endif /* SUPPORT_UNICODE */ } if ((cc[-1] & XCL_HASPROP) == 0) { if ((cc[-1] & XCL_MAP) != 0) { jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found)) { OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO)); } add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(jump); cc += 32 / sizeof(PCRE2_UCHAR); } else { OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, min); add_jump(compiler, (cc[-1] & XCL_NOT) == 0 ? backtracks : &found, CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, max - min)); } } else if ((cc[-1] & XCL_MAP) != 0) { OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); #ifdef SUPPORT_UNICODE charsaved = TRUE; #endif /* SUPPORT_UNICODE */ if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list)) { #if PCRE2_CODE_UNIT_WIDTH == 8 jump = NULL; if (common->utf) #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO)); #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ JUMPHERE(jump); } OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); cc += 32 / sizeof(PCRE2_UCHAR); } #ifdef SUPPORT_UNICODE if (needstype || needsscript) { if (needschar && !charsaved) OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); #if PCRE2_CODE_UNIT_WIDTH == 32 if (!common->utf) { jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR); JUMPHERE(jump); } #endif /* PCRE2_CODE_UNIT_WIDTH == 32 */ OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2)); OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1); /* Before anything else, we deal with scripts. */ if (needsscript) { OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 3); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script)); ccbegin = cc; while (*cc != XCL_END) { if (*cc == XCL_SINGLE) { cc ++; GETCHARINCTEST(c, cc); } else if (*cc == XCL_RANGE) { cc ++; GETCHARINCTEST(c, cc); GETCHARINCTEST(c, cc); } else { SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP); cc++; if (*cc == PT_SC) { compares--; invertcmp = (compares == 0 && list != backtracks); if (cc[-1] == XCL_NOTPROP) invertcmp ^= 0x1; jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]); add_jump(compiler, compares > 0 ? list : backtracks, jump); } cc += 2; } } cc = ccbegin; if (needstype) { /* TMP2 has already been shifted by 2 */ if (!needschar) { OP2(SLJIT_ADD, TMP1, 0, TMP2, 0, TMP2, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); } else { OP2(SLJIT_ADD, TMP1, 0, TMP2, 0, TMP2, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); typereg = RETURN_ADDR; } } else if (needschar) OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); } else if (needstype) { OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 3); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2); if (!needschar) { OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); } else { OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); typereg = RETURN_ADDR; } } else if (needschar) OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0); } #endif /* SUPPORT_UNICODE */ /* Generating code. */ charoffset = 0; numberofcmps = 0; #ifdef SUPPORT_UNICODE typeoffset = 0; #endif /* SUPPORT_UNICODE */ while (*cc != XCL_END) { compares--; invertcmp = (compares == 0 && list != backtracks); jump = NULL; if (*cc == XCL_SINGLE) { cc ++; GETCHARINCTEST(c, cc); if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } else { jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); numberofcmps = 0; } } else if (*cc == XCL_RANGE) { cc ++; GETCHARINCTEST(c, cc); SET_CHAR_OFFSET(c); GETCHARINCTEST(c, cc); if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } else { jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); numberofcmps = 0; } } #ifdef SUPPORT_UNICODE else { SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP); if (*cc == XCL_NOTPROP) invertcmp ^= 0x1; cc++; switch(*cc) { case PT_ANY: if (!invertcmp) jump = JUMP(SLJIT_JUMP); break; case PT_LAMP: OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_GC: c = PRIV(ucp_typerange)[(int)cc[1] * 2]; SET_TYPE_OFFSET(c); jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, PRIV(ucp_typerange)[(int)cc[1] * 2 + 1] - c); break; case PT_PC: jump = CMP(SLJIT_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, (int)cc[1] - typeoffset); break; case PT_SC: compares++; /* Do nothing. */ break; case PT_SPACE: case PT_PXSPACE: SET_CHAR_OFFSET(9); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_TYPE_OFFSET(ucp_Zl); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_WORD: OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset)); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); /* Fall through. */ case PT_ALNUM: SET_TYPE_OFFSET(ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Nd); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_CLIST: other_cases = PRIV(ucd_caseless_sets) + cc[1]; /* At least three characters are required. Otherwise this case would be handled by the normal code path. */ SLJIT_ASSERT(other_cases[0] != NOTACHAR && other_cases[1] != NOTACHAR && other_cases[2] != NOTACHAR); SLJIT_ASSERT(other_cases[0] < other_cases[1] && other_cases[1] < other_cases[2]); /* Optimizing character pairs, if their difference is power of 2. */ if (is_powerof2(other_cases[1] ^ other_cases[0])) { if (charoffset == 0) OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); else { OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); other_cases += 2; } else if (is_powerof2(other_cases[2] ^ other_cases[1])) { if (charoffset == 0) OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[2] ^ other_cases[1]); else { OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset)); OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); other_cases += 3; } else { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); } while (*other_cases != NOTACHAR) { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); } jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_UCNC: OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset)); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset)); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset)); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_CHAR_OFFSET(0xa0); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset)); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_PXGRAPH: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); break; case PT_PXPRINT: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll); OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); break; case PT_PXPUNCT: SET_TYPE_OFFSET(ucp_Sc); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f); OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Pc); OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; default: SLJIT_UNREACHABLE(); break; } cc += 2; } #endif /* SUPPORT_UNICODE */ if (jump != NULL) add_jump(compiler, compares > 0 ? list : backtracks, jump); } if (found != NULL) set_jumps(found, LABEL()); } #undef SET_TYPE_OFFSET #undef SET_CHAR_OFFSET #endif static PCRE2_SPTR compile_simple_assertion_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks) { DEFINE_COMPILER; int length; struct sljit_jump *jump[4]; #ifdef SUPPORT_UNICODE struct sljit_label *label; #endif /* SUPPORT_UNICODE */ switch(type) { case OP_SOD: if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); } else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); return cc; case OP_SOM: if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); } else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); return cc; case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL)); #ifdef SUPPORT_UNICODE if (common->invalid_utf) { add_jump(compiler, backtracks, CMP((type == OP_NOT_WORD_BOUNDARY) ? SLJIT_NOT_EQUAL : SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0)); return cc; } #endif /* SUPPORT_UNICODE */ sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; case OP_EODN: /* Requires rather complex checks. */ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); if (common->nltype == NLTYPE_FIXED && common->newline > 255) { OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0)); else { jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0); OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL)); check_partial(common, TRUE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(jump[1]); } OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); } else if (common->nltype == NLTYPE_FIXED) { OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); } else { OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); jump[2] = JUMP(SLJIT_GREATER); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */); /* Equal. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(jump[1]); if (common->nltype == NLTYPE_ANYCRLF) { OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, STR_END, 0)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL)); } else { OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); } JUMPHERE(jump[2]); JUMPHERE(jump[3]); } JUMPHERE(jump[0]); if (common->mode != PCRE2_JIT_COMPLETE) check_partial(common, TRUE); return cc; case OP_EOD: add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0)); if (common->mode != PCRE2_JIT_COMPLETE) check_partial(common, TRUE); return cc; case OP_DOLL: if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); } else OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); if (!common->endonly) compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks); else { add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0)); check_partial(common, FALSE); } return cc; case OP_DOLLM: jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); } else OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); check_partial(common, FALSE); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); if (common->nltype == NLTYPE_FIXED && common->newline > 255) { OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0)); else { jump[1] = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0); /* STR_PTR = STR_END - IN_UCHARS(1) */ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); check_partial(common, TRUE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(jump[1]); } OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); } else { peek_char(common, common->nlmax, TMP3, 0, NULL); check_newlinechar(common, common->nltype, backtracks, FALSE); } JUMPHERE(jump[0]); return cc; case OP_CIRC: if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0)); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); } else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0)); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); } return cc; case OP_CIRCM: /* TMP2 might be used by peek_char_back. */ if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0); OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); } add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); if (!common->alt_circumflex) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); if (common->nltype == NLTYPE_FIXED && common->newline > 255) { OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, TMP2, 0)); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); } else { peek_char_back(common, common->nlmax, backtracks); check_newlinechar(common, common->nltype, backtracks, FALSE); } JUMPHERE(jump[0]); return cc; case OP_REVERSE: length = GET(cc, 0); if (length == 0) return cc + LINK_SIZE; if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); } else OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin)); #ifdef SUPPORT_UNICODE if (common->utf) { OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, length); label = LABEL(); add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0)); move_back(common, backtracks, FALSE); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP3, 0, TMP3, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else #endif { OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0)); } check_start_used_ptr(common); return cc + LINK_SIZE; } SLJIT_UNREACHABLE(); return cc; } #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH != 32 static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc) { PCRE2_SPTR start_subject = args->begin; PCRE2_SPTR end_subject = args->end; int lgb, rgb, ricount; PCRE2_SPTR prevcc, endcc, bptr; BOOL first = TRUE; uint32_t c; prevcc = cc; endcc = NULL; do { GETCHARINC(c, cc); rgb = UCD_GRAPHBREAK(c); if (first) { lgb = rgb; endcc = cc; first = FALSE; continue; } if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; /* Not breaking between Regional Indicators is allowed only if there are an even number of preceding RIs. */ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) { ricount = 0; bptr = prevcc; /* bptr is pointing to the left-hand character */ while (bptr > start_subject) { bptr--; BACKCHAR(bptr); GETCHAR(c, bptr); if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; ricount++; } if ((ricount & 1) != 0) break; /* Grapheme break required */ } /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this allows any number of them before a following Extended_Pictographic. */ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || lgb != ucp_gbExtended_Pictographic) lgb = rgb; prevcc = endcc; endcc = cc; } while (cc < end_subject); return endcc; } #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ static PCRE2_SPTR SLJIT_FUNC do_extuni_utf_invalid(jit_arguments *args, PCRE2_SPTR cc) { PCRE2_SPTR start_subject = args->begin; PCRE2_SPTR end_subject = args->end; int lgb, rgb, ricount; PCRE2_SPTR prevcc, endcc, bptr; BOOL first = TRUE; uint32_t c; prevcc = cc; endcc = NULL; do { GETCHARINC_INVALID(c, cc, end_subject, break); rgb = UCD_GRAPHBREAK(c); if (first) { lgb = rgb; endcc = cc; first = FALSE; continue; } if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; /* Not breaking between Regional Indicators is allowed only if there are an even number of preceding RIs. */ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) { ricount = 0; bptr = prevcc; /* bptr is pointing to the left-hand character */ while (bptr > start_subject) { GETCHARBACK_INVALID(c, bptr, start_subject, break); if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; ricount++; } if ((ricount & 1) != 0) break; /* Grapheme break required */ } /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this allows any number of them before a following Extended_Pictographic. */ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || lgb != ucp_gbExtended_Pictographic) lgb = rgb; prevcc = endcc; endcc = cc; } while (cc < end_subject); return endcc; } static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc) { PCRE2_SPTR start_subject = args->begin; PCRE2_SPTR end_subject = args->end; int lgb, rgb, ricount; PCRE2_SPTR bptr; uint32_t c; /* Patch by PH */ /* GETCHARINC(c, cc); */ c = *cc++; #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x110000) return NULL; #endif /* PCRE2_CODE_UNIT_WIDTH == 32 */ lgb = UCD_GRAPHBREAK(c); while (cc < end_subject) { c = *cc; #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x110000) break; #endif /* PCRE2_CODE_UNIT_WIDTH == 32 */ rgb = UCD_GRAPHBREAK(c); if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; /* Not breaking between Regional Indicators is allowed only if there are an even number of preceding RIs. */ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) { ricount = 0; bptr = cc - 1; /* bptr is pointing to the left-hand character */ while (bptr > start_subject) { bptr--; c = *bptr; #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x110000) break; #endif /* PCRE2_CODE_UNIT_WIDTH == 32 */ if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; ricount++; } if ((ricount & 1) != 0) break; /* Grapheme break required */ } /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this allows any number of them before a following Extended_Pictographic. */ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || lgb != ucp_gbExtended_Pictographic) lgb = rgb; cc++; } return cc; } #endif /* SUPPORT_UNICODE */ static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr) { DEFINE_COMPILER; int length; unsigned int c, oc, bit; compare_context context; struct sljit_jump *jump[3]; jump_list *end_list; #ifdef SUPPORT_UNICODE PCRE2_UCHAR propdata[5]; #endif /* SUPPORT_UNICODE */ switch(type) { case OP_NOT_DIGIT: case OP_DIGIT: /* Digits are usually 0-9, so it is worth to optimize them. */ if (check_str_ptr) detect_partial_match(common, backtracks); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_digit, FALSE)) read_char7_type(common, backtracks, type == OP_NOT_DIGIT); else #endif read_char8_type(common, backtracks, type == OP_NOT_DIGIT); /* Flip the starting bit in the negative case. */ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; case OP_NOT_WHITESPACE: case OP_WHITESPACE: if (check_str_ptr) detect_partial_match(common, backtracks); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_space, FALSE)) read_char7_type(common, backtracks, type == OP_NOT_WHITESPACE); else #endif read_char8_type(common, backtracks, type == OP_NOT_WHITESPACE); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; case OP_NOT_WORDCHAR: case OP_WORDCHAR: if (check_str_ptr) detect_partial_match(common, backtracks); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_word, FALSE)) read_char7_type(common, backtracks, type == OP_NOT_WORDCHAR); else #endif read_char8_type(common, backtracks, type == OP_NOT_WORDCHAR); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; case OP_ANY: if (check_str_ptr) detect_partial_match(common, backtracks); read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR); if (common->nltype == NLTYPE_FIXED && common->newline > 255) { jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); end_list = NULL; if (common->mode != PCRE2_JIT_PARTIAL_HARD) add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); else check_str_end(common, &end_list); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline & 0xff)); set_jumps(end_list, LABEL()); JUMPHERE(jump[0]); } else check_newlinechar(common, common->nltype, backtracks, TRUE); return cc; case OP_ALLANY: if (check_str_ptr) detect_partial_match(common, backtracks); #ifdef SUPPORT_UNICODE if (common->utf) { if (common->invalid_utf) { read_char(common, 0, READ_CHAR_MAX, backtracks, READ_CHAR_UPDATE_STR_PTR); return cc; } #if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); #if PCRE2_CODE_UNIT_WIDTH == 8 jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); #elif PCRE2_CODE_UNIT_WIDTH == 16 jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ JUMPHERE(jump[0]); return cc; #endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */ } #endif /* SUPPORT_UNICODE */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); return cc; case OP_ANYBYTE: if (check_str_ptr) detect_partial_match(common, backtracks); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); return cc; #ifdef SUPPORT_UNICODE case OP_NOTPROP: case OP_PROP: propdata[0] = XCL_HASPROP; propdata[1] = type == OP_NOTPROP ? XCL_NOTPROP : XCL_PROP; propdata[2] = cc[0]; propdata[3] = cc[1]; propdata[4] = XCL_END; if (check_str_ptr) detect_partial_match(common, backtracks); compile_xclass_matchingpath(common, propdata, backtracks); return cc + 2; #endif case OP_ANYNL: if (check_str_ptr) detect_partial_match(common, backtracks); read_char(common, common->bsr_nlmin, common->bsr_nlmax, NULL, 0); jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); /* We don't need to handle soft partial matching case. */ end_list = NULL; if (common->mode != PCRE2_JIT_PARTIAL_HARD) add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); else check_str_end(common, &end_list); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump[2] = JUMP(SLJIT_JUMP); JUMPHERE(jump[0]); check_newlinechar(common, common->bsr_nltype, backtracks, FALSE); set_jumps(end_list, LABEL()); JUMPHERE(jump[1]); JUMPHERE(jump[2]); return cc; case OP_NOT_HSPACE: case OP_HSPACE: if (check_str_ptr) detect_partial_match(common, backtracks); if (type == OP_NOT_HSPACE) read_char(common, 0x9, 0x3000, backtracks, READ_CHAR_UPDATE_STR_PTR); else read_char(common, 0x9, 0x3000, NULL, 0); add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL)); sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; case OP_NOT_VSPACE: case OP_VSPACE: if (check_str_ptr) detect_partial_match(common, backtracks); if (type == OP_NOT_VSPACE) read_char(common, 0xa, 0x2029, backtracks, READ_CHAR_UPDATE_STR_PTR); else read_char(common, 0xa, 0x2029, NULL, 0); add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL)); sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; #ifdef SUPPORT_UNICODE case OP_EXTUNI: if (check_str_ptr) detect_partial_match(common, backtracks); SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); #if PCRE2_CODE_UNIT_WIDTH != 32 sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, common->utf ? (common->invalid_utf ? SLJIT_FUNC_OFFSET(do_extuni_utf_invalid) : SLJIT_FUNC_OFFSET(do_extuni_utf)) : SLJIT_FUNC_OFFSET(do_extuni_no_utf)); if (common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); #else sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, common->invalid_utf ? SLJIT_FUNC_OFFSET(do_extuni_utf_invalid) : SLJIT_FUNC_OFFSET(do_extuni_no_utf)); if (!common->utf || common->invalid_utf) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); #endif OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); if (common->mode == PCRE2_JIT_PARTIAL_HARD) { jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0); /* Since we successfully read a char above, partial matching must occure. */ check_partial(common, TRUE); JUMPHERE(jump[0]); } return cc; #endif case OP_CHAR: case OP_CHARI: length = 1; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc); #endif if (check_str_ptr && common->mode != PCRE2_JIT_COMPLETE) detect_partial_match(common, backtracks); if (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0) { OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); if (length > 1 || (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE)) add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0)); context.length = IN_UCHARS(length); context.sourcereg = -1; #if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED context.ucharptr = 0; #endif return byte_sequence_compare(common, type == OP_CHARI, cc, &context, backtracks); } #ifdef SUPPORT_UNICODE if (common->utf) { GETCHAR(c, cc); } else #endif c = *cc; SLJIT_ASSERT(type == OP_CHARI && char_has_othercase(common, cc)); if (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); oc = char_othercase(common, c); read_char(common, c < oc ? c : oc, c > oc ? c : oc, NULL, 0); SLJIT_ASSERT(!is_powerof2(c ^ oc)); if (sljit_has_cpu_feature(SLJIT_HAS_CMOV)) { OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc); CMOV(SLJIT_EQUAL, TMP1, SLJIT_IMM, c); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, c)); } else { jump[0] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, oc)); JUMPHERE(jump[0]); } return cc + length; case OP_NOT: case OP_NOTI: if (check_str_ptr) detect_partial_match(common, backtracks); length = 1; #ifdef SUPPORT_UNICODE if (common->utf) { #if PCRE2_CODE_UNIT_WIDTH == 8 c = *cc; if (c < 128 && !common->invalid_utf) { OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); if (type == OP_NOT || !char_has_othercase(common, cc)) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c)); else { /* Since UTF8 code page is fixed, we know that c is in [a-z] or [A-Z] range. */ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x20); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, c | 0x20)); } /* Skip the variable-length character. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); JUMPHERE(jump[0]); return cc + 1; } else #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ { GETCHARLEN(c, cc, length); } } else #endif /* SUPPORT_UNICODE */ c = *cc; if (type == OP_NOT || !char_has_othercase(common, cc)) { read_char(common, c, c, backtracks, READ_CHAR_UPDATE_STR_PTR); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c)); } else { oc = char_othercase(common, c); read_char(common, c < oc ? c : oc, c > oc ? c : oc, backtracks, READ_CHAR_UPDATE_STR_PTR); bit = c ^ oc; if (is_powerof2(bit)) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c | bit)); } else { add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c)); add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, oc)); } } return cc + length; case OP_CLASS: case OP_NCLASS: if (check_str_ptr) detect_partial_match(common, backtracks); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 bit = (common->utf && is_char7_bitset((const sljit_u8 *)cc, type == OP_NCLASS)) ? 127 : 255; if (type == OP_NCLASS) read_char(common, 0, bit, backtracks, READ_CHAR_UPDATE_STR_PTR); else read_char(common, 0, bit, NULL, 0); #else if (type == OP_NCLASS) read_char(common, 0, 255, backtracks, READ_CHAR_UPDATE_STR_PTR); else read_char(common, 0, 255, NULL, 0); #endif if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks)) return cc + 32 / sizeof(PCRE2_UCHAR); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 jump[0] = NULL; if (common->utf) { jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, bit); if (type == OP_CLASS) { add_jump(compiler, backtracks, jump[0]); jump[0] = NULL; } } #elif PCRE2_CODE_UNIT_WIDTH != 8 jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); if (type == OP_CLASS) { add_jump(compiler, backtracks, jump[0]); jump[0] = NULL; } #endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 if (jump[0] != NULL) JUMPHERE(jump[0]); #endif return cc + 32 / sizeof(PCRE2_UCHAR); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 case OP_XCLASS: if (check_str_ptr) detect_partial_match(common, backtracks); compile_xclass_matchingpath(common, cc + LINK_SIZE, backtracks); return cc + GET(cc, 0) - 1; #endif } SLJIT_UNREACHABLE(); return cc; } static SLJIT_INLINE PCRE2_SPTR compile_charn_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, jump_list **backtracks) { /* This function consumes at least one input character. */ /* To decrease the number of length checks, we try to concatenate the fixed length character sequences. */ DEFINE_COMPILER; PCRE2_SPTR ccbegin = cc; compare_context context; int size; context.length = 0; do { if (cc >= ccend) break; if (*cc == OP_CHAR) { size = 1; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[1])) size += GET_EXTRALEN(cc[1]); #endif } else if (*cc == OP_CHARI) { size = 1; #ifdef SUPPORT_UNICODE if (common->utf) { if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) size = 0; else if (HAS_EXTRALEN(cc[1])) size += GET_EXTRALEN(cc[1]); } else #endif if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) size = 0; } else size = 0; cc += 1 + size; context.length += IN_UCHARS(size); } while (size > 0 && context.length <= 128); cc = ccbegin; if (context.length > 0) { /* We have a fixed-length byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, context.length); add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0)); context.sourcereg = -1; #if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED context.ucharptr = 0; #endif do cc = byte_sequence_compare(common, *cc == OP_CHARI, cc + 1, &context, backtracks); while (context.length > 0); return cc; } /* A non-fixed length character will be checked if length == 0. */ return compile_char1_matchingpath(common, *cc, cc + 1, backtracks, TRUE); } /* Forward definitions. */ static void compile_matchingpath(compiler_common *, PCRE2_SPTR, PCRE2_SPTR, backtrack_common *); static void compile_backtrackingpath(compiler_common *, struct backtrack_common *); #define PUSH_BACKTRACK(size, ccstart, error) \ do \ { \ backtrack = sljit_alloc_memory(compiler, (size)); \ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ return error; \ memset(backtrack, 0, size); \ backtrack->prev = parent->top; \ backtrack->cc = (ccstart); \ parent->top = backtrack; \ } \ while (0) #define PUSH_BACKTRACK_NOVALUE(size, ccstart) \ do \ { \ backtrack = sljit_alloc_memory(compiler, (size)); \ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ return; \ memset(backtrack, 0, size); \ backtrack->prev = parent->top; \ backtrack->cc = (ccstart); \ parent->top = backtrack; \ } \ while (0) #define BACKTRACK_AS(type) ((type *)backtrack) static void compile_dnref_search(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks) { /* The OVECTOR offset goes to TMP2. */ DEFINE_COMPILER; int count = GET2(cc, 1 + IMM2_SIZE); PCRE2_SPTR slot = common->name_table + GET2(cc, 1) * common->name_entry_size; unsigned int offset; jump_list *found = NULL; SLJIT_ASSERT(*cc == OP_DNREF || *cc == OP_DNREFI); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); count--; while (count-- > 0) { offset = GET2(slot, 0) << 1; GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset)); add_jump(compiler, &found, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0)); slot += common->name_entry_size; } offset = GET2(slot, 0) << 1; GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset)); if (backtracks != NULL && !common->unset_backref) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0)); set_jumps(found, LABEL()); } static void compile_ref_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks, BOOL withchecks, BOOL emptyfail) { DEFINE_COMPILER; BOOL ref = (*cc == OP_REF || *cc == OP_REFI); int offset = 0; struct sljit_jump *jump = NULL; struct sljit_jump *partial; struct sljit_jump *nopartial; #if defined SUPPORT_UNICODE struct sljit_label *loop; struct sljit_label *caseless_loop; jump_list *no_match = NULL; int source_reg = COUNT_MATCH; int source_end_reg = ARGUMENTS; int char1_reg = STACK_LIMIT; #endif /* SUPPORT_UNICODE */ if (ref) { offset = GET2(cc, 1) << 1; OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); /* OVECTOR(1) contains the "string begin - 1" constant. */ if (withchecks && !common->unset_backref) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1))); } else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0); #if defined SUPPORT_UNICODE if (common->utf && *cc == OP_REFI) { SLJIT_ASSERT(common->iref_ptr != 0); if (ref) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); else OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); if (withchecks && emptyfail) add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr, source_reg, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw), source_end_reg, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2, char1_reg, 0); OP1(SLJIT_MOV, source_reg, 0, TMP1, 0); OP1(SLJIT_MOV, source_end_reg, 0, TMP2, 0); loop = LABEL(); jump = CMP(SLJIT_GREATER_EQUAL, source_reg, 0, source_end_reg, 0); partial = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); /* Read original character. It must be a valid UTF character. */ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); OP1(SLJIT_MOV, STR_PTR, 0, source_reg, 0); read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR | READ_CHAR_VALID_UTF); OP1(SLJIT_MOV, source_reg, 0, STR_PTR, 0); OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); OP1(SLJIT_MOV, char1_reg, 0, TMP1, 0); /* Read second character. */ read_char(common, 0, READ_CHAR_MAX, &no_match, READ_CHAR_UPDATE_STR_PTR); CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop); OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 2); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records)); OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, other_case)); OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, caseset)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0); CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop); add_jump(compiler, &no_match, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_caseless_sets)); caseless_loop = LABEL(); OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP2), 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(uint32_t)); OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, char1_reg, 0); JUMPTO(SLJIT_EQUAL, loop); JUMPTO(SLJIT_LESS, caseless_loop); set_jumps(no_match, LABEL()); if (common->mode == PCRE2_JIT_COMPLETE) JUMPHERE(partial); OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr); OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw)); OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); if (common->mode != PCRE2_JIT_COMPLETE) { JUMPHERE(partial); OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr); OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw)); OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2); check_partial(common, FALSE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); } JUMPHERE(jump); OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr); OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw)); OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2); return; } else #endif /* SUPPORT_UNICODE */ { if (ref) OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); else OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); if (withchecks) jump = JUMP(SLJIT_ZERO); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); partial = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, partial); add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); if (common->mode != PCRE2_JIT_COMPLETE) { nopartial = JUMP(SLJIT_JUMP); JUMPHERE(partial); /* TMP2 -= STR_END - STR_PTR */ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, STR_PTR, 0); OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, STR_END, 0); partial = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0); OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL)); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); JUMPHERE(partial); check_partial(common, FALSE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(nopartial); } } if (jump != NULL) { if (emptyfail) add_jump(compiler, backtracks, jump); else JUMPHERE(jump); } } static SLJIT_INLINE PCRE2_SPTR compile_ref_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; BOOL ref = (*cc == OP_REF || *cc == OP_REFI); backtrack_common *backtrack; PCRE2_UCHAR type; int offset = 0; struct sljit_label *label; struct sljit_jump *zerolength; struct sljit_jump *jump = NULL; PCRE2_SPTR ccbegin = cc; int min = 0, max = 0; BOOL minimize; PUSH_BACKTRACK(sizeof(ref_iterator_backtrack), cc, NULL); if (ref) offset = GET2(cc, 1) << 1; else cc += IMM2_SIZE; type = cc[1 + IMM2_SIZE]; SLJIT_COMPILE_ASSERT((OP_CRSTAR & 0x1) == 0, crstar_opcode_must_be_even); minimize = (type & 0x1) != 0; switch(type) { case OP_CRSTAR: case OP_CRMINSTAR: min = 0; max = 0; cc += 1 + IMM2_SIZE + 1; break; case OP_CRPLUS: case OP_CRMINPLUS: min = 1; max = 0; cc += 1 + IMM2_SIZE + 1; break; case OP_CRQUERY: case OP_CRMINQUERY: min = 0; max = 1; cc += 1 + IMM2_SIZE + 1; break; case OP_CRRANGE: case OP_CRMINRANGE: min = GET2(cc, 1 + IMM2_SIZE + 1); max = GET2(cc, 1 + IMM2_SIZE + 1 + IMM2_SIZE); cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; break; default: SLJIT_UNREACHABLE(); break; } if (!minimize) { if (min == 0) { allocate_stack(common, 2); if (ref) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); /* Temporary release of STR_PTR. */ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); /* Handles both invalid and empty cases. Since the minimum repeat, is zero the invalid case is basically the same as an empty case. */ if (ref) zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); else { compile_dnref_search(common, ccbegin, NULL); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } /* Restore if not zero length. */ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); } else { allocate_stack(common, 1); if (ref) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); if (ref) { add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1))); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); } else { compile_dnref_search(common, ccbegin, &backtrack->topbacktracks); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } } if (min > 1 || max > 1) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, 0); label = LABEL(); if (!ref) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1); compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, FALSE, FALSE); if (min > 1 || max > 1) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0); if (min > 1) CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, label); if (max > 1) { jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max); allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); JUMPTO(SLJIT_JUMP, label); JUMPHERE(jump); } } if (max == 0) { /* Includes min > 1 case as well. */ allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); JUMPTO(SLJIT_JUMP, label); } JUMPHERE(zerolength); BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL(); count_match(common); return cc; } allocate_stack(common, ref ? 2 : 3); if (ref) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); if (type != OP_CRMINSTAR) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); if (min == 0) { /* Handles both invalid and empty cases. Since the minimum repeat, is zero the invalid case is basically the same as an empty case. */ if (ref) zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); else { compile_dnref_search(common, ccbegin, NULL); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } /* Length is non-zero, we can match real repeats. */ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); jump = JUMP(SLJIT_JUMP); } else { if (ref) { add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1))); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); } else { compile_dnref_search(common, ccbegin, &backtrack->topbacktracks); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0); zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } } BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL(); if (max > 0) add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max)); if (!ref) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(2)); compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, TRUE, TRUE); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); if (min > 1) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(ref_iterator_backtrack)->matchingpath); } else if (max > 0) OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); if (jump != NULL) JUMPHERE(jump); JUMPHERE(zerolength); count_match(common); return cc; } static SLJIT_INLINE PCRE2_SPTR compile_recurse_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; recurse_entry *entry = common->entries; recurse_entry *prev = NULL; sljit_sw start = GET(cc, 1); PCRE2_SPTR start_cc; BOOL needs_control_head; PUSH_BACKTRACK(sizeof(recurse_backtrack), cc, NULL); /* Inlining simple patterns. */ if (get_framesize(common, common->start + start, NULL, TRUE, &needs_control_head) == no_stack) { start_cc = common->start + start; compile_matchingpath(common, next_opcode(common, start_cc), bracketend(start_cc) - (1 + LINK_SIZE), backtrack); BACKTRACK_AS(recurse_backtrack)->inlined_pattern = TRUE; return cc + 1 + LINK_SIZE; } while (entry != NULL) { if (entry->start == start) break; prev = entry; entry = entry->next; } if (entry == NULL) { entry = sljit_alloc_memory(compiler, sizeof(recurse_entry)); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; entry->next = NULL; entry->entry_label = NULL; entry->backtrack_label = NULL; entry->entry_calls = NULL; entry->backtrack_calls = NULL; entry->start = start; if (prev != NULL) prev->next = entry; else common->entries = entry; } BACKTRACK_AS(recurse_backtrack)->entry = entry; if (entry->entry_label == NULL) add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL)); else JUMPTO(SLJIT_FAST_CALL, entry->entry_label); /* Leave if the match is failed. */ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0)); BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL(); return cc + 1 + LINK_SIZE; } static sljit_s32 SLJIT_FUNC do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) { PCRE2_SPTR begin; PCRE2_SIZE *ovector; sljit_u32 oveccount, capture_top; if (arguments->callout == NULL) return 0; SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size); begin = arguments->begin; ovector = (PCRE2_SIZE*)(callout_block + 1); oveccount = callout_block->capture_top; SLJIT_ASSERT(oveccount >= 1); callout_block->version = 2; callout_block->callout_flags = 0; /* Offsets in subject. */ callout_block->subject_length = arguments->end - arguments->begin; callout_block->start_match = jit_ovector[0] - begin; callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin; callout_block->subject = begin; /* Convert and copy the JIT offset vector to the ovector array. */ callout_block->capture_top = 1; callout_block->offset_vector = ovector; ovector[0] = PCRE2_UNSET; ovector[1] = PCRE2_UNSET; ovector += 2; jit_ovector += 2; capture_top = 1; /* Convert pointers to sizes. */ while (--oveccount != 0) { capture_top++; ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin); ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin); if (ovector[0] != PCRE2_UNSET) callout_block->capture_top = capture_top; ovector += 2; jit_ovector += 2; } return (arguments->callout)(callout_block, arguments->callout_data); } #define CALLOUT_ARG_OFFSET(arg) \ SLJIT_OFFSETOF(pcre2_callout_block, arg) static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; sljit_s32 mov_opcode; unsigned int callout_length = (*cc == OP_CALLOUT) ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2 * LINK_SIZE); sljit_sw value1; sljit_sw value2; sljit_sw value3; sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * sizeof(sljit_sw); PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw); allocate_stack(common, callout_arg_size); SLJIT_ASSERT(common->capture_last_ptr != 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0; OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1); OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0); OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1); /* These pointer sized fields temporarly stores internal variables. */ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr)); mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV; OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 1)); OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 1 + LINK_SIZE)); if (*cc == OP_CALLOUT) { value1 = 0; value2 = 0; value3 = 0; } else { value1 = (sljit_sw) (cc + (1 + 4*LINK_SIZE) + 1); value2 = (callout_length - (1 + 4*LINK_SIZE + 2)); value3 = (sljit_sw) (GET(cc, 1 + 3*LINK_SIZE)); } OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string), SLJIT_IMM, value1); OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length), SLJIT_IMM, value2); OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0); SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); /* Needed to save important temporary registers. */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0); /* SLJIT_R0 = arguments */ OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0); GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START); sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(S32) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); free_stack(common, callout_arg_size); /* Check return value. */ OP2(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER)); if (common->abort_label == NULL) add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL) /* SIG_LESS */); else JUMPTO(SLJIT_NOT_EQUAL /* SIG_LESS */, common->abort_label); return cc + callout_length; } #undef CALLOUT_ARG_SIZE #undef CALLOUT_ARG_OFFSET static SLJIT_INLINE BOOL assert_needs_str_ptr_saving(PCRE2_SPTR cc) { while (TRUE) { switch (*cc) { case OP_CALLOUT_STR: cc += GET(cc, 1 + 2*LINK_SIZE); break; case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_CIRC: case OP_CIRCM: case OP_DOLL: case OP_DOLLM: case OP_CALLOUT: case OP_ALT: cc += PRIV(OP_lengths)[*cc]; break; case OP_KET: return FALSE; default: return TRUE; } } } static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPTR cc, assert_backtrack *backtrack, BOOL conditional) { DEFINE_COMPILER; int framesize; int extrasize; BOOL local_quit_available = FALSE; BOOL needs_control_head; int private_data_ptr; backtrack_common altbacktrack; PCRE2_SPTR ccbegin; PCRE2_UCHAR opcode; PCRE2_UCHAR bra = OP_BRA; jump_list *tmp = NULL; jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks; jump_list **found; /* Saving previous accept variables. */ BOOL save_local_quit_available = common->local_quit_available; BOOL save_in_positive_assertion = common->in_positive_assertion; then_trap_backtrack *save_then_trap = common->then_trap; struct sljit_label *save_quit_label = common->quit_label; struct sljit_label *save_accept_label = common->accept_label; jump_list *save_quit = common->quit; jump_list *save_positive_assertion_quit = common->positive_assertion_quit; jump_list *save_accept = common->accept; struct sljit_jump *jump; struct sljit_jump *brajump = NULL; /* Assert captures then. */ common->then_trap = NULL; if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) { SLJIT_ASSERT(!conditional); bra = *cc; cc++; } private_data_ptr = PRIVATE_DATA(cc); SLJIT_ASSERT(private_data_ptr != 0); framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head); backtrack->framesize = framesize; backtrack->private_data_ptr = private_data_ptr; opcode = *cc; SLJIT_ASSERT(opcode >= OP_ASSERT && opcode <= OP_ASSERTBACK_NOT); found = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) ? &tmp : target; ccbegin = cc; cc += GET(cc, 1); if (bra == OP_BRAMINZERO) { /* This is a braminzero backtrack path. */ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); } if (framesize < 0) { extrasize = 1; if (bra == OP_BRA && !assert_needs_str_ptr_saving(ccbegin + 1 + LINK_SIZE)) extrasize = 0; if (needs_control_head) extrasize++; if (framesize == no_frame) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0); if (extrasize > 0) allocate_stack(common, extrasize); if (needs_control_head) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); if (extrasize > 0) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); if (needs_control_head) { SLJIT_ASSERT(extrasize == 2); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); } } else { extrasize = needs_control_head ? 3 : 2; allocate_stack(common, framesize + extrasize); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); if (needs_control_head) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); } else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize); } memset(&altbacktrack, 0, sizeof(backtrack_common)); if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)) { /* Control verbs cannot escape from these asserts. */ local_quit_available = TRUE; common->local_quit_available = TRUE; common->quit_label = NULL; common->quit = NULL; } common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK); common->positive_assertion_quit = NULL; while (1) { common->accept_label = NULL; common->accept = NULL; altbacktrack.top = NULL; altbacktrack.topbacktracks = NULL; if (*ccbegin == OP_ALT && extrasize > 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); altbacktrack.cc = ccbegin; compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { if (local_quit_available) { common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } common->accept_label = LABEL(); if (common->accept != NULL) set_jumps(common->accept, common->accept_label); /* Reset stack. */ if (framesize < 0) { if (framesize == no_frame) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); else if (extrasize > 0) free_stack(common, extrasize); if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2)); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); } } if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) { /* We know that STR_PTR was stored on the top of the stack. */ if (conditional) { if (extrasize > 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1)); } else if (bra == OP_BRAZERO) { if (framesize < 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else if (framesize >= 0) { /* For OP_BRA and OP_BRAMINZERO. */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); } } add_jump(compiler, found, JUMP(SLJIT_JUMP)); compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { if (local_quit_available) { common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } set_jumps(altbacktrack.topbacktracks, LABEL()); if (*cc != OP_ALT) break; ccbegin = cc; cc += GET(cc, 1); } if (local_quit_available) { SLJIT_ASSERT(common->positive_assertion_quit == NULL); /* Makes the check less complicated below. */ common->positive_assertion_quit = common->quit; } /* None of them matched. */ if (common->positive_assertion_quit != NULL) { jump = JUMP(SLJIT_JUMP); set_jumps(common->positive_assertion_quit, LABEL()); SLJIT_ASSERT(framesize != no_stack); if (framesize < 0) OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw)); } JUMPHERE(jump); } if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(1)); if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) { /* Assert is failed. */ if ((conditional && extrasize > 0) || bra == OP_BRAZERO) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (framesize < 0) { /* The topmost item should be 0. */ if (bra == OP_BRAZERO) { if (extrasize == 2) free_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else if (extrasize > 0) free_stack(common, extrasize); } else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1)); /* The topmost item should be 0. */ if (bra == OP_BRAZERO) { free_stack(common, framesize + extrasize - 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else free_stack(common, framesize + extrasize); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } jump = JUMP(SLJIT_JUMP); if (bra != OP_BRAZERO) add_jump(compiler, target, jump); /* Assert is successful. */ set_jumps(tmp, LABEL()); if (framesize < 0) { /* We know that STR_PTR was stored on the top of the stack. */ if (extrasize > 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); /* Keep the STR_PTR on the top of the stack. */ if (bra == OP_BRAZERO) { OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); if (extrasize == 2) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } else if (bra == OP_BRAMINZERO) { OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } } else { if (bra == OP_BRA) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1)); } else { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); if (extrasize == 2) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (bra == OP_BRAMINZERO) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else { SLJIT_ASSERT(extrasize == 3); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), bra == OP_BRAZERO ? STR_PTR : SLJIT_IMM, 0); } } } if (bra == OP_BRAZERO) { backtrack->matchingpath = LABEL(); SET_LABEL(jump, backtrack->matchingpath); } else if (bra == OP_BRAMINZERO) { JUMPTO(SLJIT_JUMP, backtrack->matchingpath); JUMPHERE(brajump); if (framesize >= 0) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } set_jumps(backtrack->common.topbacktracks, LABEL()); } } else { /* AssertNot is successful. */ if (framesize < 0) { if (extrasize > 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (bra != OP_BRA) { if (extrasize == 2) free_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else if (extrasize > 0) free_stack(common, extrasize); } else { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1)); /* The topmost item should be 0. */ if (bra != OP_BRA) { free_stack(common, framesize + extrasize - 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else free_stack(common, framesize + extrasize); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } if (bra == OP_BRAZERO) backtrack->matchingpath = LABEL(); else if (bra == OP_BRAMINZERO) { JUMPTO(SLJIT_JUMP, backtrack->matchingpath); JUMPHERE(brajump); } if (bra != OP_BRA) { SLJIT_ASSERT(found == &backtrack->common.topbacktracks); set_jumps(backtrack->common.topbacktracks, LABEL()); backtrack->common.topbacktracks = NULL; } } if (local_quit_available) { common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return cc + 1 + LINK_SIZE; } static SLJIT_INLINE void match_once_common(compiler_common *common, PCRE2_UCHAR ket, int framesize, int private_data_ptr, BOOL has_alternatives, BOOL needs_control_head) { DEFINE_COMPILER; int stacksize; if (framesize < 0) { if (framesize == no_frame) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); else { stacksize = needs_control_head ? 1 : 0; if (ket != OP_KET || has_alternatives) stacksize++; if (stacksize > 0) free_stack(common, stacksize); } if (needs_control_head) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1)); /* TMP2 which is set here used by OP_KETRMAX below. */ if (ket == OP_KETRMAX) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); else if (ket == OP_KETRMIN) { /* Move the STR_PTR to the private_data_ptr. */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } } else { stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1; OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); if (needs_control_head) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); if (ket == OP_KETRMAX) { /* TMP2 which is set here used by OP_KETRMAX below. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); } } if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0); } static SLJIT_INLINE int match_capture_common(compiler_common *common, int stacksize, int offset, int private_data_ptr) { DEFINE_COMPILER; if (common->capture_last_ptr != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); stacksize++; } if (common->optimized_cbracket[offset >> 1] == 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); stacksize += 2; } return stacksize; } static PCRE2_SPTR SLJIT_FUNC do_script_run(PCRE2_SPTR ptr, PCRE2_SPTR endptr) { if (PRIV(script_run)(ptr, endptr, FALSE)) return endptr; return NULL; } #ifdef SUPPORT_UNICODE static PCRE2_SPTR SLJIT_FUNC do_script_run_utf(PCRE2_SPTR ptr, PCRE2_SPTR endptr) { if (PRIV(script_run)(ptr, endptr, TRUE)) return endptr; return NULL; } #endif /* SUPPORT_UNICODE */ static SLJIT_INLINE void match_script_run_common(compiler_common *common, int private_data_ptr, backtrack_common *parent) { DEFINE_COMPILER; SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); #ifdef SUPPORT_UNICODE sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, common->utf ? SLJIT_FUNC_OFFSET(do_script_run_utf) : SLJIT_FUNC_OFFSET(do_script_run)); #else sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_script_run)); #endif OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); add_jump(compiler, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); } /* Handling bracketed expressions is probably the most complex part. Stack layout naming characters: S - Push the current STR_PTR 0 - Push a 0 (NULL) A - Push the current STR_PTR. Needed for restoring the STR_PTR before the next alternative. Not pushed if there are no alternatives. M - Any values pushed by the current alternative. Can be empty, or anything. C - Push the previous OVECTOR(i), OVECTOR(i+1) and OVECTOR_PRIV(i) to the stack. L - Push the previous local (pointed by localptr) to the stack () - opional values stored on the stack ()* - optonal, can be stored multiple times The following list shows the regular expression templates, their PCRE byte codes and stack layout supported by pcre-sljit. (?:) OP_BRA | OP_KET A M () OP_CBRA | OP_KET C M (?:)+ OP_BRA | OP_KETRMAX 0 A M S ( A M S )* OP_SBRA | OP_KETRMAX 0 L M S ( L M S )* (?:)+? OP_BRA | OP_KETRMIN 0 A M S ( A M S )* OP_SBRA | OP_KETRMIN 0 L M S ( L M S )* ()+ OP_CBRA | OP_KETRMAX 0 C M S ( C M S )* OP_SCBRA | OP_KETRMAX 0 C M S ( C M S )* ()+? OP_CBRA | OP_KETRMIN 0 C M S ( C M S )* OP_SCBRA | OP_KETRMIN 0 C M S ( C M S )* (?:)? OP_BRAZERO | OP_BRA | OP_KET S ( A M 0 ) (?:)?? OP_BRAMINZERO | OP_BRA | OP_KET S ( A M 0 ) ()? OP_BRAZERO | OP_CBRA | OP_KET S ( C M 0 ) ()?? OP_BRAMINZERO | OP_CBRA | OP_KET S ( C M 0 ) (?:)* OP_BRAZERO | OP_BRA | OP_KETRMAX S 0 ( A M S )* OP_BRAZERO | OP_SBRA | OP_KETRMAX S 0 ( L M S )* (?:)*? OP_BRAMINZERO | OP_BRA | OP_KETRMIN S 0 ( A M S )* OP_BRAMINZERO | OP_SBRA | OP_KETRMIN S 0 ( L M S )* ()* OP_BRAZERO | OP_CBRA | OP_KETRMAX S 0 ( C M S )* OP_BRAZERO | OP_SCBRA | OP_KETRMAX S 0 ( C M S )* ()*? OP_BRAMINZERO | OP_CBRA | OP_KETRMIN S 0 ( C M S )* OP_BRAMINZERO | OP_SCBRA | OP_KETRMIN S 0 ( C M S )* Stack layout naming characters: A - Push the alternative index (starting from 0) on the stack. Not pushed if there is no alternatives. M - Any values pushed by the current alternative. Can be empty, or anything. The next list shows the possible content of a bracket: (|) OP_*BRA | OP_ALT ... M A (?()|) OP_*COND | OP_ALT M A (?>|) OP_ONCE | OP_ALT ... [stack trace] M A Or nothing, if trace is unnecessary */ static PCRE2_SPTR compile_bracket_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; PCRE2_UCHAR opcode; int private_data_ptr = 0; int offset = 0; int i, stacksize; int repeat_ptr = 0, repeat_length = 0; int repeat_type = 0, repeat_count = 0; PCRE2_SPTR ccbegin; PCRE2_SPTR matchingpath; PCRE2_SPTR slot; PCRE2_UCHAR bra = OP_BRA; PCRE2_UCHAR ket; assert_backtrack *assert; BOOL has_alternatives; BOOL needs_control_head = FALSE; struct sljit_jump *jump; struct sljit_jump *skip; struct sljit_label *rmax_label = NULL; struct sljit_jump *braminzero = NULL; PUSH_BACKTRACK(sizeof(bracket_backtrack), cc, NULL); if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) { bra = *cc; cc++; opcode = *cc; } opcode = *cc; ccbegin = cc; matchingpath = bracketend(cc) - 1 - LINK_SIZE; ket = *matchingpath; if (ket == OP_KET && PRIVATE_DATA(matchingpath) != 0) { repeat_ptr = PRIVATE_DATA(matchingpath); repeat_length = PRIVATE_DATA(matchingpath + 1); repeat_type = PRIVATE_DATA(matchingpath + 2); repeat_count = PRIVATE_DATA(matchingpath + 3); SLJIT_ASSERT(repeat_length != 0 && repeat_type != 0 && repeat_count != 0); if (repeat_type == OP_UPTO) ket = OP_KETRMAX; if (repeat_type == OP_MINUPTO) ket = OP_KETRMIN; } matchingpath = ccbegin + 1 + LINK_SIZE; SLJIT_ASSERT(ket == OP_KET || ket == OP_KETRMAX || ket == OP_KETRMIN); SLJIT_ASSERT(!((bra == OP_BRAZERO && ket == OP_KETRMIN) || (bra == OP_BRAMINZERO && ket == OP_KETRMAX))); cc += GET(cc, 1); has_alternatives = *cc == OP_ALT; if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND)) { SLJIT_COMPILE_ASSERT(OP_DNRREF == OP_RREF + 1 && OP_FALSE == OP_RREF + 2 && OP_TRUE == OP_RREF + 3, compile_time_checks_must_be_grouped_together); has_alternatives = ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL) ? FALSE : TRUE; } if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; if (opcode == OP_CBRA || opcode == OP_SCBRA) { /* Capturing brackets has a pre-allocated space. */ offset = GET2(ccbegin, 1 + LINK_SIZE); if (common->optimized_cbracket[offset] == 0) { private_data_ptr = OVECTOR_PRIV(offset); offset <<= 1; } else { offset <<= 1; private_data_ptr = OVECTOR(offset); } BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr; matchingpath += IMM2_SIZE; } else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_ONCE || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND) { /* Other brackets simply allocate the next entry. */ private_data_ptr = PRIVATE_DATA(ccbegin); SLJIT_ASSERT(private_data_ptr != 0); BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr; if (opcode == OP_ONCE) BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, NULL, FALSE, &needs_control_head); } /* Instructions before the first alternative. */ stacksize = 0; if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) stacksize++; if (bra == OP_BRAZERO) stacksize++; if (stacksize > 0) allocate_stack(common, stacksize); stacksize = 0; if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); stacksize++; } if (bra == OP_BRAZERO) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); if (bra == OP_BRAMINZERO) { /* This is a backtrack path! (Since the try-path of OP_BRAMINZERO matches to the empty string) */ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (ket != OP_KETRMIN) { free_stack(common, 1); braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); } else if (opcode == OP_ONCE || opcode >= OP_SBRA) { jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); /* Nothing stored during the first run. */ skip = JUMP(SLJIT_JUMP); JUMPHERE(jump); /* Checking zero-length iteration. */ if (opcode != OP_ONCE || BACKTRACK_AS(bracket_backtrack)->u.framesize < 0) { /* When we come from outside, private_data_ptr contains the previous STR_PTR. */ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); } else { /* Except when the whole stack frame must be saved. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2)); } JUMPHERE(skip); } else { jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); JUMPHERE(jump); } } if (repeat_type != 0) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, repeat_count); if (repeat_type == OP_EXACT) rmax_label = LABEL(); } if (ket == OP_KETRMIN) BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL(); if (ket == OP_KETRMAX) { rmax_label = LABEL(); if (has_alternatives && opcode >= OP_BRA && opcode < OP_SBRA && repeat_type == 0) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = rmax_label; } /* Handling capturing brackets and alternatives. */ if (opcode == OP_ONCE) { stacksize = 0; if (needs_control_head) { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); stacksize++; } if (BACKTRACK_AS(bracket_backtrack)->u.framesize < 0) { /* Neither capturing brackets nor recursions are found in the block. */ if (ket == OP_KETRMIN) { stacksize += 2; if (!needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); } else { if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0); if (ket == OP_KETRMAX || has_alternatives) stacksize++; } if (stacksize > 0) allocate_stack(common, stacksize); stacksize = 0; if (needs_control_head) { stacksize++; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); } if (ket == OP_KETRMIN) { if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); } else if (ket == OP_KETRMAX || has_alternatives) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); } else { if (ket != OP_KET || has_alternatives) stacksize++; stacksize += BACKTRACK_AS(bracket_backtrack)->u.framesize + 1; allocate_stack(common, stacksize); if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stacksize = needs_control_head ? 1 : 0; if (ket != OP_KET || has_alternatives) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); stacksize++; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); } else { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); } init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1); } } else if (opcode == OP_CBRA || opcode == OP_SCBRA) { /* Saving the previous values. */ if (common->optimized_cbracket[offset >> 1] != 0) { SLJIT_ASSERT(private_data_ptr == OVECTOR(offset)); allocate_stack(common, 2); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr + sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); } } else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND) { /* Saving the previous value. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); } else if (has_alternatives) { /* Pushing the starting string pointer. */ allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } /* Generating code for the first alternative. */ if (opcode == OP_COND || opcode == OP_SCOND) { if (*matchingpath == OP_CREF) { SLJIT_ASSERT(has_alternatives); add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(matchingpath, 1) << 1), SLJIT_MEM1(SLJIT_SP), OVECTOR(1))); matchingpath += 1 + IMM2_SIZE; } else if (*matchingpath == OP_DNCREF) { SLJIT_ASSERT(has_alternatives); i = GET2(matchingpath, 1 + IMM2_SIZE); slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size; OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); slot += common->name_entry_size; i--; while (i-- > 0) { OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0); slot += common->name_entry_size; } OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), JUMP(SLJIT_ZERO)); matchingpath += 1 + 2 * IMM2_SIZE; } else if ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL) { /* Never has other case. */ BACKTRACK_AS(bracket_backtrack)->u.condfailed = NULL; SLJIT_ASSERT(!has_alternatives); if (*matchingpath == OP_TRUE) { stacksize = 1; matchingpath++; } else if (*matchingpath == OP_FALSE || *matchingpath == OP_FAIL) stacksize = 0; else if (*matchingpath == OP_RREF) { stacksize = GET2(matchingpath, 1); if (common->currententry == NULL) stacksize = 0; else if (stacksize == RREF_ANY) stacksize = 1; else if (common->currententry->start == 0) stacksize = stacksize == 0; else stacksize = stacksize == (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE); if (stacksize != 0) matchingpath += 1 + IMM2_SIZE; } else { if (common->currententry == NULL || common->currententry->start == 0) stacksize = 0; else { stacksize = GET2(matchingpath, 1 + IMM2_SIZE); slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size; i = (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE); while (stacksize > 0) { if ((int)GET2(slot, 0) == i) break; slot += common->name_entry_size; stacksize--; } } if (stacksize != 0) matchingpath += 1 + 2 * IMM2_SIZE; } /* The stacksize == 0 is a common "else" case. */ if (stacksize == 0) { if (*cc == OP_ALT) { matchingpath = cc + 1 + LINK_SIZE; cc += GET(cc, 1); } else matchingpath = cc; } } else { SLJIT_ASSERT(has_alternatives && *matchingpath >= OP_ASSERT && *matchingpath <= OP_ASSERTBACK_NOT); /* Similar code as PUSH_BACKTRACK macro. */ assert = sljit_alloc_memory(compiler, sizeof(assert_backtrack)); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; memset(assert, 0, sizeof(assert_backtrack)); assert->common.cc = matchingpath; BACKTRACK_AS(bracket_backtrack)->u.assert = assert; matchingpath = compile_assert_matchingpath(common, matchingpath, assert, TRUE); } } compile_matchingpath(common, matchingpath, cc, backtrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (opcode == OP_ONCE) match_once_common(common, ket, BACKTRACK_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head); if (opcode == OP_SCRIPT_RUN) match_script_run_common(common, private_data_ptr, backtrack); stacksize = 0; if (repeat_type == OP_MINUPTO) { /* We need to preserve the counter. TMP2 will be used below. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr); stacksize++; } if (ket != OP_KET || bra != OP_BRA) stacksize++; if (offset != 0) { if (common->capture_last_ptr != 0) stacksize++; if (common->optimized_cbracket[offset >> 1] == 0) stacksize += 2; } if (has_alternatives && opcode != OP_ONCE) stacksize++; if (stacksize > 0) allocate_stack(common, stacksize); stacksize = 0; if (repeat_type == OP_MINUPTO) { /* TMP2 was set above. */ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1); stacksize++; } if (ket != OP_KET || bra != OP_BRA) { if (ket != OP_KET) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); stacksize++; } if (offset != 0) stacksize = match_capture_common(common, stacksize, offset, private_data_ptr); /* Skip and count the other alternatives. */ i = 1; while (*cc == OP_ALT) { cc += GET(cc, 1); i++; } if (has_alternatives) { if (opcode != OP_ONCE) { if (i <= 3) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); else BACKTRACK_AS(bracket_backtrack)->u.matching_put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize)); } if (ket != OP_KETRMAX) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); } /* Must be after the matchingpath label. */ if (offset != 0 && common->optimized_cbracket[offset >> 1] != 0) { SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); } if (ket == OP_KETRMAX) { if (repeat_type != 0) { if (has_alternatives) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); /* Drop STR_PTR for greedy plus quantifier. */ if (opcode != OP_ONCE) free_stack(common, 1); } else if (opcode < OP_BRA || opcode >= OP_SBRA) { if (has_alternatives) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); /* Checking zero-length iteration. */ if (opcode != OP_ONCE) { /* This case includes opcodes such as OP_SCRIPT_RUN. */ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0, rmax_label); /* Drop STR_PTR for greedy plus quantifier. */ if (bra != OP_BRAZERO) free_stack(common, 1); } else /* TMP2 must contain the starting STR_PTR. */ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, rmax_label); } else JUMPTO(SLJIT_JUMP, rmax_label); BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL(); } if (repeat_type == OP_EXACT) { count_match(common); OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); } else if (repeat_type == OP_UPTO) { /* We need to preserve the counter. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr); allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); } if (bra == OP_BRAZERO) BACKTRACK_AS(bracket_backtrack)->zero_matchingpath = LABEL(); if (bra == OP_BRAMINZERO) { /* This is a backtrack path! (From the viewpoint of OP_BRAMINZERO) */ JUMPTO(SLJIT_JUMP, ((braminzero_backtrack *)parent)->matchingpath); if (braminzero != NULL) { JUMPHERE(braminzero); /* We need to release the end pointer to perform the backtrack for the zero-length iteration. When framesize is < 0, OP_ONCE will do the release itself. */ if (opcode == OP_ONCE && BACKTRACK_AS(bracket_backtrack)->u.framesize >= 0) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } else if (ket == OP_KETRMIN && opcode != OP_ONCE) free_stack(common, 1); } /* Continue to the normal backtrack. */ } if ((ket != OP_KET && bra != OP_BRAMINZERO) || bra == OP_BRAZERO) count_match(common); cc += 1 + LINK_SIZE; if (opcode == OP_ONCE) { /* We temporarily encode the needs_control_head in the lowest bit. Note: on the target architectures of SLJIT the ((x << 1) >> 1) returns the same value for small signed numbers (including negative numbers). */ BACKTRACK_AS(bracket_backtrack)->u.framesize = (int)((unsigned)BACKTRACK_AS(bracket_backtrack)->u.framesize << 1) | (needs_control_head ? 1 : 0); } return cc + repeat_length; } static PCRE2_SPTR compile_bracketpos_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; PCRE2_UCHAR opcode; int private_data_ptr; int cbraprivptr = 0; BOOL needs_control_head; int framesize; int stacksize; int offset = 0; BOOL zero = FALSE; PCRE2_SPTR ccbegin = NULL; int stack; /* Also contains the offset of control head. */ struct sljit_label *loop = NULL; struct jump_list *emptymatch = NULL; PUSH_BACKTRACK(sizeof(bracketpos_backtrack), cc, NULL); if (*cc == OP_BRAPOSZERO) { zero = TRUE; cc++; } opcode = *cc; private_data_ptr = PRIVATE_DATA(cc); SLJIT_ASSERT(private_data_ptr != 0); BACKTRACK_AS(bracketpos_backtrack)->private_data_ptr = private_data_ptr; switch(opcode) { case OP_BRAPOS: case OP_SBRAPOS: ccbegin = cc + 1 + LINK_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: offset = GET2(cc, 1 + LINK_SIZE); /* This case cannot be optimized in the same was as normal capturing brackets. */ SLJIT_ASSERT(common->optimized_cbracket[offset] == 0); cbraprivptr = OVECTOR_PRIV(offset); offset <<= 1; ccbegin = cc + 1 + LINK_SIZE + IMM2_SIZE; break; default: SLJIT_UNREACHABLE(); break; } framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head); BACKTRACK_AS(bracketpos_backtrack)->framesize = framesize; if (framesize < 0) { if (offset != 0) { stacksize = 2; if (common->capture_last_ptr != 0) stacksize++; } else stacksize = 1; if (needs_control_head) stacksize++; if (!zero) stacksize++; BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize; allocate_stack(common, stacksize); if (framesize == no_frame) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0); stack = 0; if (offset != 0) { stack = 2; OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); if (common->capture_last_ptr != 0) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0); stack = 3; } } else { if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); stack = 1; } if (needs_control_head) stack++; if (!zero) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), SLJIT_IMM, 1); if (needs_control_head) { stack--; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0); } } else { stacksize = framesize + 1; if (!zero) stacksize++; if (needs_control_head) stacksize++; if (offset == 0) stacksize++; BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize; allocate_stack(common, stacksize); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stack = 0; if (!zero) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 1); stack = 1; } if (needs_control_head) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0); stack++; } if (offset == 0) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), STR_PTR, 0); stack++; } OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0); init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize); stack -= 1 + (offset == 0); } if (offset != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0); loop = LABEL(); while (*cc != OP_KETRPOS) { backtrack->top = NULL; backtrack->topbacktracks = NULL; cc += GET(cc, 1); compile_matchingpath(common, ccbegin, cc, backtrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; if (framesize < 0) { if (framesize == no_frame) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (offset != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); } else { if (opcode == OP_SBRAPOS) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } /* Even if the match is empty, we need to reset the control head. */ if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0)); if (!zero) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); } else { if (offset != 0) { OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); if (opcode == OP_SBRAPOS) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0); } /* Even if the match is empty, we need to reset the control head. */ if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack)); if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0)); if (!zero) { if (framesize < 0) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } } JUMPTO(SLJIT_JUMP, loop); flush_stubs(common); compile_backtrackingpath(common, backtrack->top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; set_jumps(backtrack->topbacktracks, LABEL()); if (framesize < 0) { if (offset != 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); else OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); } else { if (offset != 0) { /* Last alternative. */ if (*cc == OP_KETRPOS) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); } else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); } } if (*cc == OP_KETRPOS) break; ccbegin = cc + 1 + LINK_SIZE; } /* We don't have to restore the control head in case of a failed match. */ backtrack->topbacktracks = NULL; if (!zero) { if (framesize < 0) add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0)); else /* TMP2 is set to [private_data_ptr] above. */ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0)); } /* None of them matched. */ set_jumps(emptymatch, LABEL()); count_match(common); return cc + 1 + LINK_SIZE; } static SLJIT_INLINE PCRE2_SPTR get_iterator_parameters(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *opcode, PCRE2_UCHAR *type, sljit_u32 *max, sljit_u32 *exact, PCRE2_SPTR *end) { int class_len; *opcode = *cc; *exact = 0; if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO) { cc++; *type = OP_CHAR; } else if (*opcode >= OP_STARI && *opcode <= OP_POSUPTOI) { cc++; *type = OP_CHARI; *opcode -= OP_STARI - OP_STAR; } else if (*opcode >= OP_NOTSTAR && *opcode <= OP_NOTPOSUPTO) { cc++; *type = OP_NOT; *opcode -= OP_NOTSTAR - OP_STAR; } else if (*opcode >= OP_NOTSTARI && *opcode <= OP_NOTPOSUPTOI) { cc++; *type = OP_NOTI; *opcode -= OP_NOTSTARI - OP_STAR; } else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO) { cc++; *opcode -= OP_TYPESTAR - OP_STAR; *type = OP_END; } else { SLJIT_ASSERT(*opcode == OP_CLASS || *opcode == OP_NCLASS || *opcode == OP_XCLASS); *type = *opcode; cc++; class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(PCRE2_UCHAR))) : GET(cc, 0); *opcode = cc[class_len - 1]; if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY) { *opcode -= OP_CRSTAR - OP_STAR; *end = cc + class_len; if (*opcode == OP_PLUS || *opcode == OP_MINPLUS) { *exact = 1; *opcode -= OP_PLUS - OP_STAR; } } else if (*opcode >= OP_CRPOSSTAR && *opcode <= OP_CRPOSQUERY) { *opcode -= OP_CRPOSSTAR - OP_POSSTAR; *end = cc + class_len; if (*opcode == OP_POSPLUS) { *exact = 1; *opcode = OP_POSSTAR; } } else { SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE || *opcode == OP_CRPOSRANGE); *max = GET2(cc, (class_len + IMM2_SIZE)); *exact = GET2(cc, class_len); if (*max == 0) { if (*opcode == OP_CRPOSRANGE) *opcode = OP_POSSTAR; else *opcode -= OP_CRRANGE - OP_STAR; } else { *max -= *exact; if (*max == 0) *opcode = OP_EXACT; else if (*max == 1) { if (*opcode == OP_CRPOSRANGE) *opcode = OP_POSQUERY; else *opcode -= OP_CRRANGE - OP_QUERY; } else { if (*opcode == OP_CRPOSRANGE) *opcode = OP_POSUPTO; else *opcode -= OP_CRRANGE - OP_UPTO; } } *end = cc + class_len + 2 * IMM2_SIZE; } return cc; } switch(*opcode) { case OP_EXACT: *exact = GET2(cc, 0); cc += IMM2_SIZE; break; case OP_PLUS: case OP_MINPLUS: *exact = 1; *opcode -= OP_PLUS - OP_STAR; break; case OP_POSPLUS: *exact = 1; *opcode = OP_POSSTAR; break; case OP_UPTO: case OP_MINUPTO: case OP_POSUPTO: *max = GET2(cc, 0); cc += IMM2_SIZE; break; } if (*type == OP_END) { *type = *cc; *end = next_opcode(common, cc); cc++; return cc; } *end = cc + 1; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc); #endif return cc; } static PCRE2_SPTR compile_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; PCRE2_UCHAR opcode; PCRE2_UCHAR type; sljit_u32 max = 0, exact; sljit_s32 early_fail_ptr = PRIVATE_DATA(cc + 1); sljit_s32 early_fail_type; BOOL charpos_enabled; PCRE2_UCHAR charpos_char; unsigned int charpos_othercasebit; PCRE2_SPTR end; jump_list *no_match = NULL; jump_list *no_char1_match = NULL; struct sljit_jump *jump = NULL; struct sljit_label *label; int private_data_ptr = PRIVATE_DATA(cc); int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP); int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); int tmp_base, tmp_offset; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 BOOL use_tmp; #endif PUSH_BACKTRACK(sizeof(char_iterator_backtrack), cc, NULL); early_fail_type = (early_fail_ptr & 0x7); early_fail_ptr >>= 3; /* During recursion, these optimizations are disabled. */ if (common->early_fail_start_ptr == 0 && common->fast_forward_bc_ptr == NULL) { early_fail_ptr = 0; early_fail_type = type_skip; } SLJIT_ASSERT(common->fast_forward_bc_ptr != NULL || early_fail_ptr == 0 || (early_fail_ptr >= common->early_fail_start_ptr && early_fail_ptr <= common->early_fail_end_ptr)); if (early_fail_type == type_fail) add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr)); cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end); if (type != OP_EXTUNI) { tmp_base = TMP3; tmp_offset = 0; } else { tmp_base = SLJIT_MEM1(SLJIT_SP); tmp_offset = POSSESSIVE0; } /* Handle fixed part first. */ if (exact > 1) { SLJIT_ASSERT(early_fail_ptr == 0); if (common->mode == PCRE2_JIT_COMPLETE #ifdef SUPPORT_UNICODE && !common->utf #endif && type != OP_ANYNL && type != OP_EXTUNI) { OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(exact)); add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER, TMP1, 0, STR_END, 0)); OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else { OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } } else if (exact == 1) { compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); if (early_fail_type == type_fail_range) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0); OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0); add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw), STR_PTR, 0); } } switch(opcode) { case OP_STAR: case OP_UPTO: SLJIT_ASSERT(early_fail_ptr == 0 || opcode == OP_STAR); if (type == OP_ANYNL || type == OP_EXTUNI) { SLJIT_ASSERT(private_data_ptr == 0); SLJIT_ASSERT(early_fail_ptr == 0); allocate_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); if (opcode == OP_UPTO) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, max); label = LABEL(); compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE); if (opcode == OP_UPTO) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); jump = JUMP(SLJIT_ZERO); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0); } /* We cannot use TMP3 because of allocate_stack. */ allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); JUMPTO(SLJIT_JUMP, label); if (jump != NULL) JUMPHERE(jump); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; } #ifdef SUPPORT_UNICODE else if (type == OP_ALLANY && !common->invalid_utf) #else else if (type == OP_ALLANY) #endif { if (opcode == OP_STAR) { if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset0, STR_END, 0); OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); process_partial_match(common); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; } #ifdef SUPPORT_UNICODE else if (!common->utf) #else else #endif { if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max)); if (common->mode == PCRE2_JIT_COMPLETE) { OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0); } else { jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0); process_partial_match(common); JUMPHERE(jump); } OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; } } charpos_enabled = FALSE; charpos_char = 0; charpos_othercasebit = 0; if ((type != OP_CHAR && type != OP_CHARI) && (*end == OP_CHAR || *end == OP_CHARI)) { #ifdef SUPPORT_UNICODE charpos_enabled = !common->utf || !HAS_EXTRALEN(end[1]); #else charpos_enabled = TRUE; #endif if (charpos_enabled && *end == OP_CHARI && char_has_othercase(common, end + 1)) { charpos_othercasebit = char_get_othercase_bit(common, end + 1); if (charpos_othercasebit == 0) charpos_enabled = FALSE; } if (charpos_enabled) { charpos_char = end[1]; /* Consume the OP_CHAR opcode. */ end += 2; #if PCRE2_CODE_UNIT_WIDTH == 8 SLJIT_ASSERT((charpos_othercasebit >> 8) == 0); #elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 SLJIT_ASSERT((charpos_othercasebit >> 9) == 0); if ((charpos_othercasebit & 0x100) != 0) charpos_othercasebit = (charpos_othercasebit & 0xff) << 8; #endif if (charpos_othercasebit != 0) charpos_char |= charpos_othercasebit; BACKTRACK_AS(char_iterator_backtrack)->u.charpos.enabled = TRUE; BACKTRACK_AS(char_iterator_backtrack)->u.charpos.chr = charpos_char; BACKTRACK_AS(char_iterator_backtrack)->u.charpos.othercasebit = charpos_othercasebit; } } if (charpos_enabled) { if (opcode == OP_UPTO) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max + 1); /* Search the first instance of charpos_char. */ jump = JUMP(SLJIT_JUMP); label = LABEL(); if (opcode == OP_UPTO) { OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO)); } compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); JUMPHERE(jump); detect_partial_match(common, &backtrack->topbacktracks); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (charpos_othercasebit != 0) OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label); if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); if (opcode == OP_UPTO) { OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); } /* Search the last instance of charpos_char. */ label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, FALSE); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); detect_partial_match(common, &no_match); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); if (charpos_othercasebit != 0) OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit); if (opcode == OP_STAR) { CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); JUMPTO(SLJIT_JUMP, label); } else { jump = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); JUMPHERE(jump); OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } set_jumps(no_match, LABEL()); OP2(SLJIT_ADD, STR_PTR, 0, base, offset0, SLJIT_IMM, IN_UCHARS(1)); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); } else { if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 use_tmp = (!HAS_VIRTUAL_REGISTERS && opcode == OP_STAR); SLJIT_ASSERT(!use_tmp || tmp_base == TMP3); if (common->utf) OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0); #endif if (opcode == OP_UPTO) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max); detect_partial_match(common, &no_match); label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0); #endif if (opcode == OP_UPTO) { OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); } detect_partial_match_to(common, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_char1_match, LABEL()); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) { set_jumps(no_match, LABEL()); if (use_tmp) { OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); OP1(SLJIT_MOV, base, offset0, TMP3, 0); } else OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); } else #endif { OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); } if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); } BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; case OP_MINSTAR: if (private_data_ptr == 0) allocate_stack(common, 1); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); break; case OP_MINUPTO: SLJIT_ASSERT(early_fail_ptr == 0); if (private_data_ptr == 0) allocate_stack(common, 2); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, max + 1); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; case OP_QUERY: case OP_MINQUERY: SLJIT_ASSERT(early_fail_ptr == 0); if (private_data_ptr == 0) allocate_stack(common, 1); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); if (opcode == OP_QUERY) compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE); BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL(); break; case OP_EXACT: break; case OP_POSSTAR: #if defined SUPPORT_UNICODE if (type == OP_ALLANY && !common->invalid_utf) #else if (type == OP_ALLANY) #endif { OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); process_partial_match(common); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0); break; } #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) { OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); detect_partial_match(common, &no_match); label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, FALSE); OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); detect_partial_match_to(common, label); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset); if (early_fail_ptr != 0) { if (!HAS_VIRTUAL_REGISTERS && tmp_base == TMP3) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, TMP3, 0); else OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); } break; } #endif detect_partial_match(common, &no_match); label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); detect_partial_match_to(common, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_char1_match, LABEL()); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_match, LABEL()); if (early_fail_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); break; case OP_POSUPTO: SLJIT_ASSERT(early_fail_ptr == 0); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0); OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max); detect_partial_match(common, &no_match); label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, FALSE); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0); OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); detect_partial_match_to(common, label); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1); break; } #endif if (type == OP_ALLANY) { OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max)); if (common->mode == PCRE2_JIT_COMPLETE) { OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0); } else { jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0); process_partial_match(common); JUMPHERE(jump); } break; } OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max); detect_partial_match(common, &no_match); label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); detect_partial_match_to(common, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_char1_match, LABEL()); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_match, LABEL()); break; case OP_POSQUERY: SLJIT_ASSERT(early_fail_ptr == 0); OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); compile_char1_matchingpath(common, type, cc, &no_match, TRUE); OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset); break; default: SLJIT_UNREACHABLE(); break; } count_match(common); return end; } static SLJIT_INLINE PCRE2_SPTR compile_fail_accept_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); if (*cc == OP_FAIL) { add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP)); return cc + 1; } if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0) add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty) { /* No need to check notempty conditions. */ if (common->accept_label == NULL) add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); else JUMPTO(SLJIT_JUMP, common->accept_label); return cc + 1; } if (common->accept_label == NULL) add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0))); else CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label); if (HAS_VIRTUAL_REGISTERS) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); } else OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options)); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO)); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); if (common->accept_label == NULL) add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO)); else JUMPTO(SLJIT_ZERO, common->accept_label); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str)); if (common->accept_label == NULL) add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0)); else CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, common->accept_label); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP)); return cc + 1; } static SLJIT_INLINE PCRE2_SPTR compile_close_matchingpath(compiler_common *common, PCRE2_SPTR cc) { DEFINE_COMPILER; int offset = GET2(cc, 1); BOOL optimized_cbracket = common->optimized_cbracket[offset] != 0; /* Data will be discarded anyway... */ if (common->currententry != NULL) return cc + 1 + IMM2_SIZE; if (!optimized_cbracket) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR_PRIV(offset)); offset <<= 1; OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); if (!optimized_cbracket) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); return cc + 1 + IMM2_SIZE; } static SLJIT_INLINE PCRE2_SPTR compile_control_verb_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; PCRE2_UCHAR opcode = *cc; PCRE2_SPTR ccend = cc + 1; if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) ccend += 2 + cc[1]; PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); if (opcode == OP_SKIP) { allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); return ccend; } if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) { if (HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0); } return ccend; } static PCRE2_UCHAR then_trap_opcode[1] = { OP_THEN_TRAP }; static SLJIT_INLINE void compile_then_trap_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; BOOL needs_control_head; int size; PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc); common->then_trap = BACKTRACK_AS(then_trap_backtrack); BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode; BACKTRACK_AS(then_trap_backtrack)->start = (sljit_sw)(cc - common->start); BACKTRACK_AS(then_trap_backtrack)->framesize = get_framesize(common, cc, ccend, FALSE, &needs_control_head); size = BACKTRACK_AS(then_trap_backtrack)->framesize; size = 3 + (size < 0 ? 0 : size); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); allocate_stack(common, size); if (size > 3) OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); else OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 2), SLJIT_IMM, type_then_trap); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0); size = BACKTRACK_AS(then_trap_backtrack)->framesize; if (size >= 0) init_frame(common, cc, ccend, size - 1, 0); } static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent) { DEFINE_COMPILER; backtrack_common *backtrack; BOOL has_then_trap = FALSE; then_trap_backtrack *save_then_trap = NULL; SLJIT_ASSERT(*ccend == OP_END || (*ccend >= OP_ALT && *ccend <= OP_KETRPOS)); if (common->has_then && common->then_offsets[cc - common->start] != 0) { SLJIT_ASSERT(*ccend != OP_END && common->control_head_ptr != 0); has_then_trap = TRUE; save_then_trap = common->then_trap; /* Tail item on backtrack. */ compile_then_trap_matchingpath(common, cc, ccend, parent); } while (cc < ccend) { switch(*cc) { case OP_SOD: case OP_SOM: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: case OP_EODN: case OP_EOD: case OP_DOLL: case OP_DOLLM: case OP_CIRC: case OP_CIRCM: case OP_REVERSE: cc = compile_simple_assertion_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); break; case OP_NOT_DIGIT: case OP_DIGIT: case OP_NOT_WHITESPACE: case OP_WHITESPACE: case OP_NOT_WORDCHAR: case OP_WORDCHAR: case OP_ANY: case OP_ALLANY: case OP_ANYBYTE: case OP_NOTPROP: case OP_PROP: case OP_ANYNL: case OP_NOT_HSPACE: case OP_HSPACE: case OP_NOT_VSPACE: case OP_VSPACE: case OP_EXTUNI: case OP_NOT: case OP_NOTI: cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE); break; case OP_SET_SOM: PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); cc++; break; case OP_CHAR: case OP_CHARI: if (common->mode == PCRE2_JIT_COMPLETE) cc = compile_charn_matchingpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); else cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE); break; case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_QUERY: case OP_MINQUERY: case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_QUERYI: case OP_MINQUERYI: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: case OP_TYPEPOSUPTO: cc = compile_iterator_matchingpath(common, cc, parent); break; case OP_CLASS: case OP_NCLASS: if (cc[1 + (32 / sizeof(PCRE2_UCHAR))] >= OP_CRSTAR && cc[1 + (32 / sizeof(PCRE2_UCHAR))] <= OP_CRPOSRANGE) cc = compile_iterator_matchingpath(common, cc, parent); else cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE); break; #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 case OP_XCLASS: if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRPOSRANGE) cc = compile_iterator_matchingpath(common, cc, parent); else cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE); break; #endif case OP_REF: case OP_REFI: if (cc[1 + IMM2_SIZE] >= OP_CRSTAR && cc[1 + IMM2_SIZE] <= OP_CRPOSRANGE) cc = compile_ref_iterator_matchingpath(common, cc, parent); else { compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE); cc += 1 + IMM2_SIZE; } break; case OP_DNREF: case OP_DNREFI: if (cc[1 + 2 * IMM2_SIZE] >= OP_CRSTAR && cc[1 + 2 * IMM2_SIZE] <= OP_CRPOSRANGE) cc = compile_ref_iterator_matchingpath(common, cc, parent); else { compile_dnref_search(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks); compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE); cc += 1 + 2 * IMM2_SIZE; } break; case OP_RECURSE: cc = compile_recurse_matchingpath(common, cc, parent); break; case OP_CALLOUT: case OP_CALLOUT_STR: cc = compile_callout_matchingpath(common, cc, parent); break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc); cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE); break; case OP_BRAMINZERO: PUSH_BACKTRACK_NOVALUE(sizeof(braminzero_backtrack), cc); cc = bracketend(cc + 1); if (*(cc - 1 - LINK_SIZE) != OP_KETRMIN) { allocate_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } else { allocate_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0); } BACKTRACK_AS(braminzero_backtrack)->matchingpath = LABEL(); count_match(common); break; case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRA: case OP_CBRA: case OP_COND: case OP_SBRA: case OP_SCBRA: case OP_SCOND: cc = compile_bracket_matchingpath(common, cc, parent); break; case OP_BRAZERO: if (cc[1] > OP_ASSERTBACK_NOT) cc = compile_bracket_matchingpath(common, cc, parent); else { PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc); cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE); } break; case OP_BRAPOS: case OP_CBRAPOS: case OP_SBRAPOS: case OP_SCBRAPOS: case OP_BRAPOSZERO: cc = compile_bracketpos_matchingpath(common, cc, parent); break; case OP_MARK: PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc); SLJIT_ASSERT(common->mark_ptr != 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); allocate_stack(common, common->has_skip_arg ? 5 : 1); if (HAS_VIRTUAL_REGISTERS) OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0), TMP2, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0); if (common->has_skip_arg) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, type_mark); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), SLJIT_IMM, (sljit_sw)(cc + 2)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(3), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); } cc += 1 + 2 + cc[1]; break; case OP_PRUNE: case OP_PRUNE_ARG: case OP_SKIP: case OP_SKIP_ARG: case OP_THEN: case OP_THEN_ARG: case OP_COMMIT: case OP_COMMIT_ARG: cc = compile_control_verb_matchingpath(common, cc, parent); break; case OP_FAIL: case OP_ACCEPT: case OP_ASSERT_ACCEPT: cc = compile_fail_accept_matchingpath(common, cc, parent); break; case OP_CLOSE: cc = compile_close_matchingpath(common, cc); break; case OP_SKIPZERO: cc = bracketend(cc + 1); break; default: SLJIT_UNREACHABLE(); return; } if (cc == NULL) return; } if (has_then_trap) { /* Head item on backtrack. */ PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc); BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode; BACKTRACK_AS(then_trap_backtrack)->then_trap = common->then_trap; common->then_trap = save_then_trap; } SLJIT_ASSERT(cc == ccend); } #undef PUSH_BACKTRACK #undef PUSH_BACKTRACK_NOVALUE #undef BACKTRACK_AS #define COMPILE_BACKTRACKINGPATH(current) \ do \ { \ compile_backtrackingpath(common, (current)); \ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ return; \ } \ while (0) #define CURRENT_AS(type) ((type *)current) static void compile_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; PCRE2_SPTR cc = current->cc; PCRE2_UCHAR opcode; PCRE2_UCHAR type; sljit_u32 max = 0, exact; struct sljit_label *label = NULL; struct sljit_jump *jump = NULL; jump_list *jumplist = NULL; PCRE2_SPTR end; int private_data_ptr = PRIVATE_DATA(cc); int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP); int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr; int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw); cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end); switch(opcode) { case OP_STAR: case OP_UPTO: if (type == OP_ANYNL || type == OP_EXTUNI) { SLJIT_ASSERT(private_data_ptr == 0); set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath); } else { if (CURRENT_AS(char_iterator_backtrack)->u.charpos.enabled) { OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); OP1(SLJIT_MOV, TMP2, 0, base, offset1); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); label = LABEL(); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); if (CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit != 0) OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit); CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.chr, CURRENT_AS(char_iterator_backtrack)->matchingpath); move_back(common, NULL, TRUE); CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP2, 0, label); } else { OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, base, offset1); move_back(common, NULL, TRUE); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath); } JUMPHERE(jump); if (private_data_ptr == 0) free_stack(common, 2); } break; case OP_MINSTAR: OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); compile_char1_matchingpath(common, type, cc, &jumplist, TRUE); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath); set_jumps(jumplist, LABEL()); if (private_data_ptr == 0) free_stack(common, 1); break; case OP_MINUPTO: OP1(SLJIT_MOV, TMP1, 0, base, offset1); OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, base, offset1, TMP1, 0); compile_char1_matchingpath(common, type, cc, &jumplist, TRUE); OP1(SLJIT_MOV, base, offset0, STR_PTR, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath); set_jumps(jumplist, LABEL()); if (private_data_ptr == 0) free_stack(common, 2); break; case OP_QUERY: OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath); jump = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath); JUMPHERE(jump); if (private_data_ptr == 0) free_stack(common, 1); break; case OP_MINQUERY: OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0); jump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); compile_char1_matchingpath(common, type, cc, &jumplist, TRUE); JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath); set_jumps(jumplist, LABEL()); JUMPHERE(jump); if (private_data_ptr == 0) free_stack(common, 1); break; case OP_EXACT: case OP_POSSTAR: case OP_POSQUERY: case OP_POSUPTO: break; default: SLJIT_UNREACHABLE(); break; } set_jumps(current->topbacktracks, LABEL()); } static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; PCRE2_SPTR cc = current->cc; BOOL ref = (*cc == OP_REF || *cc == OP_REFI); PCRE2_UCHAR type; type = cc[ref ? 1 + IMM2_SIZE : 1 + 2 * IMM2_SIZE]; if ((type & 0x1) == 0) { /* Maximize case. */ set_jumps(current->topbacktracks, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath); return; } OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath); set_jumps(current->topbacktracks, LABEL()); free_stack(common, ref ? 2 : 3); } static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; recurse_entry *entry; if (!CURRENT_AS(recurse_backtrack)->inlined_pattern) { entry = CURRENT_AS(recurse_backtrack)->entry; if (entry->backtrack_label == NULL) add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL)); else JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath); } else compile_backtrackingpath(common, current->top); set_jumps(current->topbacktracks, LABEL()); } static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; PCRE2_SPTR cc = current->cc; PCRE2_UCHAR bra = OP_BRA; struct sljit_jump *brajump = NULL; SLJIT_ASSERT(*cc != OP_BRAMINZERO); if (*cc == OP_BRAZERO) { bra = *cc; cc++; } if (bra == OP_BRAZERO) { SLJIT_ASSERT(current->topbacktracks == NULL); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); } if (CURRENT_AS(assert_backtrack)->framesize < 0) { set_jumps(current->topbacktracks, LABEL()); if (bra == OP_BRAZERO) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath); free_stack(common, 1); } return; } if (bra == OP_BRAZERO) { if (*cc == OP_ASSERT_NOT || *cc == OP_ASSERTBACK_NOT) { OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath); free_stack(common, 1); return; } free_stack(common, 1); brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); } if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0); set_jumps(current->topbacktracks, LABEL()); } else set_jumps(current->topbacktracks, LABEL()); if (bra == OP_BRAZERO) { /* We know there is enough place on the stack. */ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath); JUMPHERE(brajump); } } static void compile_bracket_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; int opcode, stacksize, alt_count, alt_max; int offset = 0; int private_data_ptr = CURRENT_AS(bracket_backtrack)->private_data_ptr; int repeat_ptr = 0, repeat_type = 0, repeat_count = 0; PCRE2_SPTR cc = current->cc; PCRE2_SPTR ccbegin; PCRE2_SPTR ccprev; PCRE2_UCHAR bra = OP_BRA; PCRE2_UCHAR ket; assert_backtrack *assert; BOOL has_alternatives; BOOL needs_control_head = FALSE; struct sljit_jump *brazero = NULL; struct sljit_jump *next_alt = NULL; struct sljit_jump *once = NULL; struct sljit_jump *cond = NULL; struct sljit_label *rmin_label = NULL; struct sljit_label *exact_label = NULL; struct sljit_put_label *put_label = NULL; if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) { bra = *cc; cc++; } opcode = *cc; ccbegin = bracketend(cc) - 1 - LINK_SIZE; ket = *ccbegin; if (ket == OP_KET && PRIVATE_DATA(ccbegin) != 0) { repeat_ptr = PRIVATE_DATA(ccbegin); repeat_type = PRIVATE_DATA(ccbegin + 2); repeat_count = PRIVATE_DATA(ccbegin + 3); SLJIT_ASSERT(repeat_type != 0 && repeat_count != 0); if (repeat_type == OP_UPTO) ket = OP_KETRMAX; if (repeat_type == OP_MINUPTO) ket = OP_KETRMIN; } ccbegin = cc; cc += GET(cc, 1); has_alternatives = *cc == OP_ALT; if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) has_alternatives = (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) || CURRENT_AS(bracket_backtrack)->u.condfailed != NULL; if (opcode == OP_CBRA || opcode == OP_SCBRA) offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1; if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; alt_max = has_alternatives ? no_alternatives(ccbegin) : 0; /* Decoding the needs_control_head in framesize. */ if (opcode == OP_ONCE) { needs_control_head = (CURRENT_AS(bracket_backtrack)->u.framesize & 0x1) != 0; CURRENT_AS(bracket_backtrack)->u.framesize >>= 1; } if (ket != OP_KET && repeat_type != 0) { /* TMP1 is used in OP_KETRMIN below. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); if (repeat_type == OP_UPTO) OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0, SLJIT_IMM, 1); else OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0); } if (ket == OP_KETRMAX) { if (bra == OP_BRAZERO) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); brazero = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0); } } else if (ket == OP_KETRMIN) { if (bra != OP_BRAMINZERO) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (repeat_type != 0) { /* TMP1 was set a few lines above. */ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); /* Drop STR_PTR for non-greedy plus quantifier. */ if (opcode != OP_ONCE) free_stack(common, 1); } else if (opcode >= OP_SBRA || opcode == OP_ONCE) { /* Checking zero-length iteration. */ if (opcode != OP_ONCE || CURRENT_AS(bracket_backtrack)->u.framesize < 0) CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); } /* Drop STR_PTR for non-greedy plus quantifier. */ if (opcode != OP_ONCE) free_stack(common, 1); } else JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); } rmin_label = LABEL(); if (repeat_type != 0) OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); } else if (bra == OP_BRAZERO) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); brazero = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); } else if (repeat_type == OP_EXACT) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); exact_label = LABEL(); } if (offset != 0) { if (common->capture_last_ptr != 0) { SLJIT_ASSERT(common->optimized_cbracket[offset >> 1] == 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2)); free_stack(common, 3); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); } else if (common->optimized_cbracket[offset >> 1] == 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); free_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0); } } if (SLJIT_UNLIKELY(opcode == OP_ONCE)) { if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } once = JUMP(SLJIT_JUMP); } else if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) { if (has_alternatives) { /* Always exactly one alternative. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); alt_max = 2; next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); } } else if (has_alternatives) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); if (alt_max > 3) { sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0); SLJIT_ASSERT(CURRENT_AS(bracket_backtrack)->u.matching_put_label); sljit_set_put_label(CURRENT_AS(bracket_backtrack)->u.matching_put_label, LABEL()); sljit_emit_op0(compiler, SLJIT_ENDBR); } else next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); } COMPILE_BACKTRACKINGPATH(current->top); if (current->topbacktracks) set_jumps(current->topbacktracks, LABEL()); if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) { /* Conditional block always has at most one alternative. */ if (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) { SLJIT_ASSERT(has_alternatives); assert = CURRENT_AS(bracket_backtrack)->u.assert; if (assert->framesize >= 0 && (ccbegin[1 + LINK_SIZE] == OP_ASSERT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK)) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } cond = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL()); } else if (CURRENT_AS(bracket_backtrack)->u.condfailed != NULL) { SLJIT_ASSERT(has_alternatives); cond = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(bracket_backtrack)->u.condfailed, LABEL()); } else SLJIT_ASSERT(!has_alternatives); } if (has_alternatives) { alt_count = 1; do { current->top = NULL; current->topbacktracks = NULL; current->nextbacktracks = NULL; /* Conditional blocks always have an additional alternative, even if it is empty. */ if (*cc == OP_ALT) { ccprev = cc + 1 + LINK_SIZE; cc += GET(cc, 1); if (opcode != OP_COND && opcode != OP_SCOND) { if (opcode != OP_ONCE) { if (private_data_ptr != 0) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); else OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); } else OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(needs_control_head ? 1 : 0)); } compile_matchingpath(common, ccprev, cc, current); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return; if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (opcode == OP_SCRIPT_RUN) match_script_run_common(common, private_data_ptr, current); } /* Instructions after the current alternative is successfully matched. */ /* There is a similar code in compile_bracket_matchingpath. */ if (opcode == OP_ONCE) match_once_common(common, ket, CURRENT_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head); stacksize = 0; if (repeat_type == OP_MINUPTO) { /* We need to preserve the counter. TMP2 will be used below. */ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr); stacksize++; } if (ket != OP_KET || bra != OP_BRA) stacksize++; if (offset != 0) { if (common->capture_last_ptr != 0) stacksize++; if (common->optimized_cbracket[offset >> 1] == 0) stacksize += 2; } if (opcode != OP_ONCE) stacksize++; if (stacksize > 0) allocate_stack(common, stacksize); stacksize = 0; if (repeat_type == OP_MINUPTO) { /* TMP2 was set above. */ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1); stacksize++; } if (ket != OP_KET || bra != OP_BRA) { if (ket != OP_KET) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); stacksize++; } if (offset != 0) stacksize = match_capture_common(common, stacksize, offset, private_data_ptr); if (opcode != OP_ONCE) { if (alt_max <= 3) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, alt_count); else put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize)); } if (offset != 0 && ket == OP_KETRMAX && common->optimized_cbracket[offset >> 1] != 0) { /* If ket is not OP_KETRMAX, this code path is executed after the jump to alternative_matchingpath. */ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); } JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->alternative_matchingpath); if (opcode != OP_ONCE) { if (alt_max <= 3) { JUMPHERE(next_alt); alt_count++; if (alt_count < alt_max) { SLJIT_ASSERT(alt_count == 2 && alt_max == 3); next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1); } } else { sljit_set_put_label(put_label, LABEL()); sljit_emit_op0(compiler, SLJIT_ENDBR); } } COMPILE_BACKTRACKINGPATH(current->top); if (current->topbacktracks) set_jumps(current->topbacktracks, LABEL()); SLJIT_ASSERT(!current->nextbacktracks); } while (*cc == OP_ALT); if (cond != NULL) { SLJIT_ASSERT(opcode == OP_COND || opcode == OP_SCOND); assert = CURRENT_AS(bracket_backtrack)->u.assert; if ((ccbegin[1 + LINK_SIZE] == OP_ASSERT_NOT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK_NOT) && assert->framesize >= 0) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } JUMPHERE(cond); } /* Free the STR_PTR. */ if (private_data_ptr == 0) free_stack(common, 1); } if (offset != 0) { /* Using both tmp register is better for instruction scheduling. */ if (common->optimized_cbracket[offset >> 1] != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); free_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0); } else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } } else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND) { OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); } else if (opcode == OP_ONCE) { cc = ccbegin + GET(ccbegin, 1); stacksize = needs_control_head ? 1 : 0; if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) { /* Reset head and drop saved frame. */ stacksize += CURRENT_AS(bracket_backtrack)->u.framesize + ((ket != OP_KET || *cc == OP_ALT) ? 2 : 1); } else if (ket == OP_KETRMAX || (*cc == OP_ALT && ket != OP_KETRMIN)) { /* The STR_PTR must be released. */ stacksize++; } if (stacksize > 0) free_stack(common, stacksize); JUMPHERE(once); /* Restore previous private_data_ptr */ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1)); else if (ket == OP_KETRMIN) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); /* See the comment below. */ free_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } } if (repeat_type == OP_EXACT) { OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0); CMPTO(SLJIT_LESS_EQUAL, TMP1, 0, SLJIT_IMM, repeat_count, exact_label); } else if (ket == OP_KETRMAX) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); if (bra != OP_BRAZERO) free_stack(common, 1); CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath); if (bra == OP_BRAZERO) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath); JUMPHERE(brazero); free_stack(common, 1); } } else if (ket == OP_KETRMIN) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); /* OP_ONCE removes everything in case of a backtrack, so we don't need to explicitly release the STR_PTR. The extra release would affect badly the free_stack(2) above. */ if (opcode != OP_ONCE) free_stack(common, 1); CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, rmin_label); if (opcode == OP_ONCE) free_stack(common, bra == OP_BRAMINZERO ? 2 : 1); else if (bra == OP_BRAMINZERO) free_stack(common, 1); } else if (bra == OP_BRAZERO) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath); JUMPHERE(brazero); } } static SLJIT_INLINE void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; int offset; struct sljit_jump *jump; if (CURRENT_AS(bracketpos_backtrack)->framesize < 0) { if (*current->cc == OP_CBRAPOS || *current->cc == OP_SCBRAPOS) { offset = (GET2(current->cc, 1 + LINK_SIZE)) << 1; OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0); } set_jumps(current->topbacktracks, LABEL()); free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); return; } OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw)); if (current->topbacktracks) { jump = JUMP(SLJIT_JUMP); set_jumps(current->topbacktracks, LABEL()); /* Drop the stack frame. */ free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); JUMPHERE(jump); } OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1)); } static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current) { assert_backtrack backtrack; current->top = NULL; current->topbacktracks = NULL; current->nextbacktracks = NULL; if (current->cc[1] > OP_ASSERTBACK_NOT) { /* Manual call of compile_bracket_matchingpath and compile_bracket_backtrackingpath. */ compile_bracket_matchingpath(common, current->cc, current); compile_bracket_backtrackingpath(common, current->top); } else { memset(&backtrack, 0, sizeof(backtrack)); backtrack.common.cc = current->cc; backtrack.matchingpath = CURRENT_AS(braminzero_backtrack)->matchingpath; /* Manual call of compile_assert_matchingpath. */ compile_assert_matchingpath(common, current->cc, &backtrack, FALSE); } SLJIT_ASSERT(!current->nextbacktracks && !current->topbacktracks); } static SLJIT_INLINE void compile_control_verb_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; PCRE2_UCHAR opcode = *current->cc; struct sljit_label *loop; struct sljit_jump *jump; if (opcode == OP_THEN || opcode == OP_THEN_ARG) { if (common->then_trap != NULL) { SLJIT_ASSERT(common->control_head_ptr != 0); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, type_then_trap); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, common->then_trap->start); jump = JUMP(SLJIT_JUMP); loop = LABEL(); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); JUMPHERE(jump); CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop); CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop); add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP)); return; } else if (!common->local_quit_available && common->in_positive_assertion) { add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP)); return; } } if (common->local_quit_available) { /* Abort match with a fail. */ if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); else JUMPTO(SLJIT_JUMP, common->quit_label); return; } if (opcode == OP_SKIP_ARG) { SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0); add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0)); return; } if (opcode == OP_SKIP) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); else OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_IMM, 0); add_jump(compiler, &common->reset_match, JUMP(SLJIT_JUMP)); } static SLJIT_INLINE void compile_then_trap_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; struct sljit_jump *jump; int size; if (CURRENT_AS(then_trap_backtrack)->then_trap) { common->then_trap = CURRENT_AS(then_trap_backtrack)->then_trap; return; } size = CURRENT_AS(then_trap_backtrack)->framesize; size = 3 + (size < 0 ? 0 : size); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(size - 3)); free_stack(common, size); jump = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL()); /* STACK_TOP is set by THEN. */ if (CURRENT_AS(then_trap_backtrack)->framesize >= 0) { add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw)); } OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 3); JUMPHERE(jump); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0); } static void compile_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; then_trap_backtrack *save_then_trap = common->then_trap; while (current) { if (current->nextbacktracks != NULL) set_jumps(current->nextbacktracks, LABEL()); switch(*current->cc) { case OP_SET_SOM: OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP1, 0); break; case OP_STAR: case OP_MINSTAR: case OP_PLUS: case OP_MINPLUS: case OP_QUERY: case OP_MINQUERY: case OP_UPTO: case OP_MINUPTO: case OP_EXACT: case OP_POSSTAR: case OP_POSPLUS: case OP_POSQUERY: case OP_POSUPTO: case OP_STARI: case OP_MINSTARI: case OP_PLUSI: case OP_MINPLUSI: case OP_QUERYI: case OP_MINQUERYI: case OP_UPTOI: case OP_MINUPTOI: case OP_EXACTI: case OP_POSSTARI: case OP_POSPLUSI: case OP_POSQUERYI: case OP_POSUPTOI: case OP_NOTSTAR: case OP_NOTMINSTAR: case OP_NOTPLUS: case OP_NOTMINPLUS: case OP_NOTQUERY: case OP_NOTMINQUERY: case OP_NOTUPTO: case OP_NOTMINUPTO: case OP_NOTEXACT: case OP_NOTPOSSTAR: case OP_NOTPOSPLUS: case OP_NOTPOSQUERY: case OP_NOTPOSUPTO: case OP_NOTSTARI: case OP_NOTMINSTARI: case OP_NOTPLUSI: case OP_NOTMINPLUSI: case OP_NOTQUERYI: case OP_NOTMINQUERYI: case OP_NOTUPTOI: case OP_NOTMINUPTOI: case OP_NOTEXACTI: case OP_NOTPOSSTARI: case OP_NOTPOSPLUSI: case OP_NOTPOSQUERYI: case OP_NOTPOSUPTOI: case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: case OP_TYPEPOSUPTO: case OP_CLASS: case OP_NCLASS: #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 case OP_XCLASS: #endif compile_iterator_backtrackingpath(common, current); break; case OP_REF: case OP_REFI: case OP_DNREF: case OP_DNREFI: compile_ref_iterator_backtrackingpath(common, current); break; case OP_RECURSE: compile_recurse_backtrackingpath(common, current); break; case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: compile_assert_backtrackingpath(common, current); break; case OP_ASSERT_NA: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRA: case OP_CBRA: case OP_COND: case OP_SBRA: case OP_SCBRA: case OP_SCOND: compile_bracket_backtrackingpath(common, current); break; case OP_BRAZERO: if (current->cc[1] > OP_ASSERTBACK_NOT) compile_bracket_backtrackingpath(common, current); else compile_assert_backtrackingpath(common, current); break; case OP_BRAPOS: case OP_CBRAPOS: case OP_SBRAPOS: case OP_SCBRAPOS: case OP_BRAPOSZERO: compile_bracketpos_backtrackingpath(common, current); break; case OP_BRAMINZERO: compile_braminzero_backtrackingpath(common, current); break; case OP_MARK: OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0)); if (common->has_skip_arg) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, common->has_skip_arg ? 5 : 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0); if (common->has_skip_arg) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0); break; case OP_THEN: case OP_THEN_ARG: case OP_PRUNE: case OP_PRUNE_ARG: case OP_SKIP: case OP_SKIP_ARG: compile_control_verb_backtrackingpath(common, current); break; case OP_COMMIT: case OP_COMMIT_ARG: if (!common->local_quit_available) OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); else JUMPTO(SLJIT_JUMP, common->quit_label); break; case OP_CALLOUT: case OP_CALLOUT_STR: case OP_FAIL: case OP_ACCEPT: case OP_ASSERT_ACCEPT: set_jumps(current->topbacktracks, LABEL()); break; case OP_THEN_TRAP: /* A virtual opcode for then traps. */ compile_then_trap_backtrackingpath(common, current); break; default: SLJIT_UNREACHABLE(); break; } current = current->prev; } common->then_trap = save_then_trap; } static SLJIT_INLINE void compile_recurse(compiler_common *common) { DEFINE_COMPILER; PCRE2_SPTR cc = common->start + common->currententry->start; PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE); PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE); BOOL needs_control_head; BOOL has_quit; BOOL has_accept; int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &needs_control_head, &has_quit, &has_accept); int alt_count, alt_max, local_size; backtrack_common altbacktrack; jump_list *match = NULL; struct sljit_jump *next_alt = NULL; struct sljit_jump *accept_exit = NULL; struct sljit_label *quit; struct sljit_put_label *put_label = NULL; /* Recurse captures then. */ common->then_trap = NULL; SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS); alt_max = no_alternatives(cc); alt_count = 0; /* Matching path. */ SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0); common->currententry->entry_label = LABEL(); set_jumps(common->currententry->entry_calls, common->currententry->entry_label); sljit_emit_fast_enter(compiler, TMP2, 0); count_match(common); local_size = (alt_max > 1) ? 2 : 1; /* (Reversed) stack layout: [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */ allocate_stack(common, private_data_size + local_size); /* Save return address. */ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0); copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, has_quit); /* This variable is saved and restored all time when we enter or exit from a recursive context. */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0); if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); if (alt_max > 1) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); memset(&altbacktrack, 0, sizeof(backtrack_common)); common->quit_label = NULL; common->accept_label = NULL; common->quit = NULL; common->accept = NULL; altbacktrack.cc = ccbegin; cc += GET(cc, 1); while (1) { altbacktrack.top = NULL; altbacktrack.topbacktracks = NULL; if (altbacktrack.cc != ccbegin) OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); compile_matchingpath(common, altbacktrack.cc, cc, &altbacktrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return; allocate_stack(common, (alt_max > 1 || has_accept) ? 2 : 1); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); if (alt_max > 1 || has_accept) { if (alt_max > 3) put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(1)); else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count); } add_jump(compiler, &match, JUMP(SLJIT_JUMP)); if (alt_count == 0) { /* Backtracking path entry. */ SLJIT_ASSERT(common->currententry->backtrack_label == NULL); common->currententry->backtrack_label = LABEL(); set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label); sljit_emit_fast_enter(compiler, TMP1, 0); if (has_accept) accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); /* Save return address. */ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0); copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); if (alt_max > 1) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); free_stack(common, 2); if (alt_max > 3) { sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0); sljit_set_put_label(put_label, LABEL()); sljit_emit_op0(compiler, SLJIT_ENDBR); } else next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); } else free_stack(common, has_accept ? 2 : 1); } else if (alt_max > 3) { sljit_set_put_label(put_label, LABEL()); sljit_emit_op0(compiler, SLJIT_ENDBR); } else { JUMPHERE(next_alt); if (alt_count + 1 < alt_max) { SLJIT_ASSERT(alt_count == 1 && alt_max == 3); next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1); } } alt_count++; compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return; set_jumps(altbacktrack.topbacktracks, LABEL()); if (*cc != OP_ALT) break; altbacktrack.cc = cc + 1 + LINK_SIZE; cc += GET(cc, 1); } /* No alternative is matched. */ quit = LABEL(); copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, has_quit); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); free_stack(common, private_data_size + local_size); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP_SRC(SLJIT_FAST_RETURN, TMP2, 0); if (common->quit != NULL) { SLJIT_ASSERT(has_quit); set_jumps(common->quit, LABEL()); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, has_quit); JUMPTO(SLJIT_JUMP, quit); } if (has_accept) { JUMPHERE(accept_exit); free_stack(common, 2); /* Save return address. */ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0); copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, has_quit); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); free_stack(common, private_data_size + local_size); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); OP_SRC(SLJIT_FAST_RETURN, TMP2, 0); } if (common->accept != NULL) { SLJIT_ASSERT(has_accept); set_jumps(common->accept, LABEL()); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0); allocate_stack(common, 2); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1); } set_jumps(match, LABEL()); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); OP_SRC(SLJIT_FAST_RETURN, TMP2, 0); } #undef COMPILE_BACKTRACKINGPATH #undef CURRENT_AS #define PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS \ (PCRE2_JIT_INVALID_UTF) static int jit_compile(pcre2_code *code, sljit_u32 mode) { pcre2_real_code *re = (pcre2_real_code *)code; struct sljit_compiler *compiler; backtrack_common rootbacktrack; compiler_common common_data; compiler_common *common = &common_data; const sljit_u8 *tables = re->tables; void *allocator_data = &re->memctl; int private_data_size; PCRE2_SPTR ccend; executable_functions *functions; void *executable_func; sljit_uw executable_size; sljit_uw total_length; struct sljit_label *mainloop_label = NULL; struct sljit_label *continue_match_label; struct sljit_label *empty_match_found_label = NULL; struct sljit_label *empty_match_backtrack_label = NULL; struct sljit_label *reset_match_label; struct sljit_label *quit_label; struct sljit_jump *jump; struct sljit_jump *minlength_check_failed = NULL; struct sljit_jump *empty_match = NULL; struct sljit_jump *end_anchor_failed = NULL; jump_list *reqcu_not_found = NULL; SLJIT_ASSERT(tables); #if HAS_VIRTUAL_REGISTERS == 1 SLJIT_ASSERT(sljit_get_register_index(TMP3) < 0 && sljit_get_register_index(ARGUMENTS) < 0 && sljit_get_register_index(RETURN_ADDR) < 0); #elif HAS_VIRTUAL_REGISTERS == 0 SLJIT_ASSERT(sljit_get_register_index(TMP3) >= 0 && sljit_get_register_index(ARGUMENTS) >= 0 && sljit_get_register_index(RETURN_ADDR) >= 0); #else #error "Invalid value for HAS_VIRTUAL_REGISTERS" #endif memset(&rootbacktrack, 0, sizeof(backtrack_common)); memset(common, 0, sizeof(compiler_common)); common->re = re; common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)); rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size; #ifdef SUPPORT_UNICODE common->invalid_utf = (mode & PCRE2_JIT_INVALID_UTF) != 0; #endif /* SUPPORT_UNICODE */ mode &= ~PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS; common->start = rootbacktrack.cc; common->read_only_data_head = NULL; common->fcc = tables + fcc_offset; common->lcc = (sljit_sw)(tables + lcc_offset); common->mode = mode; common->might_be_empty = (re->minlength == 0) || (re->flags & PCRE2_MATCH_EMPTY); common->allow_empty_partial = (re->max_lookbehind > 0) || (re->flags & PCRE2_MATCH_EMPTY); common->nltype = NLTYPE_FIXED; switch(re->newline_convention) { case PCRE2_NEWLINE_CR: common->newline = CHAR_CR; break; case PCRE2_NEWLINE_LF: common->newline = CHAR_NL; break; case PCRE2_NEWLINE_CRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; break; case PCRE2_NEWLINE_ANY: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break; case PCRE2_NEWLINE_ANYCRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break; case PCRE2_NEWLINE_NUL: common->newline = CHAR_NUL; break; default: return PCRE2_ERROR_INTERNAL; } common->nlmax = READ_CHAR_MAX; common->nlmin = 0; if (re->bsr_convention == PCRE2_BSR_UNICODE) common->bsr_nltype = NLTYPE_ANY; else if (re->bsr_convention == PCRE2_BSR_ANYCRLF) common->bsr_nltype = NLTYPE_ANYCRLF; else { #ifdef BSR_ANYCRLF common->bsr_nltype = NLTYPE_ANYCRLF; #else common->bsr_nltype = NLTYPE_ANY; #endif } common->bsr_nlmax = READ_CHAR_MAX; common->bsr_nlmin = 0; common->endonly = (re->overall_options & PCRE2_DOLLAR_ENDONLY) != 0; common->ctypes = (sljit_sw)(tables + ctypes_offset); common->name_count = re->name_count; common->name_entry_size = re->name_entry_size; common->unset_backref = (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) != 0; common->alt_circumflex = (re->overall_options & PCRE2_ALT_CIRCUMFLEX) != 0; #ifdef SUPPORT_UNICODE /* PCRE_UTF[16|32] have the same value as PCRE_UTF8. */ common->utf = (re->overall_options & PCRE2_UTF) != 0; common->ucp = (re->overall_options & PCRE2_UCP) != 0; if (common->utf) { if (common->nltype == NLTYPE_ANY) common->nlmax = 0x2029; else if (common->nltype == NLTYPE_ANYCRLF) common->nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL; else { /* We only care about the first newline character. */ common->nlmax = common->newline & 0xff; } if (common->nltype == NLTYPE_FIXED) common->nlmin = common->newline & 0xff; else common->nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL; if (common->bsr_nltype == NLTYPE_ANY) common->bsr_nlmax = 0x2029; else common->bsr_nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL; common->bsr_nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL; } else common->invalid_utf = FALSE; #endif /* SUPPORT_UNICODE */ ccend = bracketend(common->start); /* Calculate the local space size on the stack. */ common->ovector_start = LIMIT_MATCH + sizeof(sljit_sw); common->optimized_cbracket = (sljit_u8 *)SLJIT_MALLOC(re->top_bracket + 1, allocator_data); if (!common->optimized_cbracket) return PCRE2_ERROR_NOMEMORY; #if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 1 memset(common->optimized_cbracket, 0, re->top_bracket + 1); #else memset(common->optimized_cbracket, 1, re->top_bracket + 1); #endif SLJIT_ASSERT(*common->start == OP_BRA && ccend[-(1 + LINK_SIZE)] == OP_KET); #if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 2 common->capture_last_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); #endif if (!check_opcode_types(common, common->start, ccend)) { SLJIT_FREE(common->optimized_cbracket, allocator_data); return PCRE2_ERROR_NOMEMORY; } /* Checking flags and updating ovector_start. */ if (mode == PCRE2_JIT_COMPLETE && (re->flags & PCRE2_LASTSET) != 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { common->req_char_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } if (mode != PCRE2_JIT_COMPLETE) { common->start_used_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); if (mode == PCRE2_JIT_PARTIAL_SOFT) { common->hit_start = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } } if ((re->overall_options & (PCRE2_FIRSTLINE | PCRE2_USE_OFFSET_LIMIT)) != 0) { common->match_end_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } #if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD common->control_head_ptr = 1; #endif if (common->control_head_ptr != 0) { common->control_head_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } if (common->has_set_som) { /* Saving the real start pointer is necessary. */ common->start_ptr = common->ovector_start; common->ovector_start += sizeof(sljit_sw); } /* Aligning ovector to even number of sljit words. */ if ((common->ovector_start & sizeof(sljit_sw)) != 0) common->ovector_start += sizeof(sljit_sw); if (common->start_ptr == 0) common->start_ptr = OVECTOR(0); /* Capturing brackets cannot be optimized if callouts are allowed. */ if (common->capture_last_ptr != 0) memset(common->optimized_cbracket, 0, re->top_bracket + 1); SLJIT_ASSERT(!(common->req_char_ptr != 0 && common->start_used_ptr != 0)); common->cbra_ptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_sw); total_length = ccend - common->start; common->private_data_ptrs = (sljit_s32 *)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), allocator_data); if (!common->private_data_ptrs) { SLJIT_FREE(common->optimized_cbracket, allocator_data); return PCRE2_ERROR_NOMEMORY; } memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_s32)); private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw); if ((re->overall_options & PCRE2_ANCHORED) == 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && !common->has_skip_in_assert_back) detect_early_fail(common, common->start, &private_data_size, 0, 0, TRUE); set_private_data_ptrs(common, &private_data_size, ccend); SLJIT_ASSERT(common->early_fail_start_ptr <= common->early_fail_end_ptr); if (private_data_size > SLJIT_MAX_LOCAL_SIZE) { SLJIT_FREE(common->private_data_ptrs, allocator_data); SLJIT_FREE(common->optimized_cbracket, allocator_data); return PCRE2_ERROR_NOMEMORY; } if (common->has_then) { common->then_offsets = (sljit_u8 *)(common->private_data_ptrs + total_length); memset(common->then_offsets, 0, total_length); set_then_offsets(common, common->start, NULL); } compiler = sljit_create_compiler(allocator_data, NULL); if (!compiler) { SLJIT_FREE(common->optimized_cbracket, allocator_data); SLJIT_FREE(common->private_data_ptrs, allocator_data); return PCRE2_ERROR_NOMEMORY; } common->compiler = compiler; /* Main pcre_jit_exec entry. */ sljit_emit_enter(compiler, 0, SLJIT_ARG1(SW), 5, 5, 0, 0, private_data_size); /* Register init. */ reset_ovector(common, (re->top_bracket + 1) * 2); if (common->req_char_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, SLJIT_R0, 0); OP1(SLJIT_MOV, ARGUMENTS, 0, SLJIT_S0, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_S0, 0); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match)); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end)); OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0); if (common->early_fail_start_ptr < common->early_fail_end_ptr) reset_early_fail(common); if (mode == PCRE2_JIT_PARTIAL_SOFT) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1); if (common->mark_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0); if (common->control_head_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); /* Main part of the matching */ if ((re->overall_options & PCRE2_ANCHORED) == 0) { mainloop_label = mainloop_entry(common); continue_match_label = LABEL(); /* Forward search if possible. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common)) ; else if ((re->flags & PCRE2_FIRSTSET) != 0) fast_forward_first_char(common); else if ((re->flags & PCRE2_STARTLINE) != 0) fast_forward_newline(common); else if ((re->flags & PCRE2_FIRSTMAPSET) != 0) fast_forward_start_bits(common); } } else continue_match_label = LABEL(); if (mode == PCRE2_JIT_COMPLETE && re->minlength > 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(re->minlength)); minlength_check_failed = CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0); } if (common->req_char_ptr != 0) reqcu_not_found = search_requested_char(common, (PCRE2_UCHAR)(re->last_codeunit), (re->flags & PCRE2_LASTCASELESS) != 0, (re->flags & PCRE2_FIRSTSET) != 0); /* Store the current STR_PTR in OVECTOR(0). */ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0); /* Copy the limit of allowed recursions. */ OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH); if (common->capture_last_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, 0); if (common->fast_forward_bc_ptr != NULL) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3, STR_PTR, 0); if (common->start_ptr != OVECTOR(0)) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_ptr, STR_PTR, 0); /* Copy the beginning of the string. */ if (mode == PCRE2_JIT_PARTIAL_SOFT) { jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); JUMPHERE(jump); } else if (mode == PCRE2_JIT_PARTIAL_HARD) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0); compile_matchingpath(common, common->start, ccend, &rootbacktrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { sljit_free_compiler(compiler); SLJIT_FREE(common->optimized_cbracket, allocator_data); SLJIT_FREE(common->private_data_ptrs, allocator_data); PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data); return PCRE2_ERROR_NOMEMORY; } if ((re->overall_options & PCRE2_ENDANCHORED) != 0) end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0); if (common->might_be_empty) { empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); empty_match_found_label = LABEL(); } common->accept_label = LABEL(); if (common->accept != NULL) set_jumps(common->accept, common->accept_label); /* This means we have a match. Update the ovector. */ copy_ovector(common, re->top_bracket + 1); common->quit_label = common->abort_label = LABEL(); if (common->quit != NULL) set_jumps(common->quit, common->quit_label); if (common->abort != NULL) set_jumps(common->abort, common->abort_label); if (minlength_check_failed != NULL) SET_LABEL(minlength_check_failed, common->abort_label); sljit_emit_op0(compiler, SLJIT_SKIP_FRAMES_BEFORE_RETURN); sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0); if (common->failed_match != NULL) { SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); set_jumps(common->failed_match, LABEL()); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); JUMPTO(SLJIT_JUMP, common->abort_label); } if ((re->overall_options & PCRE2_ENDANCHORED) != 0) JUMPHERE(end_anchor_failed); if (mode != PCRE2_JIT_COMPLETE) { common->partialmatchlabel = LABEL(); set_jumps(common->partialmatch, common->partialmatchlabel); return_with_partial_match(common, common->quit_label); } if (common->might_be_empty) empty_match_backtrack_label = LABEL(); compile_backtrackingpath(common, rootbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { sljit_free_compiler(compiler); SLJIT_FREE(common->optimized_cbracket, allocator_data); SLJIT_FREE(common->private_data_ptrs, allocator_data); PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data); return PCRE2_ERROR_NOMEMORY; } SLJIT_ASSERT(rootbacktrack.prev == NULL); reset_match_label = LABEL(); if (mode == PCRE2_JIT_PARTIAL_SOFT) { /* Update hit_start only in the first time. */ jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, TMP1, 0); JUMPHERE(jump); } /* Check we have remaining characters. */ if ((re->overall_options & PCRE2_ANCHORED) == 0 && common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); } OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), (common->fast_forward_bc_ptr != NULL) ? (PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3) : common->start_ptr); if ((re->overall_options & PCRE2_ANCHORED) == 0) { if (common->ff_newline_shortcut != NULL) { /* There cannot be more newlines if PCRE2_FIRSTLINE is set. */ if ((re->overall_options & PCRE2_FIRSTLINE) == 0) { if (common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP1(SLJIT_MOV, STR_END, 0, TMP1, 0); CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, common->ff_newline_shortcut); OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } else CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, common->ff_newline_shortcut); } } else CMPTO(SLJIT_LESS, STR_PTR, 0, (common->match_end_ptr == 0) ? STR_END : TMP1, 0, mainloop_label); } /* No more remaining characters. */ if (reqcu_not_found != NULL) set_jumps(reqcu_not_found, LABEL()); if (mode == PCRE2_JIT_PARTIAL_SOFT) CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1, common->partialmatchlabel); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); JUMPTO(SLJIT_JUMP, common->quit_label); flush_stubs(common); if (common->might_be_empty) { JUMPHERE(empty_match); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label); OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); JUMPTO(SLJIT_ZERO, empty_match_found_label); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label); JUMPTO(SLJIT_JUMP, empty_match_backtrack_label); } common->fast_forward_bc_ptr = NULL; common->early_fail_start_ptr = 0; common->early_fail_end_ptr = 0; common->currententry = common->entries; common->local_quit_available = TRUE; quit_label = common->quit_label; while (common->currententry != NULL) { /* Might add new entries. */ compile_recurse(common); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { sljit_free_compiler(compiler); SLJIT_FREE(common->optimized_cbracket, allocator_data); SLJIT_FREE(common->private_data_ptrs, allocator_data); PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data); return PCRE2_ERROR_NOMEMORY; } flush_stubs(common); common->currententry = common->currententry->next; } common->local_quit_available = FALSE; common->quit_label = quit_label; /* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */ /* This is a (really) rare case. */ set_jumps(common->stackalloc, LABEL()); /* RETURN_ADDR is not a saved register. */ sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE); OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0); sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0); OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); OP_SRC(SLJIT_FAST_RETURN, TMP1, 0); /* Allocation failed. */ JUMPHERE(jump); /* We break the return address cache here, but this is a really rare case. */ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_JIT_STACKLIMIT); JUMPTO(SLJIT_JUMP, common->quit_label); /* Call limit reached. */ set_jumps(common->calllimit, LABEL()); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_MATCHLIMIT); JUMPTO(SLJIT_JUMP, common->quit_label); if (common->revertframes != NULL) { set_jumps(common->revertframes, LABEL()); do_revertframes(common); } if (common->wordboundary != NULL) { set_jumps(common->wordboundary, LABEL()); check_wordboundary(common); } if (common->anynewline != NULL) { set_jumps(common->anynewline, LABEL()); check_anynewline(common); } if (common->hspace != NULL) { set_jumps(common->hspace, LABEL()); check_hspace(common); } if (common->vspace != NULL) { set_jumps(common->vspace, LABEL()); check_vspace(common); } if (common->casefulcmp != NULL) { set_jumps(common->casefulcmp, LABEL()); do_casefulcmp(common); } if (common->caselesscmp != NULL) { set_jumps(common->caselesscmp, LABEL()); do_caselesscmp(common); } if (common->reset_match != NULL) { set_jumps(common->reset_match, LABEL()); do_reset_match(common, (re->top_bracket + 1) * 2); CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP1, 0, continue_match_label); OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); JUMPTO(SLJIT_JUMP, reset_match_label); } #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utfreadchar != NULL) { set_jumps(common->utfreadchar, LABEL()); do_utfreadchar(common); } if (common->utfreadtype8 != NULL) { set_jumps(common->utfreadtype8, LABEL()); do_utfreadtype8(common); } if (common->utfpeakcharback != NULL) { set_jumps(common->utfpeakcharback, LABEL()); do_utfpeakcharback(common); } #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ #if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 if (common->utfreadchar_invalid != NULL) { set_jumps(common->utfreadchar_invalid, LABEL()); do_utfreadchar_invalid(common); } if (common->utfreadnewline_invalid != NULL) { set_jumps(common->utfreadnewline_invalid, LABEL()); do_utfreadnewline_invalid(common); } if (common->utfmoveback_invalid) { set_jumps(common->utfmoveback_invalid, LABEL()); do_utfmoveback_invalid(common); } if (common->utfpeakcharback_invalid) { set_jumps(common->utfpeakcharback_invalid, LABEL()); do_utfpeakcharback_invalid(common); } #endif /* PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 */ if (common->getucd != NULL) { set_jumps(common->getucd, LABEL()); do_getucd(common); } if (common->getucdtype != NULL) { set_jumps(common->getucdtype, LABEL()); do_getucdtype(common); } #endif /* SUPPORT_UNICODE */ SLJIT_FREE(common->optimized_cbracket, allocator_data); SLJIT_FREE(common->private_data_ptrs, allocator_data); executable_func = sljit_generate_code(compiler); executable_size = sljit_get_generated_code_size(compiler); sljit_free_compiler(compiler); if (executable_func == NULL) { PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data); return PCRE2_ERROR_NOMEMORY; } /* Reuse the function descriptor if possible. */ if (re->executable_jit != NULL) functions = (executable_functions *)re->executable_jit; else { functions = SLJIT_MALLOC(sizeof(executable_functions), allocator_data); if (functions == NULL) { /* This case is highly unlikely since we just recently freed a lot of memory. Not impossible though. */ sljit_free_code(executable_func, NULL); PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data); return PCRE2_ERROR_NOMEMORY; } memset(functions, 0, sizeof(executable_functions)); functions->top_bracket = re->top_bracket + 1; functions->limit_match = re->limit_match; re->executable_jit = functions; } /* Turn mode into an index. */ if (mode == PCRE2_JIT_COMPLETE) mode = 0; else mode = (mode == PCRE2_JIT_PARTIAL_SOFT) ? 1 : 2; SLJIT_ASSERT(mode < JIT_NUMBER_OF_COMPILE_MODES); functions->executable_funcs[mode] = executable_func; functions->read_only_data_heads[mode] = common->read_only_data_head; functions->executable_sizes[mode] = executable_size; return 0; } #endif /************************************************* * JIT compile a Regular Expression * *************************************************/ /* This function used JIT to convert a previously-compiled pattern into machine code. Arguments: code a compiled pattern options JIT option bits Returns: 0: success or (*NOJIT) was used <0: an error code */ #define PUBLIC_JIT_COMPILE_OPTIONS \ (PCRE2_JIT_COMPLETE|PCRE2_JIT_PARTIAL_SOFT|PCRE2_JIT_PARTIAL_HARD|PCRE2_JIT_INVALID_UTF) PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_jit_compile(pcre2_code *code, uint32_t options) { pcre2_real_code *re = (pcre2_real_code *)code; #ifdef SUPPORT_JIT executable_functions *functions; static int executable_allocator_is_working = 0; #endif if (code == NULL) return PCRE2_ERROR_NULL; if ((options & ~PUBLIC_JIT_COMPILE_OPTIONS) != 0) return PCRE2_ERROR_JIT_BADOPTION; /* Support for invalid UTF was first introduced in JIT, with the option PCRE2_JIT_INVALID_UTF. Later, support was added to the interpreter, and the compile-time option PCRE2_MATCH_INVALID_UTF was created. This is now the preferred feature, with the earlier option deprecated. However, for backward compatibility, if the earlier option is set, it forces the new option so that if JIT matching falls back to the interpreter, there is still support for invalid UTF. However, if this function has already been successfully called without PCRE2_JIT_INVALID_UTF and without PCRE2_MATCH_INVALID_UTF (meaning that non-invalid-supporting JIT code was compiled), give an error. If in the future support for PCRE2_JIT_INVALID_UTF is withdrawn, the following actions are needed: 1. Remove the definition from pcre2.h.in and from the list in PUBLIC_JIT_COMPILE_OPTIONS above. 2. Replace PCRE2_JIT_INVALID_UTF with a local flag in this module. 3. Replace PCRE2_JIT_INVALID_UTF in pcre2_jit_test.c. 4. Delete the following short block of code. The setting of "re" and "functions" can be moved into the JIT-only block below, but if that is done, (void)re and (void)functions will be needed in the non-JIT case, to avoid compiler warnings. */ #ifdef SUPPORT_JIT functions = (executable_functions *)re->executable_jit; #endif if ((options & PCRE2_JIT_INVALID_UTF) != 0) { if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) == 0) { #ifdef SUPPORT_JIT if (functions != NULL) return PCRE2_ERROR_JIT_BADOPTION; #endif re->overall_options |= PCRE2_MATCH_INVALID_UTF; } } /* The above tests are run with and without JIT support. This means that PCRE2_JIT_INVALID_UTF propagates back into the regex options (ensuring interpreter support) even in the absence of JIT. But now, if there is no JIT support, give an error return. */ #ifndef SUPPORT_JIT return PCRE2_ERROR_JIT_BADOPTION; #else /* SUPPORT_JIT */ /* There is JIT support. Do the necessary. */ if ((re->flags & PCRE2_NOJIT) != 0) return 0; if (executable_allocator_is_working == 0) { /* Checks whether the executable allocator is working. This check might run multiple times in multi-threaded environments, but the result should not be affected by it. */ void *ptr = SLJIT_MALLOC_EXEC(32, NULL); executable_allocator_is_working = -1; if (ptr != NULL) { SLJIT_FREE_EXEC(((sljit_u8*)(ptr)) + SLJIT_EXEC_OFFSET(ptr), NULL); executable_allocator_is_working = 1; } } if (executable_allocator_is_working < 0) return PCRE2_ERROR_NOMEMORY; if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0) options |= PCRE2_JIT_INVALID_UTF; if ((options & PCRE2_JIT_COMPLETE) != 0 && (functions == NULL || functions->executable_funcs[0] == NULL)) { uint32_t excluded_options = (PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD); int result = jit_compile(code, options & ~excluded_options); if (result != 0) return result; } if ((options & PCRE2_JIT_PARTIAL_SOFT) != 0 && (functions == NULL || functions->executable_funcs[1] == NULL)) { uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_HARD); int result = jit_compile(code, options & ~excluded_options); if (result != 0) return result; } if ((options & PCRE2_JIT_PARTIAL_HARD) != 0 && (functions == NULL || functions->executable_funcs[2] == NULL)) { uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT); int result = jit_compile(code, options & ~excluded_options); if (result != 0) return result; } return 0; #endif /* SUPPORT_JIT */ } /* JIT compiler uses an all-in-one approach. This improves security, since the code generator functions are not exported. */ #define INCLUDED_FROM_PCRE2_JIT_COMPILE #include "pcre2_jit_match.c" #include "pcre2_jit_misc.c" /* End of pcre2_jit_compile.c */ vfu-4.22/vstring/pcre2/pcre2_newline.c0000644000175000017500000001432414145574056016241 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains internal functions for testing newlines when more than one kind of newline is to be recognized. When a newline is found, its length is returned. In principle, we could implement several newline "types", each referring to a different set of newline characters. At present, PCRE2 supports only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF, and NLTYPE_ANY. The full list of Unicode newline characters is taken from http://unicode.org/unicode/reports/tr18/. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Check for newline at given position * *************************************************/ /* This function is called only via the IS_NEWLINE macro, which does so only when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the code unit pointed to by ptr is less than the end of the string. Arguments: ptr pointer to possible newline type the newline type endptr pointer to the end of the string lenptr where to return the length utf TRUE if in utf mode Returns: TRUE or FALSE */ BOOL PRIV(is_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR endptr, uint32_t *lenptr, BOOL utf) { uint32_t c; #ifdef SUPPORT_UNICODE if (utf) { GETCHAR(c, ptr); } else c = *ptr; #else (void)utf; c = *ptr; #endif /* SUPPORT_UNICODE */ if (type == NLTYPE_ANYCRLF) switch(c) { case CHAR_LF: *lenptr = 1; return TRUE; case CHAR_CR: *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1; return TRUE; default: return FALSE; } /* NLTYPE_ANY */ else switch(c) { #ifdef EBCDIC case CHAR_NEL: #endif case CHAR_LF: case CHAR_VT: case CHAR_FF: *lenptr = 1; return TRUE; case CHAR_CR: *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1; return TRUE; #ifndef EBCDIC #if PCRE2_CODE_UNIT_WIDTH == 8 case CHAR_NEL: *lenptr = utf? 2 : 1; return TRUE; case 0x2028: /* LS */ case 0x2029: /* PS */ *lenptr = 3; return TRUE; #else /* 16-bit or 32-bit code units */ case CHAR_NEL: case 0x2028: /* LS */ case 0x2029: /* PS */ *lenptr = 1; return TRUE; #endif #endif /* Not EBCDIC */ default: return FALSE; } } /************************************************* * Check for newline at previous position * *************************************************/ /* This function is called only via the WAS_NEWLINE macro, which does so only when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the initial value of ptr is greater than the start of the string that is being processed. Arguments: ptr pointer to possible newline type the newline type startptr pointer to the start of the string lenptr where to return the length utf TRUE if in utf mode Returns: TRUE or FALSE */ BOOL PRIV(was_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR startptr, uint32_t *lenptr, BOOL utf) { uint32_t c; ptr--; #ifdef SUPPORT_UNICODE if (utf) { BACKCHAR(ptr); GETCHAR(c, ptr); } else c = *ptr; #else (void)utf; c = *ptr; #endif /* SUPPORT_UNICODE */ if (type == NLTYPE_ANYCRLF) switch(c) { case CHAR_LF: *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1; return TRUE; case CHAR_CR: *lenptr = 1; return TRUE; default: return FALSE; } /* NLTYPE_ANY */ else switch(c) { case CHAR_LF: *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1; return TRUE; #ifdef EBCDIC case CHAR_NEL: #endif case CHAR_VT: case CHAR_FF: case CHAR_CR: *lenptr = 1; return TRUE; #ifndef EBCDIC #if PCRE2_CODE_UNIT_WIDTH == 8 case CHAR_NEL: *lenptr = utf? 2 : 1; return TRUE; case 0x2028: /* LS */ case 0x2029: /* PS */ *lenptr = 3; return TRUE; #else /* 16-bit or 32-bit code units */ case CHAR_NEL: case 0x2028: /* LS */ case 0x2029: /* PS */ *lenptr = 1; return TRUE; #endif #endif /* Not EBCDIC */ default: return FALSE; } } /* End of pcre2_newline.c */ vfu-4.22/vstring/pcre2/pcre2_ord2utf.c0000644000175000017500000000723514145574056016170 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This file contains a function that converts a Unicode character code point into a UTF string. The behaviour is different for each code unit width. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /* If SUPPORT_UNICODE is not defined, this function will never be called. Supply a dummy function because some compilers do not like empty source modules. */ #ifndef SUPPORT_UNICODE unsigned int PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer) { (void)(cvalue); (void)(buffer); return 0; } #else /* SUPPORT_UNICODE */ /************************************************* * Convert code point to UTF * *************************************************/ /* Arguments: cvalue the character value buffer pointer to buffer for result Returns: number of code units placed in the buffer */ unsigned int PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer) { /* Convert to UTF-8 */ #if PCRE2_CODE_UNIT_WIDTH == 8 int i, j; for (i = 0; i < PRIV(utf8_table1_size); i++) if ((int)cvalue <= PRIV(utf8_table1)[i]) break; buffer += i; for (j = i; j > 0; j--) { *buffer-- = 0x80 | (cvalue & 0x3f); cvalue >>= 6; } *buffer = PRIV(utf8_table2)[i] | cvalue; return i + 1; /* Convert to UTF-16 */ #elif PCRE2_CODE_UNIT_WIDTH == 16 if (cvalue <= 0xffff) { *buffer = (PCRE2_UCHAR)cvalue; return 1; } cvalue -= 0x10000; *buffer++ = 0xd800 | (cvalue >> 10); *buffer = 0xdc00 | (cvalue & 0x3ff); return 2; /* Convert to UTF-32 */ #else *buffer = (PCRE2_UCHAR)cvalue; return 1; #endif } #endif /* SUPPORT_UNICODE */ /* End of pcre_ord2utf.c */ vfu-4.22/vstring/pcre2/pcre2_convert.c0000644000175000017500000007434614145574056016272 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #define TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \ PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED) #define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \ PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \ PCRE2_CONVERT_GLOB_NO_STARSTAR| \ TYPE_OPTIONS) #define DUMMY_BUFFER_SIZE 100 /* Generated pattern fragments */ #define STR_BACKSLASH_A STR_BACKSLASH STR_A #define STR_BACKSLASH_z STR_BACKSLASH STR_z #define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET #define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN #define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS #define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS #define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS /* States for range and POSIX processing */ enum { RANGE_NOT_STARTED, RANGE_STARTING, RANGE_STARTED }; enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET, POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED }; /* Macro to add a character string to the output buffer, checking for overflow. */ #define PUTCHARS(string) \ { \ for (s = (char *)(string); *s != 0; s++) \ { \ if (p >= endp) return PCRE2_ERROR_NOMEMORY; \ *p++ = *s; \ } \ } /* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */ static const char *pcre2_escaped_literals = STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS; /* Recognized escaped metacharacters in POSIX basic patterns. */ static const char *posix_meta_escapes = STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9; /************************************************* * Convert a POSIX pattern * *************************************************/ /* This function handles both basic and extended POSIX patterns. Arguments: pattype the pattern type pattern the pattern plength length in code units utf TRUE if UTF use_buffer where to put the output use_length length of use_buffer bufflenptr where to put the used length dummyrun TRUE if a dummy run ccontext the convert context Returns: 0 => success !0 => error code */ static int convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength, BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) { char *s; PCRE2_SPTR posix = pattern; PCRE2_UCHAR *p = use_buffer; PCRE2_UCHAR *pp = p; PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */ PCRE2_SIZE convlength = 0; uint32_t bracount = 0; uint32_t posix_state = POSIX_START_REGEX; uint32_t lastspecial = 0; BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0; BOOL nextisliteral = FALSE; (void)utf; /* Not used when Unicode not supported */ (void)ccontext; /* Not currently used */ /* Initialize default for error offset as end of input. */ *bufflenptr = plength; PUTCHARS(STR_STAR_NUL); /* Now scan the input. */ while (plength > 0) { uint32_t c, sc; int clength = 1; /* Add in the length of the last item, then, if in the dummy run, pull the pointer back to the start of the (temporary) buffer and then remember the start of the next item. */ convlength += p - pp; if (dummyrun) p = use_buffer; pp = p; /* Pick up the next character */ #ifndef SUPPORT_UNICODE c = *posix; #else GETCHARLENTEST(c, posix, clength); #endif posix += clength; plength -= clength; sc = nextisliteral? 0 : c; nextisliteral = FALSE; /* Handle a character within a class. */ if (posix_state >= POSIX_CLASS_NOT_STARTED) { if (c == CHAR_RIGHT_SQUARE_BRACKET) { PUTCHARS(STR_RIGHT_SQUARE_BRACKET); posix_state = POSIX_NOT_BRACKET; } /* Not the end of the class */ else { switch (posix_state) { case POSIX_CLASS_STARTED: if (c <= 127 && islower(c)) break; /* Remain in started state */ posix_state = POSIX_CLASS_NOT_STARTED; if (c == CHAR_COLON && plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET) { PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET); plength--; posix++; continue; /* With next character after :] */ } /* Fall through */ case POSIX_CLASS_NOT_STARTED: if (c == CHAR_LEFT_SQUARE_BRACKET) posix_state = POSIX_CLASS_STARTING; break; case POSIX_CLASS_STARTING: if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED; break; } if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH); if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; memcpy(p, posix - clength, CU2BYTES(clength)); p += clength; } } /* Handle a character not within a class. */ else switch(sc) { case CHAR_LEFT_SQUARE_BRACKET: PUTCHARS(STR_LEFT_SQUARE_BRACKET); #ifdef NEVER /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does support) but they are not part of POSIX 1003.1. */ if (plength >= 6) { if (posix[0] == CHAR_LEFT_SQUARE_BRACKET && posix[1] == CHAR_COLON && (posix[2] == CHAR_LESS_THAN_SIGN || posix[2] == CHAR_GREATER_THAN_SIGN) && posix[3] == CHAR_COLON && posix[4] == CHAR_RIGHT_SQUARE_BRACKET && posix[5] == CHAR_RIGHT_SQUARE_BRACKET) { if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY; memcpy(p, posix, CU2BYTES(6)); p += 6; posix += 6; plength -= 6; continue; /* With next character */ } } #endif /* Handle start of "normal" character classes */ posix_state = POSIX_CLASS_NOT_STARTED; /* Handle ^ and ] as first characters */ if (plength > 0) { if (*posix == CHAR_CIRCUMFLEX_ACCENT) { posix++; plength--; PUTCHARS(STR_CIRCUMFLEX_ACCENT); } if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET) { posix++; plength--; PUTCHARS(STR_RIGHT_SQUARE_BRACKET); } } break; case CHAR_BACKSLASH: if (plength == 0) return PCRE2_ERROR_END_BACKSLASH; if (extended) nextisliteral = TRUE; else { if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL) { if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH); if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; lastspecial = *p++ = *posix++; plength--; } else nextisliteral = TRUE; } break; case CHAR_RIGHT_PARENTHESIS: if (!extended || bracount == 0) goto ESCAPE_LITERAL; bracount--; goto COPY_SPECIAL; case CHAR_LEFT_PARENTHESIS: bracount++; /* Fall through */ case CHAR_QUESTION_MARK: case CHAR_PLUS: case CHAR_LEFT_CURLY_BRACKET: case CHAR_RIGHT_CURLY_BRACKET: case CHAR_VERTICAL_LINE: if (!extended) goto ESCAPE_LITERAL; /* Fall through */ case CHAR_DOT: case CHAR_DOLLAR_SIGN: posix_state = POSIX_NOT_BRACKET; COPY_SPECIAL: lastspecial = c; if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; *p++ = c; break; case CHAR_ASTERISK: if (lastspecial != CHAR_ASTERISK) { if (!extended && (posix_state < POSIX_NOT_BRACKET || lastspecial == CHAR_LEFT_PARENTHESIS)) goto ESCAPE_LITERAL; goto COPY_SPECIAL; } break; /* Ignore second and subsequent asterisks */ case CHAR_CIRCUMFLEX_ACCENT: if (extended) goto COPY_SPECIAL; if (posix_state == POSIX_START_REGEX || lastspecial == CHAR_LEFT_PARENTHESIS) { posix_state = POSIX_ANCHORED; goto COPY_SPECIAL; } /* Fall through */ default: if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) { ESCAPE_LITERAL: PUTCHARS(STR_BACKSLASH); } lastspecial = 0xff; /* Indicates nothing special */ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; memcpy(p, posix - clength, CU2BYTES(clength)); p += clength; posix_state = POSIX_NOT_BRACKET; break; } } if (posix_state >= POSIX_CLASS_NOT_STARTED) return PCRE2_ERROR_MISSING_SQUARE_BRACKET; convlength += p - pp; /* Final segment */ *bufflenptr = convlength; *p++ = 0; return 0; } /************************************************* * Convert a glob pattern * *************************************************/ /* Context for writing the output into a buffer. */ typedef struct pcre2_output_context { PCRE2_UCHAR *output; /* current output position */ PCRE2_SPTR output_end; /* output end */ PCRE2_SIZE output_size; /* size of the output */ uint8_t out_str[8]; /* string copied to the output */ } pcre2_output_context; /* Write a character into the output. Arguments: out output context chr the next character */ static void convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr) { out->output_size++; if (out->output < out->output_end) *out->output++ = chr; } /* Write a string into the output. Arguments: out output context length length of out->out_str */ static void convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length) { uint8_t *out_str = out->out_str; PCRE2_UCHAR *output = out->output; PCRE2_SPTR output_end = out->output_end; PCRE2_SIZE output_size = out->output_size; do { output_size++; if (output < output_end) *output++ = *out_str++; } while (--length != 0); out->output = output; out->output_size = output_size; } /* Prints the separator into the output. Arguments: out output context separator glob separator with_escape backslash is needed before separator */ static void convert_glob_print_separator(pcre2_output_context *out, PCRE2_UCHAR separator, BOOL with_escape) { if (with_escape) convert_glob_write(out, CHAR_BACKSLASH); convert_glob_write(out, separator); } /* Prints a wildcard into the output. Arguments: out output context separator glob separator with_escape backslash is needed before separator */ static void convert_glob_print_wildcard(pcre2_output_context *out, PCRE2_UCHAR separator, BOOL with_escape) { out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; convert_glob_write_str(out, 2); convert_glob_print_separator(out, separator, with_escape); convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET); } /* Parse a posix class. Arguments: from starting point of scanning the range pattern_end end of pattern out output context Returns: >0 => class index 0 => malformed class */ static int convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, pcre2_output_context *out) { static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:" "graph:lower:print:punct:space:upper:word:xdigit:"; PCRE2_SPTR start = *from + 1; PCRE2_SPTR pattern = start; const char *class_ptr; PCRE2_UCHAR c; int class_index; while (TRUE) { if (pattern >= pattern_end) return 0; c = *pattern++; if (c < CHAR_a || c > CHAR_z) break; } if (c != CHAR_COLON || pattern >= pattern_end || *pattern != CHAR_RIGHT_SQUARE_BRACKET) return 0; class_ptr = posix_classes; class_index = 1; while (TRUE) { if (*class_ptr == CHAR_NUL) return 0; pattern = start; while (*pattern == (PCRE2_UCHAR) *class_ptr) { if (*pattern == CHAR_COLON) { pattern += 2; start -= 2; do convert_glob_write(out, *start++); while (start < pattern); *from = pattern; return class_index; } pattern++; class_ptr++; } while (*class_ptr != CHAR_COLON) class_ptr++; class_ptr++; class_index++; } } /* Checks whether the character is in the class. Arguments: class_index class index c character Returns: !0 => character is found in the class 0 => otherwise */ static BOOL convert_glob_char_in_class(int class_index, PCRE2_UCHAR c) { switch (class_index) { case 1: return isalnum(c); case 2: return isalpha(c); case 3: return 1; case 4: return c == CHAR_HT || c == CHAR_SPACE; case 5: return iscntrl(c); case 6: return isdigit(c); case 7: return isgraph(c); case 8: return islower(c); case 9: return isprint(c); case 10: return ispunct(c); case 11: return isspace(c); case 12: return isupper(c); case 13: return isalnum(c) || c == CHAR_UNDERSCORE; default: return isxdigit(c); } } /* Parse a range of characters. Arguments: from starting point of scanning the range pattern_end end of pattern out output context separator glob separator with_escape backslash is needed before separator Returns: 0 => success !0 => error code */ static int convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator, BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep) { BOOL is_negative = FALSE; BOOL separator_seen = FALSE; BOOL has_prev_c; PCRE2_SPTR pattern = *from; PCRE2_SPTR char_start = NULL; uint32_t c, prev_c; int len, class_index; (void)utf; /* Avoid compiler warning. */ if (pattern >= pattern_end) { *from = pattern; return PCRE2_ERROR_MISSING_SQUARE_BRACKET; } if (*pattern == CHAR_EXCLAMATION_MARK || *pattern == CHAR_CIRCUMFLEX_ACCENT) { pattern++; if (pattern >= pattern_end) { *from = pattern; return PCRE2_ERROR_MISSING_SQUARE_BRACKET; } is_negative = TRUE; out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; len = 2; if (!no_wildsep) { if (with_escape) { out->out_str[len] = CHAR_BACKSLASH; len++; } out->out_str[len] = (uint8_t) separator; } convert_glob_write_str(out, len + 1); } else convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET); has_prev_c = FALSE; prev_c = 0; if (*pattern == CHAR_RIGHT_SQUARE_BRACKET) { out->out_str[0] = CHAR_BACKSLASH; out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET; convert_glob_write_str(out, 2); has_prev_c = TRUE; prev_c = CHAR_RIGHT_SQUARE_BRACKET; pattern++; } while (pattern < pattern_end) { char_start = pattern; GETCHARINCTEST(c, pattern); if (c == CHAR_RIGHT_SQUARE_BRACKET) { convert_glob_write(out, c); if (!is_negative && !no_wildsep && separator_seen) { out->out_str[0] = CHAR_LEFT_PARENTHESIS; out->out_str[1] = CHAR_QUESTION_MARK; out->out_str[2] = CHAR_LESS_THAN_SIGN; out->out_str[3] = CHAR_EXCLAMATION_MARK; convert_glob_write_str(out, 4); convert_glob_print_separator(out, separator, with_escape); convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); } *from = pattern; return 0; } if (pattern >= pattern_end) break; if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) { *from = pattern; class_index = convert_glob_parse_class(from, pattern_end, out); if (class_index != 0) { pattern = *from; has_prev_c = FALSE; prev_c = 0; if (!is_negative && convert_glob_char_in_class (class_index, separator)) separator_seen = TRUE; continue; } } else if (c == CHAR_MINUS && has_prev_c && *pattern != CHAR_RIGHT_SQUARE_BRACKET) { convert_glob_write(out, CHAR_MINUS); char_start = pattern; GETCHARINCTEST(c, pattern); if (pattern >= pattern_end) break; if (escape != 0 && c == escape) { char_start = pattern; GETCHARINCTEST(c, pattern); } else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) { *from = pattern; return PCRE2_ERROR_CONVERT_SYNTAX; } if (prev_c > c) { *from = pattern; return PCRE2_ERROR_CONVERT_SYNTAX; } if (prev_c < separator && separator < c) separator_seen = TRUE; has_prev_c = FALSE; prev_c = 0; } else { if (escape != 0 && c == escape) { char_start = pattern; GETCHARINCTEST(c, pattern); if (pattern >= pattern_end) break; } has_prev_c = TRUE; prev_c = c; } if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET || c == CHAR_BACKSLASH || c == CHAR_MINUS) convert_glob_write(out, CHAR_BACKSLASH); if (c == separator) separator_seen = TRUE; do convert_glob_write(out, *char_start++); while (char_start < pattern); } *from = pattern; return PCRE2_ERROR_MISSING_SQUARE_BRACKET; } /* Prints a (*COMMIT) into the output. Arguments: out output context */ static void convert_glob_print_commit(pcre2_output_context *out) { out->out_str[0] = CHAR_LEFT_PARENTHESIS; out->out_str[1] = CHAR_ASTERISK; out->out_str[2] = CHAR_C; out->out_str[3] = CHAR_O; out->out_str[4] = CHAR_M; out->out_str[5] = CHAR_M; out->out_str[6] = CHAR_I; out->out_str[7] = CHAR_T; convert_glob_write_str(out, 8); convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); } /* Bash glob converter. Arguments: pattype the pattern type pattern the pattern plength length in code units utf TRUE if UTF use_buffer where to put the output use_length length of use_buffer bufflenptr where to put the used length dummyrun TRUE if a dummy run ccontext the convert context Returns: 0 => success !0 => error code */ static int convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength, BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) { pcre2_output_context out; PCRE2_SPTR pattern_start = pattern; PCRE2_SPTR pattern_end = pattern + plength; PCRE2_UCHAR separator = ccontext->glob_separator; PCRE2_UCHAR escape = ccontext->glob_escape; PCRE2_UCHAR c; BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0; BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0; BOOL in_atomic = FALSE; BOOL after_starstar = FALSE; BOOL no_slash_z = FALSE; BOOL with_escape, is_start, after_separator; int result = 0; (void)utf; /* Avoid compiler warning. */ #ifdef SUPPORT_UNICODE if (utf && (separator >= 128 || escape >= 128)) { /* Currently only ASCII characters are supported. */ *bufflenptr = 0; return PCRE2_ERROR_CONVERT_SYNTAX; } #endif with_escape = strchr(pcre2_escaped_literals, separator) != NULL; /* Initialize default for error offset as end of input. */ out.output = use_buffer; out.output_end = use_buffer + use_length; out.output_size = 0; out.out_str[0] = CHAR_LEFT_PARENTHESIS; out.out_str[1] = CHAR_QUESTION_MARK; out.out_str[2] = CHAR_s; out.out_str[3] = CHAR_RIGHT_PARENTHESIS; convert_glob_write_str(&out, 4); is_start = TRUE; if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK) { if (no_wildsep) is_start = FALSE; else if (!no_starstar && pattern + 1 < pattern_end && pattern[1] == CHAR_ASTERISK) is_start = FALSE; } if (is_start) { out.out_str[0] = CHAR_BACKSLASH; out.out_str[1] = CHAR_A; convert_glob_write_str(&out, 2); } while (pattern < pattern_end) { c = *pattern++; if (c == CHAR_ASTERISK) { is_start = pattern == pattern_start + 1; if (in_atomic) { convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); in_atomic = FALSE; } if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK) { after_separator = is_start || (pattern[-2] == separator); do pattern++; while (pattern < pattern_end && *pattern == CHAR_ASTERISK); if (pattern >= pattern_end) { no_slash_z = TRUE; break; } after_starstar = TRUE; if (after_separator && escape != 0 && *pattern == escape && pattern + 1 < pattern_end && pattern[1] == separator) pattern++; if (is_start) { if (*pattern != separator) continue; out.out_str[0] = CHAR_LEFT_PARENTHESIS; out.out_str[1] = CHAR_QUESTION_MARK; out.out_str[2] = CHAR_COLON; out.out_str[3] = CHAR_BACKSLASH; out.out_str[4] = CHAR_A; out.out_str[5] = CHAR_VERTICAL_LINE; convert_glob_write_str(&out, 6); convert_glob_print_separator(&out, separator, with_escape); convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); pattern++; continue; } convert_glob_print_commit(&out); if (!after_separator || *pattern != separator) { out.out_str[0] = CHAR_DOT; out.out_str[1] = CHAR_ASTERISK; out.out_str[2] = CHAR_QUESTION_MARK; convert_glob_write_str(&out, 3); continue; } out.out_str[0] = CHAR_LEFT_PARENTHESIS; out.out_str[1] = CHAR_QUESTION_MARK; out.out_str[2] = CHAR_COLON; out.out_str[3] = CHAR_DOT; out.out_str[4] = CHAR_ASTERISK; out.out_str[5] = CHAR_QUESTION_MARK; convert_glob_write_str(&out, 6); convert_glob_print_separator(&out, separator, with_escape); out.out_str[0] = CHAR_RIGHT_PARENTHESIS; out.out_str[1] = CHAR_QUESTION_MARK; out.out_str[2] = CHAR_QUESTION_MARK; convert_glob_write_str(&out, 3); pattern++; continue; } if (pattern < pattern_end && *pattern == CHAR_ASTERISK) { do pattern++; while (pattern < pattern_end && *pattern == CHAR_ASTERISK); } if (no_wildsep) { if (pattern >= pattern_end) { no_slash_z = TRUE; break; } /* Start check must be after the end check. */ if (is_start) continue; } if (!is_start) { if (after_starstar) { out.out_str[0] = CHAR_LEFT_PARENTHESIS; out.out_str[1] = CHAR_QUESTION_MARK; out.out_str[2] = CHAR_GREATER_THAN_SIGN; convert_glob_write_str(&out, 3); in_atomic = TRUE; } else convert_glob_print_commit(&out); } if (no_wildsep) convert_glob_write(&out, CHAR_DOT); else convert_glob_print_wildcard(&out, separator, with_escape); out.out_str[0] = CHAR_ASTERISK; out.out_str[1] = CHAR_QUESTION_MARK; if (pattern >= pattern_end) out.out_str[1] = CHAR_PLUS; convert_glob_write_str(&out, 2); continue; } if (c == CHAR_QUESTION_MARK) { if (no_wildsep) convert_glob_write(&out, CHAR_DOT); else convert_glob_print_wildcard(&out, separator, with_escape); continue; } if (c == CHAR_LEFT_SQUARE_BRACKET) { result = convert_glob_parse_range(&pattern, pattern_end, &out, utf, separator, with_escape, escape, no_wildsep); if (result != 0) break; continue; } if (escape != 0 && c == escape) { if (pattern >= pattern_end) { result = PCRE2_ERROR_CONVERT_SYNTAX; break; } c = *pattern++; } if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) convert_glob_write(&out, CHAR_BACKSLASH); convert_glob_write(&out, c); } if (result == 0) { if (!no_slash_z) { out.out_str[0] = CHAR_BACKSLASH; out.out_str[1] = CHAR_z; convert_glob_write_str(&out, 2); } if (in_atomic) convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); convert_glob_write(&out, CHAR_NUL); if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer)) result = PCRE2_ERROR_NOMEMORY; } if (result != 0) { *bufflenptr = pattern - pattern_start; return result; } *bufflenptr = out.output_size - 1; return 0; } /************************************************* * Convert pattern * *************************************************/ /* This is the external-facing function for converting other forms of pattern into PCRE2 regular expression patterns. On error, the bufflenptr argument is used to return an offset in the original pattern. Arguments: pattern the input pattern plength length of input, or PCRE2_ZERO_TERMINATED options options bits buffptr pointer to pointer to output buffer bufflenptr pointer to length of output buffer ccontext convert context or NULL Returns: 0 for success, else an error code (+ve or -ve) */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options, PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr, pcre2_convert_context *ccontext) { int i, rc; PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE]; PCRE2_UCHAR *use_buffer = dummy_buffer; PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE; BOOL utf = (options & PCRE2_CONVERT_UTF) != 0; uint32_t pattype = options & TYPE_OPTIONS; if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL; if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */ (pattype & (~pattype+1)) != pattype || /* More than one type set */ pattype == 0) /* No type set */ { *bufflenptr = 0; /* Error offset */ return PCRE2_ERROR_BADOPTION; } if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern); if (ccontext == NULL) ccontext = (pcre2_convert_context *)(&PRIV(default_convert_context)); /* Check UTF if required. */ #ifndef SUPPORT_UNICODE if (utf) { *bufflenptr = 0; /* Error offset */ return PCRE2_ERROR_UNICODE_NOT_SUPPORTED; } #else if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0) { PCRE2_SIZE erroroffset; rc = PRIV(valid_utf)(pattern, plength, &erroroffset); if (rc != 0) { *bufflenptr = erroroffset; return rc; } } #endif /* If buffptr is not NULL, and what it points to is not NULL, we are being provided with a buffer and a length, so set them as the buffer to use. */ if (buffptr != NULL && *buffptr != NULL) { use_buffer = *buffptr; use_length = *bufflenptr; } /* Call an individual converter, either just once (if a buffer was provided or just the length is needed), or twice (if a memory allocation is required). */ for (i = 0; i < 2; i++) { PCRE2_UCHAR *allocated; BOOL dummyrun = buffptr == NULL || *buffptr == NULL; switch(pattype) { case PCRE2_CONVERT_GLOB: rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf, use_buffer, use_length, bufflenptr, dummyrun, ccontext); break; case PCRE2_CONVERT_POSIX_BASIC: case PCRE2_CONVERT_POSIX_EXTENDED: rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length, bufflenptr, dummyrun, ccontext); break; default: *bufflenptr = 0; /* Error offset */ return PCRE2_ERROR_INTERNAL; } if (rc != 0 || /* Error */ buffptr == NULL || /* Just the length is required */ *buffptr != NULL) /* Buffer was provided or allocated */ return rc; /* Allocate memory for the buffer, with hidden space for an allocator at the start. The next time round the loop runs the conversion for real. */ allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) + (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext); if (allocated == NULL) return PCRE2_ERROR_NOMEMORY; *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl)); use_buffer = *buffptr; use_length = *bufflenptr + 1; } /* Control should never get here. */ return PCRE2_ERROR_INTERNAL; } /************************************************* * Free converted pattern * *************************************************/ /* This frees a converted pattern that was put in newly-allocated memory. Argument: the converted pattern Returns: nothing */ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_converted_pattern_free(PCRE2_UCHAR *converted) { if (converted != NULL) { pcre2_memctl *memctl = (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl)); memctl->free(memctl, memctl->memory_data); } } /* End of pcre2_convert.c */ vfu-4.22/vstring/pcre2/pcre2_string_utils.c0000644000175000017500000001402114145574056017320 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains internal functions for comparing and finding the length of strings. These are used instead of strcmp() etc because the standard functions work only on 8-bit data. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Emulated memmove() for systems without it * *************************************************/ /* This function can make use of bcopy() if it is available. Otherwise do it by steam, as there some non-Unix environments that lack both memmove() and bcopy(). */ #if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) void * PRIV(memmove)(void *d, const void *s, size_t n) { #ifdef HAVE_BCOPY bcopy(s, d, n); return d; #else size_t i; unsigned char *dest = (unsigned char *)d; const unsigned char *src = (const unsigned char *)s; if (dest > src) { dest += n; src += n; for (i = 0; i < n; ++i) *(--dest) = *(--src); return (void *)dest; } else { for (i = 0; i < n; ++i) *dest++ = *src++; return (void *)(dest - n); } #endif /* not HAVE_BCOPY */ } #endif /* not VPCOMPAT && not HAVE_MEMMOVE */ /************************************************* * Compare two zero-terminated PCRE2 strings * *************************************************/ /* Arguments: str1 first string str2 second string Returns: 0, 1, or -1 */ int PRIV(strcmp)(PCRE2_SPTR str1, PCRE2_SPTR str2) { PCRE2_UCHAR c1, c2; while (*str1 != '\0' || *str2 != '\0') { c1 = *str1++; c2 = *str2++; if (c1 != c2) return ((c1 > c2) << 1) - 1; } return 0; } /************************************************* * Compare zero-terminated PCRE2 & 8-bit strings * *************************************************/ /* As the 8-bit string is almost always a literal, its type is specified as const char *. Arguments: str1 first string str2 second string Returns: 0, 1, or -1 */ int PRIV(strcmp_c8)(PCRE2_SPTR str1, const char *str2) { PCRE2_UCHAR c1, c2; while (*str1 != '\0' || *str2 != '\0') { c1 = *str1++; c2 = *str2++; if (c1 != c2) return ((c1 > c2) << 1) - 1; } return 0; } /************************************************* * Compare two PCRE2 strings, given a length * *************************************************/ /* Arguments: str1 first string str2 second string len the length Returns: 0, 1, or -1 */ int PRIV(strncmp)(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len) { PCRE2_UCHAR c1, c2; for (; len > 0; len--) { c1 = *str1++; c2 = *str2++; if (c1 != c2) return ((c1 > c2) << 1) - 1; } return 0; } /************************************************* * Compare PCRE2 string to 8-bit string by length * *************************************************/ /* As the 8-bit string is almost always a literal, its type is specified as const char *. Arguments: str1 first string str2 second string len the length Returns: 0, 1, or -1 */ int PRIV(strncmp_c8)(PCRE2_SPTR str1, const char *str2, size_t len) { PCRE2_UCHAR c1, c2; for (; len > 0; len--) { c1 = *str1++; c2 = *str2++; if (c1 != c2) return ((c1 > c2) << 1) - 1; } return 0; } /************************************************* * Find the length of a PCRE2 string * *************************************************/ /* Argument: the string Returns: the length */ PCRE2_SIZE PRIV(strlen)(PCRE2_SPTR str) { PCRE2_SIZE c = 0; while (*str++ != 0) c++; return c; } /************************************************* * Copy 8-bit 0-terminated string to PCRE2 string * *************************************************/ /* Arguments: str1 buffer to receive the string str2 8-bit string to be copied Returns: the number of code units used (excluding trailing zero) */ PCRE2_SIZE PRIV(strcpy_c8)(PCRE2_UCHAR *str1, const char *str2) { PCRE2_UCHAR *t = str1; while (*str2 != 0) *t++ = *str2++; *t = 0; return t - str1; } /* End of pcre2_string_utils.c */ vfu-4.22/vstring/pcre2/config.h0000644000175000017500000003463714145574056014770 0ustar cadecade/* src/config.h. Generated from config.h.in by configure. */ /* src/config.h.in. Generated from configure.ac by autoheader. */ /* PCRE2 is written in Standard C, but there are a few non-standard things it can cope with, allowing it to run on SunOS4 and other "close to standard" systems. In environments that support the GNU autotools, config.h.in is converted into config.h by the "configure" script. In environments that use CMake, config-cmake.in is converted into config.h. If you are going to build PCRE2 "by hand" without using "configure" or CMake, you should copy the distributed config.h.generic to config.h, and edit the macro definitions to be the way you need them. You must then add -DHAVE_CONFIG_H to all of your compile commands, so that config.h is included at the start of every source. Alternatively, you can avoid editing by using -D on the compiler command line to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H, but if you do, default values will be taken from config.h for non-boolean macros that are not defined on the command line. Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All such macros are listed as a commented #undef in config.h.generic. Macros such as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are surrounded by #ifndef/#endif lines so that the value can be overridden by -D. PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if HAVE_BCOPY is defined. If your system has neither bcopy() nor memmove(), make sure both macros are undefined; an emulation function will then be used. */ /* By default, the \R escape sequence matches any Unicode line ending character or sequence of characters. If BSR_ANYCRLF is defined (to any value), this is changed so that backslash-R matches only CR, LF, or CRLF. The build-time default can be overridden by the user of PCRE2 at runtime. */ /* #undef BSR_ANYCRLF */ /* Define to any value to disable the use of the z and t modifiers in formatting settings such as %zu or %td (this is rarely needed). */ /* #undef DISABLE_PERCENT_ZT */ /* If you are compiling for a system that uses EBCDIC instead of ASCII character codes, define this macro to any value. When EBCDIC is set, PCRE2 assumes that all input strings are in EBCDIC. If you do not define this macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It is not possible to build a version of PCRE2 that supports both EBCDIC and UTF-8/16/32. */ /* #undef EBCDIC */ /* In an EBCDIC environment, define this macro to any value to arrange for the NL character to be 0x25 instead of the default 0x15. NL plays the role that LF does in an ASCII/Unicode environment. */ /* #undef EBCDIC_NL25 */ /* Define this if your compiler supports __attribute__((uninitialized)) */ /* #undef HAVE_ATTRIBUTE_UNINITIALIZED */ /* Define to 1 if you have the `bcopy' function. */ #define HAVE_BCOPY 1 /* Define to 1 if you have the header file. */ #define HAVE_BZLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_DIRENT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_EDITLINE_READLINE_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_EDIT_READLINE_READLINE_H */ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the `memfd_create' function. */ #define HAVE_MEMFD_CREATE 1 /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkostemp' function. */ #define HAVE_MKOSTEMP 1 /* Define if you have POSIX threads libraries and header files. */ /* #undef HAVE_PTHREAD */ /* Have PTHREAD_PRIO_INHERIT. */ /* #undef HAVE_PTHREAD_PRIO_INHERIT */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_HISTORY_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_READLINE_H */ /* Define to 1 if you have the `realpath' function. */ #define HAVE_REALPATH 1 /* Define to 1 if you have the `secure_getenv' function. */ #define HAVE_SECURE_GETENV 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_WAIT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if the compiler supports simple visibility declarations. */ #define HAVE_VISIBILITY 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_WINDOWS_H */ /* Define to 1 if you have the header file. */ #define HAVE_ZLIB_H 1 /* This limits the amount of memory that may be used while matching a pattern. It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply to JIT matching. The value is in kibibytes (units of 1024 bytes). */ #define HEAP_LIMIT 20000000 /* The value of LINK_SIZE determines the number of bytes used to store links as offsets within the compiled regex. The default is 2, which allows for compiled patterns up to 65535 code units long. This covers the vast majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes instead. This allows for longer patterns in extreme cases. */ #define LINK_SIZE 2 /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* The value of MATCH_LIMIT determines the default number of times the pcre2_match() function can record a backtrack position during a single matching attempt. The value is also used to limit a loop counter in pcre2_dfa_match(). There is a runtime interface for setting a different limit. The limit exists in order to catch runaway regular expressions that take for ever to determine that they do not match. The default is set very large so that it does not accidentally catch legitimate cases. */ #define MATCH_LIMIT 10000000 /* The above limit applies to all backtracks, whether or not they are nested. In some environments it is desirable to limit the nesting of backtracking (that is, the depth of tree that is searched) more strictly, in order to restrict the maximum amount of heap memory that is used. The value of MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it must be less than the value of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There is a runtime method for setting a different limit. In the case of pcre2_dfa_match(), this limit controls the depth of the internal nested function calls that are used for pattern recursions, lookarounds, and atomic groups. */ #define MATCH_LIMIT_DEPTH MATCH_LIMIT /* This limit is parameterized just in case anybody ever wants to change it. Care must be taken if it is increased, because it guards against integer overflow caused by enormously large patterns. */ #define MAX_NAME_COUNT 10000 /* This limit is parameterized just in case anybody ever wants to change it. Care must be taken if it is increased, because it guards against integer overflow caused by enormously large patterns. */ #define MAX_NAME_SIZE 32 /* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */ /* #undef NEVER_BACKSLASH_C */ /* The value of NEWLINE_DEFAULT determines the default newline character sequence. PCRE2 client programs can override this by selecting other values at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5 (ANYCRLF), and 6 (NUL). */ #define NEWLINE_DEFAULT 2 /* Name of package */ #define PACKAGE "pcre2" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "PCRE2" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "PCRE2 10.40-RC1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre2" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "10.40-RC1" /* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested parentheses (of any kind) in a pattern. This limits the amount of system stack that is used while compiling a pattern. */ #define PARENS_NEST_LIMIT 250 /* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by pcre2grep to hold parts of the file it is searching. The buffer will be expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing very long lines. The actual amount of memory used by pcre2grep is three times this number, because it allows for the buffering of "before" and "after" lines. */ #define PCRE2GREP_BUFSIZE 20480 /* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer used by pcre2grep to hold parts of the file it is searching. The actual amount of memory used by pcre2grep is three times this number, because it allows for the buffering of "before" and "after" lines. */ #define PCRE2GREP_MAX_BUFSIZE 1048576 /* to make a symbol visible */ #define PCRE2POSIX_EXP_DECL extern __attribute__ ((visibility ("default"))) /* to make a symbol visible */ #define PCRE2POSIX_EXP_DEFN extern __attribute__ ((visibility ("default"))) /* Define to any value to include debugging code. */ /* #undef PCRE2_DEBUG */ /* to make a symbol visible */ #define PCRE2_EXP_DECL extern __attribute__ ((visibility ("default"))) /* If you are compiling for a system other than a Unix-like system or Win32, and it needs some magic to be inserted before the definition of a function that is exported by the library, define this macro to contain the relevant magic. If you do not define this macro, a suitable __declspec value is used for Windows systems; in other environments "extern" is used for a C compiler and "extern C" for a C++ compiler. This macro apears at the start of every exported function that is part of the external API. It does not appear on functions that are "external" in the C sense, but which are internal to the library. */ #define PCRE2_EXP_DEFN __attribute__ ((visibility ("default"))) /* Define to any value if linking statically (TODO: make nice with Libtool) */ /* #undef PCRE2_STATIC */ /* Define to necessary symbol if this constant uses a non-standard name on your system. */ /* #undef PTHREAD_CREATE_JOINABLE */ /* Define to any non-zero number to enable support for SELinux compatible executable memory allocator in JIT. Note that this will have no effect unless SUPPORT_JIT is also defined. */ /* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to any value to enable support for Just-In-Time compiling. */ /* #undef SUPPORT_JIT */ /* Define to any value to allow pcre2grep to be linked with libbz2, so that it is able to handle .bz2 files. */ /* #undef SUPPORT_LIBBZ2 */ /* Define to any value to allow pcre2test to be linked with libedit. */ /* #undef SUPPORT_LIBEDIT */ /* Define to any value to allow pcre2test to be linked with libreadline. */ /* #undef SUPPORT_LIBREADLINE */ /* Define to any value to allow pcre2grep to be linked with libz, so that it is able to handle .gz files. */ /* #undef SUPPORT_LIBZ */ /* Define to any value to enable callout script support in pcre2grep. */ #define SUPPORT_PCRE2GREP_CALLOUT /**/ /* Define to any value to enable fork support in pcre2grep callout scripts. This will have no effect unless SUPPORT_PCRE2GREP_CALLOUT is also defined. */ #define SUPPORT_PCRE2GREP_CALLOUT_FORK /**/ /* Define to any value to enable JIT support in pcre2grep. Note that this will have no effect unless SUPPORT_JIT is also defined. */ /* #undef SUPPORT_PCRE2GREP_JIT */ /* Define to any value to enable the 16 bit PCRE2 library. */ /* #undef SUPPORT_PCRE2_16 */ /* Define to any value to enable the 32 bit PCRE2 library. */ /* #undef SUPPORT_PCRE2_32 */ /* Define to any value to enable the 8 bit PCRE2 library. */ #define SUPPORT_PCRE2_8 /**/ /* Define to any value to enable support for Unicode and UTF encoding. This will work even in an EBCDIC environment, but it is incompatible with the EBCDIC macro. That is, PCRE2 can support *either* EBCDIC code *or* ASCII/Unicode, but not both at once. */ #define SUPPORT_UNICODE /**/ /* Define to any value for valgrind support to find invalid memory reads. */ /* #undef SUPPORT_VALGRIND */ /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # define _ALL_SOURCE 1 #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # define _POSIX_PTHREAD_SEMANTICS 1 #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # define _TANDEM_SOURCE 1 #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 #endif /* Version number of package */ #define VERSION "10.40-RC1" /* Define to 1 if on MINIX. */ /* #undef _MINIX */ /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ /* #undef _POSIX_1_SOURCE */ /* Define to 1 if you need to in order for `stat' and other things to work. */ /* #undef _POSIX_SOURCE */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ /* #undef int64_t */ /* Define to `unsigned int' if does not define. */ /* #undef size_t */ #define PCRE2_CODE_UNIT_WIDTH 8 vfu-4.22/vstring/pcre2/COPYING0000644000175000017500000000014114145574056014364 0ustar cadecadePCRE2 LICENCE Please see the file LICENCE in the PCRE2 distribution for licensing details. End vfu-4.22/vstring/pcre2/pcre2_match.c0000644000175000017500000066026714145574056015711 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2015-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* These defines enable debugging code */ /* #define DEBUG_FRAMES_DISPLAY */ /* #define DEBUG_SHOW_OPS */ /* #define DEBUG_SHOW_RMATCH */ #ifdef DEBUG_FRAME_DISPLAY #include #endif /* These defines identify the name of the block containing "static" information, and fields within it. */ #define NLBLOCK mb /* Block containing newline information */ #define PSSTART start_subject /* Field containing processed string start */ #define PSEND end_subject /* Field containing processed string end */ #include "pcre2_internal.h" #define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */ /* Masks for identifying the public options that are permitted at match time. */ #define PUBLIC_MATCH_OPTIONS \ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT|PCRE2_COPY_MATCHED_SUBJECT) #define PUBLIC_JIT_MATCH_OPTIONS \ (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\ PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD|\ PCRE2_COPY_MATCHED_SUBJECT) /* Non-error returns from and within the match() function. Error returns are externally defined PCRE2_ERROR_xxx codes, which are all negative. */ #define MATCH_MATCH 1 #define MATCH_NOMATCH 0 /* Special internal returns used in the match() function. Make them sufficiently negative to avoid the external error codes. */ #define MATCH_ACCEPT (-999) #define MATCH_KETRPOS (-998) /* The next 5 must be kept together and in sequence so that a test that checks for any one of them can use a range. */ #define MATCH_COMMIT (-997) #define MATCH_PRUNE (-996) #define MATCH_SKIP (-995) #define MATCH_SKIP_ARG (-994) #define MATCH_THEN (-993) #define MATCH_BACKTRACK_MAX MATCH_THEN #define MATCH_BACKTRACK_MIN MATCH_COMMIT /* Group frame type values. Zero means the frame is not a group frame. The lower 16 bits are used for data (e.g. the capture number). Group frames are used for most groups so that information about the start is easily available at the end without having to scan back through intermediate frames (backtrack points). */ #define GF_CAPTURE 0x00010000u #define GF_NOCAPTURE 0x00020000u #define GF_CONDASSERT 0x00030000u #define GF_RECURSE 0x00040000u /* Masks for the identity and data parts of the group frame type. */ #define GF_IDMASK(a) ((a) & 0xffff0000u) #define GF_DATAMASK(a) ((a) & 0x0000ffffu) /* Repetition types */ enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS }; /* Min and max values for the common repeats; a maximum of UINT32_MAX => infinity. */ static const uint32_t rep_min[] = { 0, 0, /* * and *? */ 1, 1, /* + and +? */ 0, 0, /* ? and ?? */ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ static const uint32_t rep_max[] = { UINT32_MAX, UINT32_MAX, /* * and *? */ UINT32_MAX, UINT32_MAX, /* + and +? */ 1, 1, /* ? and ?? */ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ /* Repetition types - must include OP_CRPOSRANGE (not needed above) */ static const uint32_t rep_typ[] = { REPTYPE_MAX, REPTYPE_MIN, /* * and *? */ REPTYPE_MAX, REPTYPE_MIN, /* + and +? */ REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */ REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */ REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */ REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */ /* Numbers for RMATCH calls at backtracking points. When these lists are changed, the code at RETURN_SWITCH below must be updated in sync. */ enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, RM31, RM32, RM33, RM34, RM35, RM36 }; #ifdef SUPPORT_WIDE_CHARS enum { RM100=100, RM101 }; #endif #ifdef SUPPORT_UNICODE enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207, RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215, RM216, RM217, RM218, RM219, RM220, RM221, RM222 }; #endif /* Define short names for general fields in the current backtrack frame, which is always pointed to by the F variable. Occasional references to fields in other frames are written out explicitly. There are also some fields in the current frame whose names start with "temp" that are used for short-term, localised backtracking memory. These are #defined with Lxxx names at the point of use and undefined afterwards. */ #define Fback_frame F->back_frame #define Fcapture_last F->capture_last #define Fcurrent_recurse F->current_recurse #define Fecode F->ecode #define Feptr F->eptr #define Fgroup_frame_type F->group_frame_type #define Flast_group_offset F->last_group_offset #define Flength F->length #define Fmark F->mark #define Frdepth F->rdepth #define Fstart_match F->start_match #define Foffset_top F->offset_top #define Foccu F->occu #define Fop F->op #define Fovector F->ovector #define Freturn_id F->return_id #ifdef DEBUG_FRAMES_DISPLAY /************************************************* * Display current frames and contents * *************************************************/ /* This debugging function displays the current set of frames and their contents. It is not called automatically from anywhere, the intention being that calls can be inserted where necessary when debugging frame-related problems. Arguments: f the file to write to F the current top frame P a previous frame of interest frame_size the frame size mb points to the match block s identification text Returns: nothing */ static void display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size, match_block *mb, const char *s, ...) { uint32_t i; heapframe *Q; va_list ap; va_start(ap, s); fprintf(f, "FRAMES "); vfprintf(f, s, ap); va_end(ap); if (P != NULL) fprintf(f, " P=%lu", ((char *)P - (char *)(mb->match_frames))/frame_size); fprintf(f, "\n"); for (i = 0, Q = mb->match_frames; Q <= F; i++, Q = (heapframe *)((char *)Q + frame_size)) { fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d", i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode), Q->back_frame, Q->return_id); if (Q->last_group_offset == PCRE2_UNSET) fprintf(f, " lgoffset=unset\n"); else fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size); } } #endif /************************************************* * Process a callout * *************************************************/ /* This function is called for all callouts, whether "standalone" or at the start of a conditional group. Feptr will be pointing to either OP_CALLOUT or OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized with fixed values. Arguments: F points to the current backtracking frame mb points to the match block lengthptr where to return the length of the callout item Returns: the return from the callout or 0 if no callout function exists */ static int do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr) { int rc; PCRE2_SIZE save0, save1; PCRE2_SIZE *callout_ovector; pcre2_callout_block *cb; *lengthptr = (*Fecode == OP_CALLOUT)? PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE); if (mb->callout == NULL) return 0; /* No callout function provided */ /* The original matching code (pre 10.30) worked directly with the ovector passed by the user, and this was passed to callouts. Now that the working ovector is in the backtracking frame, it no longer needs to reserve space for the overall match offsets (which would waste space in the frame). For backward compatibility, however, we pass capture_top and offset_vector to the callout as if for the extended ovector, and we ensure that the first two slots are unset by preserving and restoring their current contents. Picky compilers complain if references such as Fovector[-2] are use directly, so we set up a separate pointer. */ callout_ovector = (PCRE2_SIZE *)(Fovector) - 2; /* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields are set externally. The first 3 never change; the last is updated for each bumpalong. */ cb = mb->cb; cb->capture_top = (uint32_t)Foffset_top/2 + 1; cb->capture_last = Fcapture_last; cb->offset_vector = callout_ovector; cb->mark = mb->nomatch_mark; cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject); cb->pattern_position = GET(Fecode, 1); cb->next_item_length = GET(Fecode, 1 + LINK_SIZE); if (*Fecode == OP_CALLOUT) /* Numerical callout */ { cb->callout_number = Fecode[1 + 2*LINK_SIZE]; cb->callout_string_offset = 0; cb->callout_string = NULL; cb->callout_string_length = 0; } else /* String callout */ { cb->callout_number = 0; cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE); cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1; cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2; } save0 = callout_ovector[0]; save1 = callout_ovector[1]; callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET; rc = mb->callout(cb, mb->callout_data); callout_ovector[0] = save0; callout_ovector[1] = save1; cb->callout_flags = 0; return rc; } /************************************************* * Match a back-reference * *************************************************/ /* This function is called only when it is known that the offset lies within the offsets that have so far been used in the match. Note that in caseless UTF-8 mode, the number of subject bytes matched may be different to the number of reference bytes. (In theory this could also happen in UTF-16 mode, but it seems unlikely.) Arguments: offset index into the offset vector caseless TRUE if caseless F the current backtracking frame pointer mb points to match block lengthptr pointer for returning the length matched Returns: = 0 sucessful match; number of code units matched is set < 0 no match > 0 partial match */ static int match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr) { PCRE2_SPTR p; PCRE2_SIZE length; PCRE2_SPTR eptr; PCRE2_SPTR eptr_start; /* Deal with an unset group. The default is no match, but there is an option to match an empty string. */ if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET) { if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) { *lengthptr = 0; return 0; /* Match */ } else return -1; /* No match */ } /* Separate the caseless and UTF cases for speed. */ eptr = eptr_start = Feptr; p = mb->start_subject + Fovector[offset]; length = Fovector[offset+1] - Fovector[offset]; if (caseless) { #if defined SUPPORT_UNICODE BOOL utf = (mb->poptions & PCRE2_UTF) != 0; if (utf || (mb->poptions & PCRE2_UCP) != 0) { PCRE2_SPTR endptr = p + length; /* Match characters up to the end of the reference. NOTE: the number of code units matched may differ, because in UTF-8 there are some characters whose upper and lower case codes have different numbers of bytes. For example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3 bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a sequence of two of the latter. It is important, therefore, to check the length along the reference, not along the subject (earlier code did this wrong). UCP without uses Unicode properties but without UTF encoding. */ while (p < endptr) { uint32_t c, d; const ucd_record *ur; if (eptr >= mb->end_subject) return 1; /* Partial match */ if (utf) { GETCHARINC(c, eptr); GETCHARINC(d, p); } else { c = *eptr++; d = *p++; } ur = GET_UCD(d); if (c != d && c != (uint32_t)((int)d + ur->other_case)) { const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset; for (;;) { if (c < *pp) return -1; /* No match */ if (c == *pp++) break; } } } } else #endif /* Not in UTF or UCP mode */ { for (; length > 0; length--) { uint32_t cc, cp; if (eptr >= mb->end_subject) return 1; /* Partial match */ cc = UCHAR21TEST(eptr); cp = UCHAR21TEST(p); if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc)) return -1; /* No match */ p++; eptr++; } } } /* In the caseful case, we can just compare the code units, whether or not we are in UTF and/or UCP mode. When partial matching, we have to do this unit by unit. */ else { if (mb->partial != 0) { for (; length > 0; length--) { if (eptr >= mb->end_subject) return 1; /* Partial match */ if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */ } } /* Not partial matching */ else { if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */ if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */ eptr += length; } } *lengthptr = eptr - eptr_start; return 0; /* Match */ } /****************************************************************************** ******************************************************************************* "Recursion" in the match() function The original match() function was highly recursive, but this proved to be the source of a number of problems over the years, mostly because of the relatively small system stacks that are commonly found. As new features were added to patterns, various kludges were invented to reduce the amount of stack used, making the code hard to understand in places. A version did exist that used individual frames on the heap instead of calling match() recursively, but this ran substantially slower. The current version is a refactoring that uses a vector of frames to remember backtracking points. This runs no slower, and possibly even a bit faster than the original recursive implementation. An initial vector of size START_FRAMES_SIZE (enough for maybe 50 frames) is allocated on the system stack. If this is not big enough, the heap is used for a larger vector. ******************************************************************************* ******************************************************************************/ /************************************************* * Macros for the match() function * *************************************************/ /* These macros pack up tests that are used for partial matching several times in the code. The second one is used when we already know we are past the end of the subject. We set the "hit end" flag if the pointer is at the end of the subject and either (a) the pointer is past the earliest inspected character (i.e. something has been matched, even if not part of the actual matched string), or (b) the pattern contains a lookbehind. These are the conditions for which adding more characters may allow the current match to continue. For hard partial matching, we immediately return a partial match. Otherwise, carrying on means that a complete match on the current subject will be sought. A partial match is returned only if no complete match can be found. */ #define CHECK_PARTIAL()\ if (Feptr >= mb->end_subject) \ { \ SCHECK_PARTIAL(); \ } #define SCHECK_PARTIAL()\ if (mb->partial != 0 && \ (Feptr > mb->start_used_ptr || mb->allowemptypartial)) \ { \ mb->hitend = TRUE; \ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \ } /* These macros are used to implement backtracking. They simulate a recursive call to the match() function by means of a local vector of frames which remember the backtracking points. */ #define RMATCH(ra,rb)\ {\ start_ecode = ra;\ Freturn_id = rb;\ goto MATCH_RECURSE;\ L_##rb:;\ } #define RRETURN(ra)\ {\ rrc = ra;\ goto RETURN_SWITCH;\ } /************************************************* * Match from current position * *************************************************/ /* This function is called to run one match attempt at a single starting point in the subject. Performance note: It might be tempting to extract commonly used fields from the mb structure (e.g. end_subject) into individual variables to improve performance. Tests using gcc on a SPARC disproved this; in the first case, it made performance worse. Arguments: start_eptr starting character in subject start_ecode starting position in compiled code ovector pointer to the final output vector oveccount number of pairs in ovector top_bracket number of capturing parentheses in the pattern frame_size size of each backtracking frame mb pointer to "static" variables block Returns: MATCH_MATCH if matched ) these values are >= 0 MATCH_NOMATCH if failed to match ) negative MATCH_xxx value for PRUNE, SKIP, etc negative PCRE2_ERROR_xxx value if aborted by an error condition (e.g. stopped by repeated call or depth limit) */ static int match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, PCRE2_SIZE *ovector, uint16_t oveccount, uint16_t top_bracket, PCRE2_SIZE frame_size, match_block *mb) { /* Frame-handling variables */ heapframe *F; /* Current frame pointer */ heapframe *N = NULL; /* Temporary frame pointers */ heapframe *P = NULL; heapframe *assert_accept_frame = NULL; /* For passing back a frame with captures */ PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */ /* Local variables that do not need to be preserved over calls to RRMATCH(). */ PCRE2_SPTR bracode; /* Temp pointer to start of group */ PCRE2_SIZE offset; /* Used for group offsets */ PCRE2_SIZE length; /* Used for various length calculations */ int rrc; /* Return from functions & backtracking "recursions" */ #ifdef SUPPORT_UNICODE int proptype; /* Type of character property */ #endif uint32_t i; /* Used for local loops */ uint32_t fc; /* Character values */ uint32_t number; /* Used for group and other numbers */ uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */ uint32_t group_frame_type; /* Specifies type for new group frames */ BOOL condition; /* Used in conditional groups */ BOOL cur_is_word; /* Used in "word" tests */ BOOL prev_is_word; /* Used in "word" tests */ /* UTF and UCP flags */ #ifdef SUPPORT_UNICODE BOOL utf = (mb->poptions & PCRE2_UTF) != 0; BOOL ucp = (mb->poptions & PCRE2_UCP) != 0; #else BOOL utf = FALSE; /* Required for convenience even when no Unicode support */ #endif /* This is the length of the last part of a backtracking frame that must be copied when a new frame is created. */ frame_copy_size = frame_size - offsetof(heapframe, eptr); /* Set up the first current frame at the start of the vector, and initialize fields that are not reset for new frames. */ F = mb->match_frames; Frdepth = 0; /* "Recursion" depth */ Fcapture_last = 0; /* Number of most recent capture */ Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */ Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */ Fmark = NULL; /* Most recent mark */ Foffset_top = 0; /* End of captures within the frame */ Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */ group_frame_type = 0; /* Not a start of group frame */ goto NEW_FRAME; /* Start processing with this frame */ /* Come back here when we want to create a new frame for remembering a backtracking point. */ MATCH_RECURSE: /* Set up a new backtracking frame. If the vector is full, get a new one on the heap, doubling the size, but constrained by the heap limit. */ N = (heapframe *)((char *)F + frame_size); if (N >= mb->match_frames_top) { PCRE2_SIZE newsize = mb->frame_vector_size * 2; heapframe *new; if ((newsize / 1024) > mb->heap_limit) { PCRE2_SIZE maxsize = ((mb->heap_limit * 1024)/frame_size) * frame_size; if (mb->frame_vector_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT; newsize = maxsize; } new = mb->memctl.malloc(newsize, mb->memctl.memory_data); if (new == NULL) return PCRE2_ERROR_NOMEMORY; memcpy(new, mb->match_frames, mb->frame_vector_size); F = (heapframe *)((char *)new + ((char *)F - (char *)mb->match_frames)); N = (heapframe *)((char *)F + frame_size); if (mb->match_frames != mb->stack_frames) mb->memctl.free(mb->match_frames, mb->memctl.memory_data); mb->match_frames = new; mb->match_frames_top = (heapframe *)((char *)mb->match_frames + newsize); mb->frame_vector_size = newsize; } #ifdef DEBUG_SHOW_RMATCH fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1); if (group_frame_type != 0) { fprintf(stderr, " type=%x ", group_frame_type); switch (GF_IDMASK(group_frame_type)) { case GF_CAPTURE: fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type)); break; case GF_NOCAPTURE: fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type)); break; case GF_CONDASSERT: fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type)); break; case GF_RECURSE: fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type)); break; default: fprintf(stderr, "*** unknown ***"); break; } } fprintf(stderr, "\n"); #endif /* Copy those fields that must be copied into the new frame, increase the "recursion" depth (i.e. the new frame's index) and then make the new frame current. */ memcpy((char *)N + offsetof(heapframe, eptr), (char *)F + offsetof(heapframe, eptr), frame_copy_size); N->rdepth = Frdepth + 1; F = N; /* Carry on processing with a new frame. */ NEW_FRAME: Fgroup_frame_type = group_frame_type; Fecode = start_ecode; /* Starting code pointer */ Fback_frame = frame_size; /* Default is go back one frame */ /* If this is a special type of group frame, remember its offset for quick access at the end of the group. If this is a recursion, set a new current recursion value. */ if (group_frame_type != 0) { Flast_group_offset = (char *)F - (char *)mb->match_frames; if (GF_IDMASK(group_frame_type) == GF_RECURSE) Fcurrent_recurse = GF_DATAMASK(group_frame_type); group_frame_type = 0; } /* ========================================================================= */ /* This is the main processing loop. First check that we haven't recorded too many backtracks (search tree is too large), or that we haven't exceeded the recursive depth limit (used too many backtracking frames). If not, process the opcodes. */ if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; for (;;) { #ifdef DEBUG_SHOW_OPS fprintf(stderr, "++ op=%d\n", *Fecode); #endif Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */ switch(Fop) { /* ===================================================================== */ /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close any currently open capturing brackets. Unlike reaching the end of a group, where we know the starting frame is at the top of the chained frames, in this case we have to search back for the relevant frame in case other types of group that use chained frames have intervened. Multiple OP_CLOSEs always come innermost first, which matches the chain order. We can ignore this in a recursion, because captures are not passed out of recursions. */ case OP_CLOSE: if (Fcurrent_recurse == RECURSE_UNSET) { number = GET2(Fecode, 1); offset = Flast_group_offset; for(;;) { if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; N = (heapframe *)((char *)mb->match_frames + offset); P = (heapframe *)((char *)N - frame_size); if (N->group_frame_type == (GF_CAPTURE | number)) break; offset = P->last_group_offset; } offset = (number << 1) - 2; Fcapture_last = number; Fovector[offset] = P->eptr - mb->start_subject; Fovector[offset+1] = Feptr - mb->start_subject; if (offset >= Foffset_top) Foffset_top = offset + 2; } Fecode += PRIV(OP_lengths)[*Fecode]; break; /* ===================================================================== */ /* Real or forced end of the pattern, assertion, or recursion. In an assertion ACCEPT, update the last used pointer and remember the current frame so that the captures and mark can be fished out of it. */ case OP_ASSERT_ACCEPT: if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; assert_accept_frame = F; RRETURN(MATCH_ACCEPT); /* If recursing, we have to find the most recent recursion. */ case OP_ACCEPT: case OP_END: /* Handle end of a recursion. */ if (Fcurrent_recurse != RECURSE_UNSET) { offset = Flast_group_offset; for(;;) { if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; N = (heapframe *)((char *)mb->match_frames + offset); P = (heapframe *)((char *)N - frame_size); if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break; offset = P->last_group_offset; } /* N is now the frame of the recursion; the previous frame is at the OP_RECURSE position. Go back there, copying the current subject position and mark, and the start_match position (\K might have changed it), and then move on past the OP_RECURSE. */ P->eptr = Feptr; P->mark = Fmark; P->start_match = Fstart_match; F = P; Fecode += 1 + LINK_SIZE; continue; } /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the start of the subject. In both cases, backtracking will then try other alternatives, if any. */ if (Feptr == Fstart_match && ((mb->moptions & PCRE2_NOTEMPTY) != 0 || ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 && Fstart_match == mb->start_subject + mb->start_offset))) RRETURN(MATCH_NOMATCH); /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not the end of the subject. After (*ACCEPT) we fail the entire match (at this position) but backtrack on reaching the end of the pattern. */ if (Feptr < mb->end_subject && ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0) { if (Fop == OP_END) RRETURN(MATCH_NOMATCH); return MATCH_NOMATCH; } /* We have a successful match of the whole pattern. Record the result and then do a direct return from the function. If there is space in the offset vector, set any pairs that follow the highest-numbered captured string but are less than the number of capturing groups in the pattern to PCRE2_UNSET. It is documented that this happens. "Gaps" are set to PCRE2_UNSET dynamically. It is only those at the end that need setting here. */ mb->end_match_ptr = Feptr; /* Record where we ended */ mb->end_offset_top = Foffset_top; /* and how many extracts were taken */ mb->mark = Fmark; /* and the last success mark */ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; ovector[0] = Fstart_match - mb->start_subject; ovector[1] = Feptr - mb->start_subject; /* Set i to the smaller of the sizes of the external and frame ovectors. */ i = 2 * ((top_bracket + 1 > oveccount)? oveccount : top_bracket + 1); memcpy(ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE)); while (--i >= Foffset_top + 2) ovector[i] = PCRE2_UNSET; return MATCH_MATCH; /* Note: NOT RRETURN */ /*===================================================================== */ /* Match any single character type except newline; have to take care with CRLF newlines and partial matching. */ case OP_ANY: if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); if (mb->partial != 0 && Feptr == mb->end_subject - 1 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } /* Fall through */ /* Match any single character whatsoever. */ case OP_ALLANY: if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ { /* not be updated before SCHECK_PARTIAL. */ SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr++; #ifdef SUPPORT_UNICODE if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); #endif Fecode++; break; /* ===================================================================== */ /* Match a single code unit, even in UTF mode. This opcode really does match any code unit, even newline. (It really should be called ANYCODEUNIT, of course - the byte name is from pre-16 bit days.) */ case OP_ANYBYTE: if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ { /* not be updated before SCHECK_PARTIAL. */ SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr++; Fecode++; break; /* ===================================================================== */ /* Match a single character, casefully */ case OP_CHAR: #ifdef SUPPORT_UNICODE if (utf) { Flength = 1; Fecode++; GETCHARLEN(fc, Fecode, Flength); if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr)) { CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ RRETURN(MATCH_NOMATCH); } for (; Flength > 0; Flength--) { if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH); } } else #endif /* Not UTF mode */ { if (mb->end_subject - Feptr < 1) { SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ RRETURN(MATCH_NOMATCH); } if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH); Fecode += 2; } break; /* ===================================================================== */ /* Match a single character, caselessly. If we are at the end of the subject, give up immediately. We get here only when the pattern character has at most one other case. Characters with more than two cases are coded as OP_PROP with the pseudo-property PT_CLIST. */ case OP_CHARI: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } #ifdef SUPPORT_UNICODE if (utf) { Flength = 1; Fecode++; GETCHARLEN(fc, Fecode, Flength); /* If the pattern character's value is < 128, we know that its other case (if any) is also < 128 (and therefore only one code unit long in all code-unit widths), so we can use the fast lookup table. We checked above that there is at least one character left in the subject. */ if (fc < 128) { uint32_t cc = UCHAR21(Feptr); if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); Fecode++; Feptr++; } /* Otherwise we must pick up the subject character and use Unicode property support to test its other case. Note that we cannot use the value of "Flength" to check for sufficient bytes left, because the other case of the character may have more or fewer code units. */ else { uint32_t dc; GETCHARINC(dc, Feptr); Fecode += Flength; if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH); } } /* If UCP is set without UTF we must do the same as above, but with one character per code unit. */ else if (ucp) { uint32_t cc = UCHAR21(Feptr); fc = Fecode[1]; if (fc < 128) { if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); } else { if (cc != fc && cc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH); } Feptr++; Fecode += 2; } else #endif /* SUPPORT_UNICODE */ /* Not UTF or UCP mode; use the table for characters < 256. */ { if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1]) != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH); Feptr++; Fecode += 2; } break; /* ===================================================================== */ /* Match not a single character. */ case OP_NOT: case OP_NOTI: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } #ifdef SUPPORT_UNICODE if (utf) { uint32_t ch; Fecode++; GETCHARINC(ch, Fecode); GETCHARINC(fc, Feptr); if (ch == fc) { RRETURN(MATCH_NOMATCH); /* Caseful match */ } else if (Fop == OP_NOTI) /* If caseless */ { if (ch > 127) ch = UCD_OTHERCASE(ch); else ch = (mb->fcc)[ch]; if (ch == fc) RRETURN(MATCH_NOMATCH); } } /* UCP without UTF is as above, but with one character per code unit. */ else if (ucp) { uint32_t ch; fc = UCHAR21INC(Feptr); ch = Fecode[1]; Fecode += 2; if (ch == fc) { RRETURN(MATCH_NOMATCH); /* Caseful match */ } else if (Fop == OP_NOTI) /* If caseless */ { if (ch > 127) ch = UCD_OTHERCASE(ch); else ch = (mb->fcc)[ch]; if (ch == fc) RRETURN(MATCH_NOMATCH); } } else #endif /* SUPPORT_UNICODE */ /* Neither UTF nor UCP is set */ { uint32_t ch = Fecode[1]; fc = UCHAR21INC(Feptr); if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc)) RRETURN(MATCH_NOMATCH); Fecode += 2; } break; /* ===================================================================== */ /* Match a single character repeatedly. */ #define Loclength F->temp_size #define Lstart_eptr F->temp_sptr[0] #define Lcharptr F->temp_sptr[1] #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #define Lc F->temp_32[2] #define Loc F->temp_32[3] case OP_EXACT: case OP_EXACTI: Lmin = Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_POSUPTO: case OP_POSUPTOI: reptype = REPTYPE_POS; Lmin = 0; Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_UPTO: case OP_UPTOI: reptype = REPTYPE_MAX; Lmin = 0; Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_MINUPTO: case OP_MINUPTOI: reptype = REPTYPE_MIN; Lmin = 0; Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATCHAR; case OP_POSSTAR: case OP_POSSTARI: reptype = REPTYPE_POS; Lmin = 0; Lmax = UINT32_MAX; Fecode++; goto REPEATCHAR; case OP_POSPLUS: case OP_POSPLUSI: reptype = REPTYPE_POS; Lmin = 1; Lmax = UINT32_MAX; Fecode++; goto REPEATCHAR; case OP_POSQUERY: case OP_POSQUERYI: reptype = REPTYPE_POS; Lmin = 0; Lmax = 1; Fecode++; goto REPEATCHAR; case OP_STAR: case OP_STARI: case OP_MINSTAR: case OP_MINSTARI: case OP_PLUS: case OP_PLUSI: case OP_MINPLUS: case OP_MINPLUSI: case OP_QUERY: case OP_QUERYI: case OP_MINQUERY: case OP_MINQUERYI: fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI); Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; /* Common code for all repeated single-character matches. We first check for the minimum number of characters. If the minimum equals the maximum, we are done. Otherwise, if minimizing, check the rest of the pattern for a match; if there isn't one, advance up to the maximum, one character at a time. If maximizing, advance up to the maximum number of matching characters, until Feptr is past the end of the maximum run. If possessive, we are then done (no backing up). Otherwise, match at this position; anything other than no match is immediately returned. For nomatch, back up one character, unless we are matching \R and the last thing matched was \r\n, in which case, back up two code units until we reach the first optional character position. The various UTF/non-UTF and caseful/caseless cases are handled separately, for speed. */ REPEATCHAR: #ifdef SUPPORT_UNICODE if (utf) { Flength = 1; Lcharptr = Fecode; GETCHARLEN(fc, Fecode, Flength); Fecode += Flength; /* Handle multi-code-unit character matching, caseful and caseless. */ if (Flength > 1) { uint32_t othercase; if (Fop >= OP_STARI && /* Caseless */ (othercase = UCD_OTHERCASE(fc)) != fc) Loclength = PRIV(ord2utf)(othercase, Foccu); else Loclength = 0; for (i = 1; i <= Lmin; i++) { if (Feptr <= mb->end_subject - Flength && memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; else if (Loclength > 0 && Feptr <= mb->end_subject - Loclength && memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) Feptr += Loclength; else { CHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } } if (Lmin == Lmax) continue; if (reptype == REPTYPE_MIN) { for (;;) { RMATCH(Fecode, RM202); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr <= mb->end_subject - Flength && memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; else if (Loclength > 0 && Feptr <= mb->end_subject - Loclength && memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) Feptr += Loclength; else { CHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ } else /* Maximize */ { Lstart_eptr = Feptr; for (i = Lmin; i < Lmax; i++) { if (Feptr <= mb->end_subject - Flength && memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; else if (Loclength > 0 && Feptr <= mb->end_subject - Loclength && memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) Feptr += Loclength; else { CHECK_PARTIAL(); break; } } /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ if (reptype != REPTYPE_POS) for(;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM203); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; BACKCHAR(Feptr); } } break; /* End of repeated wide character handling */ } /* Length of UTF character is 1. Put it into the preserved variable and fall through to the non-UTF code. */ Lc = fc; } else #endif /* SUPPORT_UNICODE */ /* When not in UTF mode, load a single-code-unit character. Then proceed as above, using Unicode casing if either UTF or UCP is set. */ Lc = *Fecode++; /* Caseless comparison */ if (Fop >= OP_STARI) { #if PCRE2_CODE_UNIT_WIDTH == 8 #ifdef SUPPORT_UNICODE if (ucp && !utf && Lc > 127) Loc = UCD_OTHERCASE(Lc); else #endif /* SUPPORT_UNICODE */ /* Lc will be < 128 in UTF-8 mode. */ Loc = mb->fcc[Lc]; #else /* 16-bit & 32-bit */ #ifdef SUPPORT_UNICODE if ((utf || ucp) && Lc > 127) Loc = UCD_OTHERCASE(Lc); else #endif /* SUPPORT_UNICODE */ Loc = TABLE_GET(Lc, mb->fcc, Lc); #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ for (i = 1; i <= Lmin; i++) { uint32_t cc; /* Faster than PCRE2_UCHAR */ if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21TEST(Feptr); if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); Feptr++; } if (Lmin == Lmax) continue; if (reptype == REPTYPE_MIN) { for (;;) { uint32_t cc; /* Faster than PCRE2_UCHAR */ RMATCH(Fecode, RM25); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21TEST(Feptr); if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); Feptr++; } /* Control never gets here */ } else /* Maximize */ { Lstart_eptr = Feptr; for (i = Lmin; i < Lmax; i++) { uint32_t cc; /* Faster than PCRE2_UCHAR */ if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } cc = UCHAR21TEST(Feptr); if (Lc != cc && Loc != cc) break; Feptr++; } if (reptype != REPTYPE_POS) for (;;) { if (Feptr == Lstart_eptr) break; RMATCH(Fecode, RM26); Feptr--; if (rrc != MATCH_NOMATCH) RRETURN(rrc); } } } /* Caseful comparisons (includes all multi-byte characters) */ else { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); } if (Lmin == Lmax) continue; if (reptype == REPTYPE_MIN) { for (;;) { RMATCH(Fecode, RM27); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ } else /* Maximize */ { Lstart_eptr = Feptr; for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (Lc != UCHAR21TEST(Feptr)) break; Feptr++; } if (reptype != REPTYPE_POS) for (;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM28); Feptr--; if (rrc != MATCH_NOMATCH) RRETURN(rrc); } } } break; #undef Loclength #undef Lstart_eptr #undef Lcharptr #undef Lmin #undef Lmax #undef Lc #undef Loc /* ===================================================================== */ /* Match a negated single one-byte character repeatedly. This is almost a repeat of the code for a repeated single character, but I haven't found a nice way of commoning these up that doesn't require a test of the positive/negative option for each character match. Maybe that wouldn't add very much to the time taken, but character matching *is* what this is all about... */ #define Lstart_eptr F->temp_sptr[0] #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #define Lc F->temp_32[2] #define Loc F->temp_32[3] case OP_NOTEXACT: case OP_NOTEXACTI: Lmin = Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTUPTO: case OP_NOTUPTOI: Lmin = 0; Lmax = GET2(Fecode, 1); reptype = REPTYPE_MAX; Fecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTMINUPTO: case OP_NOTMINUPTOI: Lmin = 0; Lmax = GET2(Fecode, 1); reptype = REPTYPE_MIN; Fecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTPOSSTAR: case OP_NOTPOSSTARI: reptype = REPTYPE_POS; Lmin = 0; Lmax = UINT32_MAX; Fecode++; goto REPEATNOTCHAR; case OP_NOTPOSPLUS: case OP_NOTPOSPLUSI: reptype = REPTYPE_POS; Lmin = 1; Lmax = UINT32_MAX; Fecode++; goto REPEATNOTCHAR; case OP_NOTPOSQUERY: case OP_NOTPOSQUERYI: reptype = REPTYPE_POS; Lmin = 0; Lmax = 1; Fecode++; goto REPEATNOTCHAR; case OP_NOTPOSUPTO: case OP_NOTPOSUPTOI: reptype = REPTYPE_POS; Lmin = 0; Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATNOTCHAR; case OP_NOTSTAR: case OP_NOTSTARI: case OP_NOTMINSTAR: case OP_NOTMINSTARI: case OP_NOTPLUS: case OP_NOTPLUSI: case OP_NOTMINPLUS: case OP_NOTMINPLUSI: case OP_NOTQUERY: case OP_NOTQUERYI: case OP_NOTMINQUERY: case OP_NOTMINQUERYI: fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; /* Common code for all repeated single-character non-matches. */ REPEATNOTCHAR: GETCHARINCTEST(Lc, Fecode); /* The code is duplicated for the caseless and caseful cases, for speed, since matching characters is likely to be quite common. First, ensure the minimum number of matches are present. If Lmin = Lmax, we are done. Otherwise, if minimizing, keep trying the rest of the expression and advancing one matching character if failing, up to the maximum. Alternatively, if maximizing, find the maximum number of characters and work backwards. */ if (Fop >= OP_NOTSTARI) /* Caseless */ { #ifdef SUPPORT_UNICODE if ((utf || ucp) && Lc > 127) Loc = UCD_OTHERCASE(Lc); else #endif /* SUPPORT_UNICODE */ Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */ #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(d, Feptr); if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); } } else #endif /* SUPPORT_UNICODE */ /* Not UTF mode */ { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); Feptr++; } } if (Lmin == Lmax) continue; /* Finished for exact count */ if (reptype == REPTYPE_MIN) { #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (;;) { RMATCH(Fecode, RM204); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(d, Feptr); if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); } } else #endif /*SUPPORT_UNICODE */ /* Not UTF mode */ { for (;;) { RMATCH(Fecode, RM29); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); Feptr++; } } /* Control never gets here */ } /* Maximize case */ else { Lstart_eptr = Feptr; #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(d, Feptr, len); if (Lc == d || Loc == d) break; Feptr += len; } /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ if (reptype != REPTYPE_POS) for(;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM205); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; BACKCHAR(Feptr); } } else #endif /* SUPPORT_UNICODE */ /* Not UTF mode */ { for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (Lc == *Feptr || Loc == *Feptr) break; Feptr++; } if (reptype != REPTYPE_POS) for (;;) { if (Feptr == Lstart_eptr) break; RMATCH(Fecode, RM30); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; } } } } /* Caseful comparisons */ else { #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(d, Feptr); if (Lc == d) RRETURN(MATCH_NOMATCH); } } else #endif /* Not UTF mode */ { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); } } if (Lmin == Lmax) continue; if (reptype == REPTYPE_MIN) { #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (;;) { RMATCH(Fecode, RM206); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(d, Feptr); if (Lc == d) RRETURN(MATCH_NOMATCH); } } else #endif /* Not UTF mode */ { for (;;) { RMATCH(Fecode, RM31); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ } /* Maximize case */ else { Lstart_eptr = Feptr; #ifdef SUPPORT_UNICODE if (utf) { uint32_t d; for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(d, Feptr, len); if (Lc == d) break; Feptr += len; } /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ if (reptype != REPTYPE_POS) for(;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM207); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; BACKCHAR(Feptr); } } else #endif /* Not UTF mode */ { for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (Lc == *Feptr) break; Feptr++; } if (reptype != REPTYPE_POS) for (;;) { if (Feptr == Lstart_eptr) break; RMATCH(Fecode, RM32); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; } } } } break; #undef Lstart_eptr #undef Lmin #undef Lmax #undef Lc #undef Loc /* ===================================================================== */ /* Match a bit-mapped character class, possibly repeatedly. These opcodes are used when all the characters in the class have values in the range 0-255, and either the matching is caseful, or the characters are in the range 0-127 when UTF processing is enabled. The only difference between OP_CLASS and OP_NCLASS occurs when a data character outside the range is encountered. */ #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #define Lstart_eptr F->temp_sptr[0] #define Lbyte_map_address F->temp_sptr[1] #define Lbyte_map ((unsigned char *)Lbyte_map_address) case OP_NCLASS: case OP_CLASS: { Lbyte_map_address = Fecode + 1; /* Save for matching */ Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ /* Look past the end of the item to see if there is repeat information following. Then obey similar code to character type repeats. */ switch (*Fecode) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSPLUS: case OP_CRPOSQUERY: fc = *Fecode++ - OP_CRSTAR; Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: Lmin = GET2(Fecode, 1); Lmax = GET2(Fecode, 1 + IMM2_SIZE); if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ reptype = rep_typ[*Fecode - OP_CRSTAR]; Fecode += 1 + 2 * IMM2_SIZE; break; default: /* No repeat follows */ Lmin = Lmax = 1; break; } /* First, ensure the minimum number of matches are present. */ #ifdef SUPPORT_UNICODE if (utf) { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); if (fc > 255) { if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); } else if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); } } else #endif /* Not UTF mode */ { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } fc = *Feptr++; #if PCRE2_CODE_UNIT_WIDTH != 8 if (fc > 255) { if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); } else #endif if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); } } /* If Lmax == Lmin we are done. Continue with main loop. */ if (Lmin == Lmax) continue; /* If minimizing, keep testing the rest of the expression and advancing the pointer while it matches the class. */ if (reptype == REPTYPE_MIN) { #ifdef SUPPORT_UNICODE if (utf) { for (;;) { RMATCH(Fecode, RM200); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); if (fc > 255) { if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); } else if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); } } else #endif /* Not UTF mode */ { for (;;) { RMATCH(Fecode, RM23); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } fc = *Feptr++; #if PCRE2_CODE_UNIT_WIDTH != 8 if (fc > 255) { if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); } else #endif if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ } /* If maximizing, find the longest possible run, then work backwards. */ else { Lstart_eptr = Feptr; #ifdef SUPPORT_UNICODE if (utf) { for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc > 255) { if (Fop == OP_CLASS) break; } else if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break; Feptr += len; } if (reptype == REPTYPE_POS) continue; /* No backtracking */ /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ for (;;) { RMATCH(Fecode, RM201); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ BACKCHAR(Feptr); } } else #endif /* Not UTF mode */ { for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } fc = *Feptr; #if PCRE2_CODE_UNIT_WIDTH != 8 if (fc > 255) { if (Fop == OP_CLASS) break; } else #endif if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break; Feptr++; } if (reptype == REPTYPE_POS) continue; /* No backtracking */ while (Feptr >= Lstart_eptr) { RMATCH(Fecode, RM24); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; } } RRETURN(MATCH_NOMATCH); } } /* Control never gets here */ #undef Lbyte_map_address #undef Lbyte_map #undef Lstart_eptr #undef Lmin #undef Lmax /* ===================================================================== */ /* Match an extended character class. In the 8-bit library, this opcode is encountered only when UTF-8 mode mode is supported. In the 16-bit and 32-bit libraries, codepoints greater than 255 may be encountered even when UTF is not supported. */ #define Lstart_eptr F->temp_sptr[0] #define Lxclass_data F->temp_sptr[1] #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: { Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */ Fecode += GET(Fecode, 1); /* Advance past the item */ switch (*Fecode) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRQUERY: case OP_CRMINQUERY: case OP_CRPOSSTAR: case OP_CRPOSPLUS: case OP_CRPOSQUERY: fc = *Fecode++ - OP_CRSTAR; Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; break; case OP_CRRANGE: case OP_CRMINRANGE: case OP_CRPOSRANGE: Lmin = GET2(Fecode, 1); Lmax = GET2(Fecode, 1 + IMM2_SIZE); if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ reptype = rep_typ[*Fecode - OP_CRSTAR]; Fecode += 1 + 2 * IMM2_SIZE; break; default: /* No repeat follows */ Lmin = Lmax = 1; break; } /* First, ensure the minimum number of matches are present. */ for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); } /* If Lmax == Lmin we can just continue with the main loop. */ if (Lmin == Lmax) continue; /* If minimizing, keep testing the rest of the expression and advancing the pointer while it matches the class. */ if (reptype == REPTYPE_MIN) { for (;;) { RMATCH(Fecode, RM100); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ } /* If maximizing, find the longest possible run, then work backwards. */ else { Lstart_eptr = Feptr; for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } #ifdef SUPPORT_UNICODE GETCHARLENTEST(fc, Feptr, len); #else fc = *Feptr; #endif if (!PRIV(xclass)(fc, Lxclass_data, utf)) break; Feptr += len; } if (reptype == REPTYPE_POS) continue; /* No backtracking */ /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ for(;;) { RMATCH(Fecode, RM101); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ #ifdef SUPPORT_UNICODE if (utf) BACKCHAR(Feptr); #endif } RRETURN(MATCH_NOMATCH); } /* Control never gets here */ } #endif /* SUPPORT_WIDE_CHARS: end of XCLASS */ #undef Lstart_eptr #undef Lxclass_data #undef Lmin #undef Lmax /* ===================================================================== */ /* Match various character types when PCRE2_UCP is not set. These opcodes are not generated when PCRE2_UCP is set - instead appropriate property tests are compiled. */ case OP_NOT_DIGIT: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_DIGIT: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_NOT_WHITESPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_WHITESPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_NOT_WORDCHAR: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_WORDCHAR: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_ANYNL: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); } else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++; break; case CHAR_LF: break; case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } Fecode++; break; case OP_NOT_HSPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ default: break; } Fecode++; break; case OP_HSPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { HSPACE_CASES: break; /* Byte and multibyte cases */ default: RRETURN(MATCH_NOMATCH); } Fecode++; break; case OP_NOT_VSPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } Fecode++; break; case OP_VSPACE: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } Fecode++; break; #ifdef SUPPORT_UNICODE /* ===================================================================== */ /* Check the next character by Unicode property. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ case OP_PROP: case OP_NOTPROP: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); { const uint32_t *cp; const ucd_record *prop = GET_UCD(fc); switch(Fecode[1]) { case PT_ANY: if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; case PT_LAMP: if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || prop->chartype == ucp_Lt) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; case PT_GC: if ((Fecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_PC: if ((Fecode[2] != prop->chartype) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_SC: if ((Fecode[2] != prop->script) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; /* These are specials */ case PT_ALNUM: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ switch(fc) { HSPACE_CASES: VSPACE_CASES: if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; default: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; } break; case PT_WORD: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || fc == CHAR_UNDERSCORE) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; case PT_CLIST: cp = PRIV(ucd_caseless_sets) + Fecode[2]; for (;;) { if (fc < *cp) { if (Fop == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } if (fc == *cp++) { if (Fop == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } } break; case PT_UCNC: if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || fc >= 0xe000) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; /* This should never occur */ default: return PCRE2_ERROR_INTERNAL; } Fecode += 3; } break; /* ===================================================================== */ /* Match an extended Unicode sequence. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ case OP_EXTUNI: if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } else { GETCHARINCTEST(fc, Feptr); Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, NULL); } CHECK_PARTIAL(); Fecode++; break; #endif /* SUPPORT_UNICODE */ /* ===================================================================== */ /* Match a single character type repeatedly. Note that the property type does not need to be in a stack frame as it is not used within an RMATCH() loop. */ #define Lstart_eptr F->temp_sptr[0] #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #define Lctype F->temp_32[2] #define Lpropvalue F->temp_32[3] case OP_TYPEEXACT: Lmin = Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPEUPTO: case OP_TYPEMINUPTO: Lmin = 0; Lmax = GET2(Fecode, 1); reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX; Fecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPEPOSSTAR: reptype = REPTYPE_POS; Lmin = 0; Lmax = UINT32_MAX; Fecode++; goto REPEATTYPE; case OP_TYPEPOSPLUS: reptype = REPTYPE_POS; Lmin = 1; Lmax = UINT32_MAX; Fecode++; goto REPEATTYPE; case OP_TYPEPOSQUERY: reptype = REPTYPE_POS; Lmin = 0; Lmax = 1; Fecode++; goto REPEATTYPE; case OP_TYPEPOSUPTO: reptype = REPTYPE_POS; Lmin = 0; Lmax = GET2(Fecode, 1); Fecode += 1 + IMM2_SIZE; goto REPEATTYPE; case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: fc = *Fecode++ - OP_TYPESTAR; Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; /* Common code for all repeated character type matches. */ REPEATTYPE: Lctype = *Fecode++; /* Code for the character type */ #ifdef SUPPORT_UNICODE if (Lctype == OP_PROP || Lctype == OP_NOTPROP) { proptype = *Fecode++; Lpropvalue = *Fecode++; } else proptype = -1; #endif /* First, ensure the minimum number of matches are present. Use inline code for maximizing the speed, and do the type test once at the start (i.e. keep it out of the loop). The code for UTF mode is separated out for tidiness, except for Unicode property tests. */ if (Lmin > 0) { #ifdef SUPPORT_UNICODE if (proptype >= 0) /* Property tests in all modes */ { switch(proptype) { case PT_ANY: if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); } break; case PT_LAMP: for (i = 1; i <= Lmin; i++) { int chartype; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); chartype = UCD_CHARTYPE(fc); if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; case PT_GC: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; case PT_PC: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; case PT_SC: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; case PT_ALNUM: for (i = 1; i <= Lmin; i++) { int category; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { HSPACE_CASES: VSPACE_CASES: if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; default: if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; } } break; case PT_WORD: for (i = 1; i <= Lmin; i++) { int category; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N || fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; case PT_CLIST: for (i = 1; i <= Lmin; i++) { const uint32_t *cp; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); cp = PRIV(ucd_caseless_sets) + Lpropvalue; for (;;) { if (fc < *cp) { if (Lctype == OP_NOTPROP) break; RRETURN(MATCH_NOMATCH); } if (fc == *cp++) { if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; } } } break; case PT_UCNC: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || fc >= 0xe000) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } break; /* This should not occur */ default: return PCRE2_ERROR_INTERNAL; } } /* Match extended Unicode sequences. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ else if (Lctype == OP_EXTUNI) { for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } else { GETCHARINCTEST(fc, Feptr); Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, NULL); } CHECK_PARTIAL(); } } else #endif /* SUPPORT_UNICODE */ /* Handle all other cases in UTF mode */ #ifdef SUPPORT_UNICODE if (utf) switch(Lctype) { case OP_ANY: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); if (mb->partial != 0 && Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } break; case OP_ALLANY: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } break; case OP_ANYBYTE: if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH); Feptr += Lmin; break; case OP_ANYNL: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); switch(fc) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; break; case CHAR_LF: break; case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } } break; case OP_NOT_HSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); switch(fc) { HSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } } break; case OP_HSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); switch(fc) { HSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } } break; case OP_NOT_VSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); switch(fc) { VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } } break; case OP_VSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); switch(fc) { VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } } break; case OP_NOT_DIGIT: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINC(fc, Feptr); if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); } break; case OP_DIGIT: for (i = 1; i <= Lmin; i++) { uint32_t cc; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21(Feptr); if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); Feptr++; /* No need to skip more code units - we know it has only one. */ } break; case OP_NOT_WHITESPACE: for (i = 1; i <= Lmin; i++) { uint32_t cc; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21(Feptr); if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } break; case OP_WHITESPACE: for (i = 1; i <= Lmin; i++) { uint32_t cc; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21(Feptr); if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); Feptr++; /* No need to skip more code units - we know it has only one. */ } break; case OP_NOT_WORDCHAR: for (i = 1; i <= Lmin; i++) { uint32_t cc; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21(Feptr); if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } break; case OP_WORDCHAR: for (i = 1; i <= Lmin; i++) { uint32_t cc; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } cc = UCHAR21(Feptr); if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); Feptr++; /* No need to skip more code units - we know it has only one. */ } break; default: return PCRE2_ERROR_INTERNAL; } /* End switch(Lctype) */ else #endif /* SUPPORT_UNICODE */ /* Code for the non-UTF case for minimum matching of operators other than OP_PROP and OP_NOTPROP. */ switch(Lctype) { case OP_ANY: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); if (mb->partial != 0 && Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && *Feptr == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Feptr++; } break; case OP_ALLANY: if (Feptr > mb->end_subject - Lmin) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr += Lmin; break; /* This OP_ANYBYTE case will never be reached because \C gets turned into OP_ALLANY in non-UTF mode. Cut out the code so that coverage reports don't complain about it's never being used. */ /* case OP_ANYBYTE: * if (Feptr > mb->end_subject - Lmin) * { * SCHECK_PARTIAL(); * RRETURN(MATCH_NOMATCH); * } * Feptr += Lmin; * break; */ case OP_ANYNL: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } switch(*Feptr++) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; break; case CHAR_LF: break; case CHAR_VT: case CHAR_FF: case CHAR_NEL: #if PCRE2_CODE_UNIT_WIDTH != 8 case 0x2028: case 0x2029: #endif if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } } break; case OP_NOT_HSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } switch(*Feptr++) { default: break; HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif RRETURN(MATCH_NOMATCH); } } break; case OP_HSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } switch(*Feptr++) { default: RRETURN(MATCH_NOMATCH); HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif break; } } break; case OP_NOT_VSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } switch(*Feptr++) { VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif RRETURN(MATCH_NOMATCH); default: break; } } break; case OP_VSPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } switch(*Feptr++) { default: RRETURN(MATCH_NOMATCH); VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif break; } } break; case OP_NOT_DIGIT: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; case OP_DIGIT: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; case OP_NOT_WHITESPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; case OP_WHITESPACE: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; case OP_NOT_WORDCHAR: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; case OP_WORDCHAR: for (i = 1; i <= Lmin; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); Feptr++; } break; default: return PCRE2_ERROR_INTERNAL; } } /* If Lmin = Lmax we are done. Continue with the main loop. */ if (Lmin == Lmax) continue; /* If minimizing, we have to test the rest of the pattern before each subsequent match. */ if (reptype == REPTYPE_MIN) { #ifdef SUPPORT_UNICODE if (proptype >= 0) { switch(proptype) { case PT_ANY: for (;;) { RMATCH(Fecode, RM208); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_LAMP: for (;;) { int chartype; RMATCH(Fecode, RM209); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); chartype = UCD_CHARTYPE(fc); if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_GC: for (;;) { RMATCH(Fecode, RM210); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_PC: for (;;) { RMATCH(Fecode, RM211); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_SC: for (;;) { RMATCH(Fecode, RM212); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_ALNUM: for (;;) { int category; RMATCH(Fecode, RM213); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ for (;;) { RMATCH(Fecode, RM214); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); switch(fc) { HSPACE_CASES: VSPACE_CASES: if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; default: if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; } } /* Control never gets here */ case PT_WORD: for (;;) { int category; RMATCH(Fecode, RM215); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N || fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ case PT_CLIST: for (;;) { const uint32_t *cp; RMATCH(Fecode, RM216); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); cp = PRIV(ucd_caseless_sets) + Lpropvalue; for (;;) { if (fc < *cp) { if (Lctype == OP_NOTPROP) break; RRETURN(MATCH_NOMATCH); } if (fc == *cp++) { if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; } } } /* Control never gets here */ case PT_UCNC: for (;;) { RMATCH(Fecode, RM217); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } GETCHARINCTEST(fc, Feptr); if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || fc >= 0xe000) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } /* Control never gets here */ /* This should never occur */ default: return PCRE2_ERROR_INTERNAL; } } /* Match extended Unicode sequences. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ else if (Lctype == OP_EXTUNI) { for (;;) { RMATCH(Fecode, RM218); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } else { GETCHARINCTEST(fc, Feptr); Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, NULL); } CHECK_PARTIAL(); } } else #endif /* SUPPORT_UNICODE */ /* UTF mode for non-property testing character types. */ #ifdef SUPPORT_UNICODE if (utf) { for (;;) { RMATCH(Fecode, RM219); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); GETCHARINC(fc, Feptr); switch(Lctype) { case OP_ANY: /* This is the non-NL case */ if (mb->partial != 0 && /* Take care with CRLF partial */ Feptr >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && fc == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } break; case OP_ALLANY: case OP_ANYBYTE: break; case OP_ANYNL: switch(fc) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; break; case CHAR_LF: break; case CHAR_VT: case CHAR_FF: case CHAR_NEL: #ifndef EBCDIC case 0x2028: case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } break; case OP_NOT_HSPACE: switch(fc) { HSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } break; case OP_HSPACE: switch(fc) { HSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } break; case OP_NOT_VSPACE: switch(fc) { VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } break; case OP_VSPACE: switch(fc) { VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } break; case OP_NOT_DIGIT: if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); break; case OP_DIGIT: if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WHITESPACE: if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WHITESPACE: if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WORDCHAR: if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WORDCHAR: if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); break; default: return PCRE2_ERROR_INTERNAL; } } } else #endif /* SUPPORT_UNICODE */ /* Not UTF mode */ { for (;;) { RMATCH(Fecode, RM33); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); fc = *Feptr++; switch(Lctype) { case OP_ANY: /* This is the non-NL case */ if (mb->partial != 0 && /* Take care with CRLF partial */ Feptr >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && fc == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } break; case OP_ALLANY: case OP_ANYBYTE: break; case OP_ANYNL: switch(fc) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; break; case CHAR_LF: break; case CHAR_VT: case CHAR_FF: case CHAR_NEL: #if PCRE2_CODE_UNIT_WIDTH != 8 case 0x2028: case 0x2029: #endif if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } break; case OP_NOT_HSPACE: switch(fc) { default: break; HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif RRETURN(MATCH_NOMATCH); } break; case OP_HSPACE: switch(fc) { default: RRETURN(MATCH_NOMATCH); HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif break; } break; case OP_NOT_VSPACE: switch(fc) { default: break; VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif RRETURN(MATCH_NOMATCH); } break; case OP_VSPACE: switch(fc) { default: RRETURN(MATCH_NOMATCH); VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif break; } break; case OP_NOT_DIGIT: if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); break; case OP_DIGIT: if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WHITESPACE: if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WHITESPACE: if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); break; case OP_NOT_WORDCHAR: if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); break; case OP_WORDCHAR: if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); break; default: return PCRE2_ERROR_INTERNAL; } } } /* Control never gets here */ } /* If maximizing, it is worth using inline code for speed, doing the type test once at the start (i.e. keep it out of the loop). */ else { Lstart_eptr = Feptr; /* Remember where we started */ #ifdef SUPPORT_UNICODE if (proptype >= 0) { switch(proptype) { case PT_ANY: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); if (Lctype == OP_NOTPROP) break; Feptr+= len; } break; case PT_LAMP: for (i = Lmin; i < Lmax; i++) { int chartype; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); chartype = UCD_CHARTYPE(fc); if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; case PT_GC: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; case PT_PC: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; case PT_SC: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; case PT_ALNUM: for (i = Lmin; i < Lmax; i++) { int category; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE was changed at release 8.34. */ case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); switch(fc) { HSPACE_CASES: VSPACE_CASES: if (Lctype == OP_NOTPROP) goto ENDLOOP99; /* Break the loop */ break; default: if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) goto ENDLOOP99; /* Break the loop */ break; } Feptr+= len; } ENDLOOP99: break; case PT_WORD: for (i = Lmin; i < Lmax; i++) { int category; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); category = UCD_CATEGORY(fc); if ((category == ucp_L || category == ucp_N || fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) break; Feptr+= len; } break; case PT_CLIST: for (i = Lmin; i < Lmax; i++) { const uint32_t *cp; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); cp = PRIV(ucd_caseless_sets) + Lpropvalue; for (;;) { if (fc < *cp) { if (Lctype == OP_NOTPROP) break; else goto GOT_MAX; } if (fc == *cp++) { if (Lctype == OP_NOTPROP) goto GOT_MAX; else break; } } Feptr += len; } GOT_MAX: break; case PT_UCNC: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLENTEST(fc, Feptr, len); if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || fc >= 0xe000) == (Lctype == OP_NOTPROP)) break; Feptr += len; } break; default: return PCRE2_ERROR_INTERNAL; } /* Feptr is now past the end of the maximum run */ if (reptype == REPTYPE_POS) continue; /* No backtracking */ /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ for(;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM222); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; if (utf) BACKCHAR(Feptr); } } /* Match extended Unicode grapheme clusters. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ else if (Lctype == OP_EXTUNI) { for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } else { GETCHARINCTEST(fc, Feptr); Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, NULL); } CHECK_PARTIAL(); } /* Feptr is now past the end of the maximum run */ if (reptype == REPTYPE_POS) continue; /* No backtracking */ /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start of the run while backtracking because the use of \C in UTF mode can cause BACKCHAR to move back past Lstart_eptr. This is just palliative; the use of \C in UTF mode is fraught with danger. */ for(;;) { int lgb, rgb; PCRE2_SPTR fptr; if (Feptr <= Lstart_eptr) break; /* At start of char run */ RMATCH(Fecode, RM220); if (rrc != MATCH_NOMATCH) RRETURN(rrc); /* Backtracking over an extended grapheme cluster involves inspecting the previous two characters (if present) to see if a break is permitted between them. */ Feptr--; if (!utf) fc = *Feptr; else { BACKCHAR(Feptr); GETCHAR(fc, Feptr); } rgb = UCD_GRAPHBREAK(fc); for (;;) { if (Feptr <= Lstart_eptr) break; /* At start of char run */ fptr = Feptr - 1; if (!utf) fc = *fptr; else { BACKCHAR(fptr); GETCHAR(fc, fptr); } lgb = UCD_GRAPHBREAK(fc); if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; Feptr = fptr; rgb = lgb; } } } else #endif /* SUPPORT_UNICODE */ #ifdef SUPPORT_UNICODE if (utf) { switch(Lctype) { case OP_ANY: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (IS_NEWLINE(Feptr)) break; if (mb->partial != 0 && /* Take care with CRLF partial */ Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } break; case OP_ALLANY: if (Lmax < UINT32_MAX) { for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } Feptr++; ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } } else { Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */ SCHECK_PARTIAL(); } break; /* The "byte" (i.e. "code unit") case is the same as non-UTF */ case OP_ANYBYTE: fc = Lmax - Lmin; if (fc > (uint32_t)(mb->end_subject - Feptr)) { Feptr = mb->end_subject; SCHECK_PARTIAL(); } else Feptr += fc; break; case OP_ANYNL: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc == CHAR_CR) { if (++Feptr >= mb->end_subject) break; if (UCHAR21(Feptr) == CHAR_LF) Feptr++; } else { if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL #ifndef EBCDIC && fc != 0x2028 && fc != 0x2029 #endif /* Not EBCDIC */ ))) break; Feptr += len; } } break; case OP_NOT_HSPACE: case OP_HSPACE: for (i = Lmin; i < Lmax; i++) { BOOL gotspace; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); switch(fc) { HSPACE_CASES: gotspace = TRUE; break; default: gotspace = FALSE; break; } if (gotspace == (Lctype == OP_NOT_HSPACE)) break; Feptr += len; } break; case OP_NOT_VSPACE: case OP_VSPACE: for (i = Lmin; i < Lmax; i++) { BOOL gotspace; int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); switch(fc) { VSPACE_CASES: gotspace = TRUE; break; default: gotspace = FALSE; break; } if (gotspace == (Lctype == OP_NOT_VSPACE)) break; Feptr += len; } break; case OP_NOT_DIGIT: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break; Feptr+= len; } break; case OP_DIGIT: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break; Feptr+= len; } break; case OP_NOT_WHITESPACE: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break; Feptr+= len; } break; case OP_WHITESPACE: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break; Feptr+= len; } break; case OP_NOT_WORDCHAR: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break; Feptr+= len; } break; case OP_WORDCHAR: for (i = Lmin; i < Lmax; i++) { int len = 1; if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } GETCHARLEN(fc, Feptr, len); if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break; Feptr+= len; } break; default: return PCRE2_ERROR_INTERNAL; } if (reptype == REPTYPE_POS) continue; /* No backtracking */ /* After \C in UTF mode, Lstart_eptr might be in the middle of a Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go too far. */ for(;;) { if (Feptr <= Lstart_eptr) break; RMATCH(Fecode, RM221); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; BACKCHAR(Feptr); if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR) Feptr--; } } else #endif /* SUPPORT_UNICODE */ /* Not UTF mode */ { switch(Lctype) { case OP_ANY: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (IS_NEWLINE(Feptr)) break; if (mb->partial != 0 && /* Take care with CRLF partial */ Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && *Feptr == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Feptr++; } break; case OP_ALLANY: case OP_ANYBYTE: fc = Lmax - Lmin; if (fc > (uint32_t)(mb->end_subject - Feptr)) { Feptr = mb->end_subject; SCHECK_PARTIAL(); } else Feptr += fc; break; case OP_ANYNL: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } fc = *Feptr; if (fc == CHAR_CR) { if (++Feptr >= mb->end_subject) break; if (*Feptr == CHAR_LF) Feptr++; } else { if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL #if PCRE2_CODE_UNIT_WIDTH != 8 && fc != 0x2028 && fc != 0x2029 #endif ))) break; Feptr++; } } break; case OP_NOT_HSPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } switch(*Feptr) { default: Feptr++; break; HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif goto ENDLOOP00; } } ENDLOOP00: break; case OP_HSPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } switch(*Feptr) { default: goto ENDLOOP01; HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 HSPACE_MULTIBYTE_CASES: #endif Feptr++; break; } } ENDLOOP01: break; case OP_NOT_VSPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } switch(*Feptr) { default: Feptr++; break; VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif goto ENDLOOP02; } } ENDLOOP02: break; case OP_VSPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } switch(*Feptr) { default: goto ENDLOOP03; VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 VSPACE_MULTIBYTE_CASES: #endif Feptr++; break; } } ENDLOOP03: break; case OP_NOT_DIGIT: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) break; Feptr++; } break; case OP_DIGIT: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) break; Feptr++; } break; case OP_NOT_WHITESPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) break; Feptr++; } break; case OP_WHITESPACE: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) break; Feptr++; } break; case OP_NOT_WORDCHAR: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) break; Feptr++; } break; case OP_WORDCHAR: for (i = Lmin; i < Lmax; i++) { if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) break; Feptr++; } break; default: return PCRE2_ERROR_INTERNAL; } if (reptype == REPTYPE_POS) continue; /* No backtracking */ for (;;) { if (Feptr == Lstart_eptr) break; RMATCH(Fecode, RM34); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr--; if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF && Feptr[-1] == CHAR_CR) Feptr--; } } } break; /* End of repeat character type processing */ #undef Lstart_eptr #undef Lmin #undef Lmax #undef Lctype #undef Lpropvalue /* ===================================================================== */ /* Match a back reference, possibly repeatedly. Look past the end of the item to see if there is repeat information following. The OP_REF and OP_REFI opcodes are used for a reference to a numbered group or to a non-duplicated named group. For a duplicated named group, OP_DNREF and OP_DNREFI are used. In this case we must scan the list of groups to which the name refers, and use the first one that is set. */ #define Lmin F->temp_32[0] #define Lmax F->temp_32[1] #define Lcaseless F->temp_32[2] #define Lstart F->temp_sptr[0] #define Loffset F->temp_size case OP_DNREF: case OP_DNREFI: Lcaseless = (Fop == OP_DNREFI); { int count = GET2(Fecode, 1+IMM2_SIZE); PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; Fecode += 1 + 2*IMM2_SIZE; while (count-- > 0) { Loffset = (GET2(slot, 0) << 1) - 2; if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break; slot += mb->name_entry_size; } } goto REF_REPEAT; case OP_REF: case OP_REFI: Lcaseless = (Fop == OP_REFI); Loffset = (GET2(Fecode, 1) << 1) - 2; Fecode += 1 + IMM2_SIZE; /* Set up for repetition, or handle the non-repeated case. The maximum and minimum must be in the heap frame, but as they are short-term values, we use temporary fields. */ REF_REPEAT: switch (*Fecode) { case OP_CRSTAR: case OP_CRMINSTAR: case OP_CRPLUS: case OP_CRMINPLUS: case OP_CRQUERY: case OP_CRMINQUERY: fc = *Fecode++ - OP_CRSTAR; Lmin = rep_min[fc]; Lmax = rep_max[fc]; reptype = rep_typ[fc]; break; case OP_CRRANGE: case OP_CRMINRANGE: Lmin = GET2(Fecode, 1); Lmax = GET2(Fecode, 1 + IMM2_SIZE); reptype = rep_typ[*Fecode - OP_CRSTAR]; if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ Fecode += 1 + 2 * IMM2_SIZE; break; default: /* No repeat follows */ { rrc = match_ref(Loffset, Lcaseless, F, mb, &length); if (rrc != 0) { if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ CHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } } Feptr += length; continue; /* With the main loop */ } /* Handle repeated back references. If a set group has length zero, just continue with the main loop, because it matches however many times. For an unset reference, if the minimum is zero, we can also just continue. We can also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset group behave as a zero-length group. For any other unset cases, carrying on will result in NOMATCH. */ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) { if (Fovector[Loffset] == Fovector[Loffset + 1]) continue; } else /* Group is not set */ { if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) continue; } /* First, ensure the minimum number of matches are present. */ for (i = 1; i <= Lmin; i++) { PCRE2_SIZE slength; rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); if (rrc != 0) { if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ CHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr += slength; } /* If min = max, we are done. They are not both allowed to be zero. */ if (Lmin == Lmax) continue; /* If minimizing, keep trying and advancing the pointer. */ if (reptype == REPTYPE_MIN) { for (;;) { PCRE2_SIZE slength; RMATCH(Fecode, RM20); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); if (rrc != 0) { if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ CHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } Feptr += slength; } /* Control never gets here */ } /* If maximizing, find the longest string and work backwards, as long as the matched lengths for each iteration are the same. */ else { BOOL samelengths = TRUE; Lstart = Feptr; /* Starting position */ Flength = Fovector[Loffset+1] - Fovector[Loffset]; for (i = Lmin; i < Lmax; i++) { PCRE2_SIZE slength; rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); if (rrc != 0) { /* Can't use CHECK_PARTIAL because we don't want to update Feptr in the soft partial matching case. */ if (rrc > 0 && mb->partial != 0 && mb->end_subject > mb->start_used_ptr) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } break; } if (slength != Flength) samelengths = FALSE; Feptr += slength; } /* If the length matched for each repetition is the same as the length of the captured group, we can easily work backwards. This is the normal case. However, in caseless UTF-8 mode there are pairs of case-equivalent characters whose lengths (in terms of code units) differ. However, this is very rare, so we handle it by re-matching fewer and fewer times. */ if (samelengths) { while (Feptr >= Lstart) { RMATCH(Fecode, RM21); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Feptr -= Flength; } } /* The rare case of non-matching lengths. Re-scan the repetition for each iteration. We know that match_ref() will succeed every time. */ else { Lmax = i; for (;;) { RMATCH(Fecode, RM22); if (rrc != MATCH_NOMATCH) RRETURN(rrc); if (Feptr == Lstart) break; /* Failed after minimal repetition */ Feptr = Lstart; Lmax--; for (i = Lmin; i < Lmax; i++) { PCRE2_SIZE slength; (void)match_ref(Loffset, Lcaseless, F, mb, &slength); Feptr += slength; } } } RRETURN(MATCH_NOMATCH); } /* Control never gets here */ #undef Lcaseless #undef Lmin #undef Lmax #undef Lstart #undef Loffset /* ========================================================================= */ /* Opcodes for the start of various parenthesized items */ /* ========================================================================= */ /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the (*THEN) is within the current branch by comparing the address of OP_THEN that is passed back with the end of the branch. If (*THEN) is within the current branch, and the branch is one of two or more alternatives (it either starts or ends with OP_ALT), we have reached the limit of THEN's action, so convert the return code to NOMATCH, which will cause normal backtracking to happen from now on. Otherwise, THEN is passed back to an outer alternative. This implements Perl's treatment of parenthesized groups, where a group not containing | does not affect the current alternative, that is, (X) is NOT the same as (X|(*F)). */ /* ===================================================================== */ /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive bracket group, indicating that it may occur zero times. It may repeat infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets with fixed upper repeat limits are compiled as a number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO. Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */ #define Lnext_ecode F->temp_sptr[0] case OP_BRAZERO: Lnext_ecode = Fecode + 1; RMATCH(Lnext_ecode, RM9); if (rrc != MATCH_NOMATCH) RRETURN(rrc); do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); Fecode = Lnext_ecode + 1 + LINK_SIZE; break; case OP_BRAMINZERO: Lnext_ecode = Fecode + 1; do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Fecode++; break; #undef Lnext_ecode case OP_SKIPZERO: Fecode++; do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); Fecode += 1 + LINK_SIZE; break; /* ===================================================================== */ /* Handle possessive brackets with an unlimited repeat. The end of these brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without going further in the pattern. */ #define Lframe_type F->temp_32[0] #define Lmatched_once F->temp_32[1] #define Lzero_allowed F->temp_32[2] #define Lstart_eptr F->temp_sptr[0] #define Lstart_group F->temp_sptr[1] case OP_BRAPOSZERO: Lzero_allowed = TRUE; /* Zero repeat is allowed */ Fecode += 1; if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE; goto POSSESSIVE_NON_CAPTURE; case OP_BRAPOS: case OP_SBRAPOS: Lzero_allowed = FALSE; /* Zero repeat not allowed */ POSSESSIVE_NON_CAPTURE: Lframe_type = GF_NOCAPTURE; /* Remembered frame type */ goto POSSESSIVE_GROUP; case OP_CBRAPOS: case OP_SCBRAPOS: Lzero_allowed = FALSE; /* Zero repeat not allowed */ POSSESSIVE_CAPTURE: number = GET2(Fecode, 1+LINK_SIZE); Lframe_type = GF_CAPTURE | number; /* Remembered frame type */ POSSESSIVE_GROUP: Lmatched_once = FALSE; /* Never matched */ Lstart_group = Fecode; /* Start of this group */ for (;;) { Lstart_eptr = Feptr; /* Position at group start */ group_frame_type = Lframe_type; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8); if (rrc == MATCH_KETRPOS) { Lmatched_once = TRUE; /* Matched at least once */ if (Feptr == Lstart_eptr) /* Empty match; skip to end */ { do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); break; } Fecode = Lstart_group; continue; } /* See comment above about handling THEN. */ if (rrc == MATCH_THEN) { PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); if (mb->verb_ecode_ptr < next_ecode && (*Fecode == OP_ALT || *next_ecode == OP_ALT)) rrc = MATCH_NOMATCH; } if (rrc != MATCH_NOMATCH) RRETURN(rrc); Fecode += GET(Fecode, 1); if (*Fecode != OP_ALT) break; } /* Success if matched something or zero repeat allowed */ if (Lmatched_once || Lzero_allowed) { Fecode += 1 + LINK_SIZE; break; } RRETURN(MATCH_NOMATCH); #undef Lmatched_once #undef Lzero_allowed #undef Lframe_type #undef Lstart_eptr #undef Lstart_group /* ===================================================================== */ /* Handle non-capturing brackets that cannot match an empty string. When we get to the final alternative within the brackets, as long as there are no THEN's in the pattern, we can optimize by not recording a new backtracking point. (Ideally we should test for a THEN within this group, but we don't have that information.) Don't do this if we are at the very top level, however, because that would make handling assertions and once-only brackets messier when there is nothing to go back to. */ #define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */ #define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */ case OP_BRA: if (mb->hasthen || Frdepth == 0) { Lframe_type = 0; goto GROUPLOOP; } for (;;) { Lnext_branch = Fecode + GET(Fecode, 1); if (*Lnext_branch != OP_ALT) break; /* This is never the final branch. We do not need to test for MATCH_THEN here because this code is not used when there is a THEN in the pattern. */ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Fecode = Lnext_branch; } /* Hit the start of the final branch. Continue at this level. */ Fecode += PRIV(OP_lengths)[*Fecode]; break; #undef Lnext_branch /* ===================================================================== */ /* Handle a capturing bracket, other than those that are possessive with an unlimited repeat. */ case OP_CBRA: case OP_SCBRA: Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE); goto GROUPLOOP; /* ===================================================================== */ /* Atomic groups and non-capturing brackets that can match an empty string must record a backtracking point and also set up a chained frame. */ case OP_ONCE: case OP_SCRIPT_RUN: case OP_SBRA: Lframe_type = GF_NOCAPTURE | Fop; GROUPLOOP: for (;;) { group_frame_type = Lframe_type; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2); if (rrc == MATCH_THEN) { PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); if (mb->verb_ecode_ptr < next_ecode && (*Fecode == OP_ALT || *next_ecode == OP_ALT)) rrc = MATCH_NOMATCH; } if (rrc != MATCH_NOMATCH) RRETURN(rrc); Fecode += GET(Fecode, 1); if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); } /* Control never reaches here. */ #undef Lframe_type /* ===================================================================== */ /* Recursion either matches the current regex, or some subexpression. The offset data is the offset to the starting bracket from the start of the whole pattern. (This is so that it works from duplicated subpatterns.) */ #define Lframe_type F->temp_32[0] #define Lstart_branch F->temp_sptr[0] case OP_RECURSE: bracode = mb->start_code + GET(Fecode, 1); number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE); /* If we are already in a recursion, check for repeating the same one without advancing the subject pointer. This should catch convoluted mutual recursions. (Some simple cases are caught at compile time.) */ if (Fcurrent_recurse != RECURSE_UNSET) { offset = Flast_group_offset; while (offset != PCRE2_UNSET) { N = (heapframe *)((char *)mb->match_frames + offset); P = (heapframe *)((char *)N - frame_size); if (N->group_frame_type == (GF_RECURSE | number)) { if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP; break; } offset = P->last_group_offset; } } /* Now run the recursion, branch by branch. */ Lstart_branch = bracode; Lframe_type = GF_RECURSE | number; for (;;) { PCRE2_SPTR next_ecode; group_frame_type = Lframe_type; RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11); next_ecode = Lstart_branch + GET(Lstart_branch,1); /* Handle backtracking verbs, which are defined in a range that can easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a recursion; they cause a NOMATCH for the entire recursion. When one of these verbs triggers, the current recursion group number is recorded. If it matches the recursion we are processing, the verb happened within the recursion and we must deal with it. Otherwise it must have happened after the recursion completed, and so has to be passed back. See comment above about handling THEN. */ if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX && mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE)) { if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode && (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT)) rrc = MATCH_NOMATCH; else RRETURN(MATCH_NOMATCH); } /* Note that carrying on after (*ACCEPT) in a recursion is handled in the OP_ACCEPT code. Nothing needs to be done here. */ if (rrc != MATCH_NOMATCH) RRETURN(rrc); Lstart_branch = next_ecode; if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH); } /* Control never reaches here. */ #undef Lframe_type #undef Lstart_branch /* ===================================================================== */ /* Positive assertions are like other groups except that PCRE doesn't allow the effect of (*THEN) to escape beyond an assertion; it is therefore treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its captures and mark retained. Any other return is an error. */ #define Lframe_type F->temp_32[0] case OP_ASSERT: case OP_ASSERTBACK: case OP_ASSERT_NA: case OP_ASSERTBACK_NA: Lframe_type = GF_NOCAPTURE | Fop; for (;;) { group_frame_type = Lframe_type; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3); if (rrc == MATCH_ACCEPT) { memcpy(Fovector, (char *)assert_accept_frame + offsetof(heapframe, ovector), assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); Foffset_top = assert_accept_frame->offset_top; Fmark = assert_accept_frame->mark; break; } if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); Fecode += GET(Fecode, 1); if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); } do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); Fecode += 1 + LINK_SIZE; break; #undef Lframe_type /* ===================================================================== */ /* Handle negative assertions. Loop for each non-matching branch as for positive assertions. */ #define Lframe_type F->temp_32[0] case OP_ASSERT_NOT: case OP_ASSERTBACK_NOT: Lframe_type = GF_NOCAPTURE | Fop; for (;;) { group_frame_type = Lframe_type; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4); switch(rrc) { case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */ case MATCH_MATCH: RRETURN (MATCH_NOMATCH); case MATCH_NOMATCH: /* Branch failed, try next if present. */ case MATCH_THEN: Fecode += GET(Fecode, 1); if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED; break; case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */ case MATCH_SKIP: case MATCH_PRUNE: do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); goto ASSERT_NOT_FAILED; default: /* Pass back any other return */ RRETURN(rrc); } } /* None of the branches have matched or there was a backtrack to (*COMMIT), (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a negative assertion, so carry on. */ ASSERT_NOT_FAILED: Fecode += 1 + LINK_SIZE; break; #undef Lframe_type /* ===================================================================== */ /* The callout item calls an external function, if one is provided, passing details of the match so far. This is mainly for debugging, though the function is able to force a failure. */ case OP_CALLOUT: case OP_CALLOUT_STR: rrc = do_callout(F, mb, &length); if (rrc > 0) RRETURN(MATCH_NOMATCH); if (rrc < 0) RRETURN(rrc); Fecode += length; break; /* ===================================================================== */ /* Conditional group: compilation checked that there are no more than two branches. If the condition is false, skipping the first branch takes us past the end of the item if there is only one branch, but that's exactly what we want. */ case OP_COND: case OP_SCOND: /* The variable Flength will be added to Fecode when the condition is false, to get to the second branch. Setting it to the offset to the ALT or KET, then incrementing Fecode achieves this effect. However, if the second branch is non-existent, we must point to the KET so that the end of the group is correctly processed. We now have Fecode pointing to the condition or callout. */ Flength = GET(Fecode, 1); /* Offset to the second branch */ if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE; Fecode += 1 + LINK_SIZE; /* From this opcode */ /* Because of the way auto-callout works during compile, a callout item is inserted between OP_COND and an assertion condition. Such a callout can also be inserted manually. */ if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR) { rrc = do_callout(F, mb, &length); if (rrc > 0) RRETURN(MATCH_NOMATCH); if (rrc < 0) RRETURN(rrc); /* Advance Fecode past the callout, so it now points to the condition. We must adjust Flength so that the value of Fecode+Flength is unchanged. */ Fecode += length; Flength -= length; } /* Test the various possible conditions */ condition = FALSE; switch(*Fecode) { case OP_RREF: /* Group recursion test */ if (Fcurrent_recurse != RECURSE_UNSET) { number = GET2(Fecode, 1); condition = (number == RREF_ANY || number == Fcurrent_recurse); } break; case OP_DNRREF: /* Duplicate named group recursion test */ if (Fcurrent_recurse != RECURSE_UNSET) { int count = GET2(Fecode, 1 + IMM2_SIZE); PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; while (count-- > 0) { number = GET2(slot, 0); condition = number == Fcurrent_recurse; if (condition) break; slot += mb->name_entry_size; } } break; case OP_CREF: /* Numbered group used test */ offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; break; case OP_DNCREF: /* Duplicate named group used test */ { int count = GET2(Fecode, 1 + IMM2_SIZE); PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; while (count-- > 0) { offset = (GET2(slot, 0) << 1) - 2; condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; if (condition) break; slot += mb->name_entry_size; } } break; case OP_FALSE: case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */ break; case OP_TRUE: condition = TRUE; break; /* The condition is an assertion. Run code similar to the assertion code above. */ #define Lpositive F->temp_32[0] #define Lstart_branch F->temp_sptr[0] default: Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK); Lstart_branch = Fecode; for (;;) { group_frame_type = GF_CONDASSERT | *Fecode; RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5); switch(rrc) { case MATCH_ACCEPT: /* Save captures */ memcpy(Fovector, (char *)assert_accept_frame + offsetof(heapframe, ovector), assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); Foffset_top = assert_accept_frame->offset_top; /* Fall through */ /* In the case of a match, the captures have already been put into the current frame. */ case MATCH_MATCH: condition = Lpositive; /* TRUE for positive assertion */ break; /* PCRE doesn't allow the effect of (*THEN) to escape beyond an assertion; it is therefore always treated as NOMATCH. */ case MATCH_NOMATCH: case MATCH_THEN: Lstart_branch += GET(Lstart_branch, 1); if (*Lstart_branch == OP_ALT) continue; /* Try next branch */ condition = !Lpositive; /* TRUE for negative assertion */ break; /* These force no match without checking other branches. */ case MATCH_COMMIT: case MATCH_SKIP: case MATCH_PRUNE: condition = !Lpositive; break; default: RRETURN(rrc); } break; /* Out of the branch loop */ } /* If the condition is true, find the end of the assertion so that advancing past it gets us to the start of the first branch. */ if (condition) { do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); } break; /* End of assertion condition */ } #undef Lpositive #undef Lstart_branch /* Choose branch according to the condition. */ Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength; /* If the opcode is OP_SCOND it means we are at a repeated conditional group that might match an empty string. We must therefore descend a level so that the start is remembered for checking. For OP_COND we can just continue at this level. */ if (Fop == OP_SCOND) { group_frame_type = GF_NOCAPTURE | Fop; RMATCH(Fecode, RM35); RRETURN(rrc); } break; /* ========================================================================= */ /* End of start of parenthesis opcodes */ /* ========================================================================= */ /* ===================================================================== */ /* Move the subject pointer back. This occurs only at the start of each branch of a lookbehind assertion. If we are too close to the start to move back, fail. When working with UTF-8 we move back a number of characters, not bytes. */ case OP_REVERSE: number = GET(Fecode, 1); #ifdef SUPPORT_UNICODE if (utf) { while (number-- > 0) { if (Feptr <= mb->check_subject) RRETURN(MATCH_NOMATCH); Feptr--; BACKCHAR(Feptr); } } else #endif /* No UTF-8 support, or not in UTF-8 mode: count is code unit count */ { if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH); Feptr -= number; } /* Save the earliest consulted character, then skip to next opcode */ if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr; Fecode += 1 + LINK_SIZE; break; /* ===================================================================== */ /* An alternation is the end of a branch; scan along to find the end of the bracketed group. */ case OP_ALT: do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); break; /* ===================================================================== */ /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the starting frame was added to the chained frames in order to remember the starting subject position for the group. */ case OP_KET: case OP_KETRMIN: case OP_KETRMAX: case OP_KETRPOS: bracode = Fecode - GET(Fecode, 1); /* Point N to the frame at the start of the most recent group. Remember the subject pointer at the start of the group. */ if (*bracode != OP_BRA && *bracode != OP_COND) { N = (heapframe *)((char *)mb->match_frames + Flast_group_offset); P = (heapframe *)((char *)N - frame_size); Flast_group_offset = P->last_group_offset; #ifdef DEBUG_SHOW_RMATCH fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n", N->rdepth, N->group_frame_type, (char *)P->eptr - (char *)mb->start_subject); #endif /* If we are at the end of an assertion that is a condition, return a match, discarding any intermediate backtracking points. Copy back the mark setting and the captures into the frame before N so that they are set on return. Doing this for all assertions, both positive and negative, seems to match what Perl does. */ if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT) { memcpy((char *)P + offsetof(heapframe, ovector), Fovector, Foffset_top * sizeof(PCRE2_SIZE)); P->offset_top = Foffset_top; P->mark = Fmark; Fback_frame = (char *)F - (char *)P; RRETURN(MATCH_MATCH); } } else P = NULL; /* Indicates starting frame not recorded */ /* The group was not a conditional assertion. */ switch (*bracode) { case OP_BRA: /* No need to do anything for these */ case OP_COND: case OP_SCOND: break; /* Non-atomic positive assertions are like OP_BRA, except that the subject pointer must be put back to where it was at the start of the assertion. */ case OP_ASSERT_NA: case OP_ASSERTBACK_NA: if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; Feptr = P->eptr; break; /* Atomic positive assertions are like OP_ONCE, except that in addition the subject pointer must be put back to where it was at the start of the assertion. */ case OP_ASSERT: case OP_ASSERTBACK: if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; Feptr = P->eptr; /* Fall through */ /* For an atomic group, discard internal backtracking points. We must also ensure that any remaining branches within the top-level of the group are not tried. Do this by adjusting the code pointer within the backtrack frame so that it points to the final branch. */ case OP_ONCE: Fback_frame = ((char *)F - (char *)P); for (;;) { uint32_t y = GET(P->ecode,1); if ((P->ecode)[y] != OP_ALT) break; P->ecode += y; } break; /* A matching negative assertion returns MATCH, which is turned into NOMATCH at the assertion level. */ case OP_ASSERT_NOT: case OP_ASSERTBACK_NOT: RRETURN(MATCH_MATCH); /* At the end of a script run, apply the script-checking rules. This code will never by exercised if Unicode support it not compiled, because in that environment script runs cause an error at compile time. */ case OP_SCRIPT_RUN: if (!PRIV(script_run)(P->eptr, Feptr, utf)) RRETURN(MATCH_NOMATCH); break; /* Whole-pattern recursion is coded as a recurse into group 0, so it won't be picked up here. Instead, we catch it when the OP_END is reached. Other recursion is handled here. */ case OP_CBRA: case OP_CBRAPOS: case OP_SCBRA: case OP_SCBRAPOS: number = GET2(bracode, 1+LINK_SIZE); /* Handle a recursively called group. We reinstate the previous set of captures and then carry on after the recursion call. */ if (Fcurrent_recurse == number) { P = (heapframe *)((char *)N - frame_size); memcpy((char *)F + offsetof(heapframe, ovector), P->ovector, P->offset_top * sizeof(PCRE2_SIZE)); Foffset_top = P->offset_top; Fcapture_last = P->capture_last; Fcurrent_recurse = P->current_recurse; Fecode = P->ecode + 1 + LINK_SIZE; continue; /* With next opcode */ } /* Deal with actual capturing. */ offset = (number << 1) - 2; Fcapture_last = number; Fovector[offset] = P->eptr - mb->start_subject; Fovector[offset+1] = Feptr - mb->start_subject; if (offset >= Foffset_top) Foffset_top = offset + 2; break; } /* End actions relating to the starting opcode */ /* OP_KETRPOS is a possessive repeating ket. Remember the current position, and return the MATCH_KETRPOS. This makes it possible to do the repeats one at a time from the outer level. This must precede the empty string test - in this case that test is done at the outer level. */ if (*Fecode == OP_KETRPOS) { memcpy((char *)P + offsetof(heapframe, eptr), (char *)F + offsetof(heapframe, eptr), frame_copy_size); RRETURN(MATCH_KETRPOS); } /* Handle the different kinds of closing brackets. A non-repeating ket needs no special action, just continuing at this level. This also happens for the repeating kets if the group matched no characters, in order to forcibly break infinite loops. Otherwise, the repeating kets try the rest of the pattern or restart from the preceding bracket, in the appropriate order. */ if (Fop != OP_KET && (P == NULL || Feptr != P->eptr)) { if (Fop == OP_KETRMIN) { RMATCH(Fecode + 1 + LINK_SIZE, RM6); if (rrc != MATCH_NOMATCH) RRETURN(rrc); Fecode -= GET(Fecode, 1); break; /* End of ket processing */ } /* Repeat the maximum number of times (KETRMAX) */ RMATCH(bracode, RM7); if (rrc != MATCH_NOMATCH) RRETURN(rrc); } /* Carry on at this level for a non-repeating ket, or after matching an empty string, or after repeating for a maximum number of times. */ Fecode += 1 + LINK_SIZE; break; /* ===================================================================== */ /* Start and end of line assertions, not multiline mode. */ case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */ if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0) RRETURN(MATCH_NOMATCH); Fecode++; break; case OP_SOD: /* Unconditional start of subject */ if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH); Fecode++; break; /* When PCRE2_NOTEOL is unset, assert before the subject end, or a terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */ case OP_DOLL: if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS; /* Fall through */ /* Unconditional end of subject assertion (\z) */ case OP_EOD: if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH); if (mb->partial != 0) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Fecode++; break; /* End of subject or ending \n assertion (\Z) */ case OP_EODN: ASSERT_NL_OR_EOS: if (Feptr < mb->end_subject && (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen)) { if (mb->partial != 0 && Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } RRETURN(MATCH_NOMATCH); } /* Either at end of string or \n before end. */ if (mb->partial != 0) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } Fecode++; break; /* ===================================================================== */ /* Start and end of line assertions, multiline mode. */ /* Start of subject unless notbol, or after any newline except for one at the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */ case OP_CIRCM: if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject) RRETURN(MATCH_NOMATCH); if (Feptr != mb->start_subject && ((Feptr == mb->end_subject && (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) || !WAS_NEWLINE(Feptr))) RRETURN(MATCH_NOMATCH); Fecode++; break; /* Assert before any newline, or before end of subject unless noteol is set. */ case OP_DOLLM: if (Feptr < mb->end_subject) { if (!IS_NEWLINE(Feptr)) { if (mb->partial != 0 && Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } RRETURN(MATCH_NOMATCH); } } else { if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); SCHECK_PARTIAL(); } Fecode++; break; /* ===================================================================== */ /* Start of match assertion */ case OP_SOM: if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); Fecode++; break; /* ===================================================================== */ /* Reset the start of match point */ case OP_SET_SOM: Fstart_match = Feptr; Fecode++; break; /* ===================================================================== */ /* Word boundary assertions. Find out if the previous and current characters are "word" characters. It takes a bit more work in UTF mode. Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is not set. When it is set, use Unicode properties if available, even when not in UTF mode. Remember the earliest and latest consulted characters. */ case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: if (Feptr == mb->check_subject) prev_is_word = FALSE; else { PCRE2_SPTR lastptr = Feptr - 1; #ifdef SUPPORT_UNICODE if (utf) { BACKCHAR(lastptr); GETCHAR(fc, lastptr); } else #endif /* SUPPORT_UNICODE */ fc = *lastptr; if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr; #ifdef SUPPORT_UNICODE if ((mb->poptions & PCRE2_UCP) != 0) { if (fc == '_') prev_is_word = TRUE; else { int cat = UCD_CATEGORY(fc); prev_is_word = (cat == ucp_L || cat == ucp_N); } } else #endif /* SUPPORT_UNICODE */ prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; } /* Get status of next character */ if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); cur_is_word = FALSE; } else { PCRE2_SPTR nextptr = Feptr + 1; #ifdef SUPPORT_UNICODE if (utf) { FORWARDCHARTEST(nextptr, mb->end_subject); GETCHAR(fc, Feptr); } else #endif /* SUPPORT_UNICODE */ fc = *Feptr; if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr; #ifdef SUPPORT_UNICODE if ((mb->poptions & PCRE2_UCP) != 0) { if (fc == '_') cur_is_word = TRUE; else { int cat = UCD_CATEGORY(fc); cur_is_word = (cat == ucp_L || cat == ucp_N); } } else #endif /* SUPPORT_UNICODE */ cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; } /* Now see if the situation is what we want */ if ((*Fecode++ == OP_WORD_BOUNDARY)? cur_is_word == prev_is_word : cur_is_word != prev_is_word) RRETURN(MATCH_NOMATCH); break; /* ===================================================================== */ /* Backtracking (*VERB)s, with and without arguments. Note that if the pattern is successfully matched, we do not come back from RMATCH. */ case OP_MARK: Fmark = mb->nomatch_mark = Fecode + 2; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12); /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an argument, and we must check whether that argument matches this MARK's argument. It is passed back in mb->verb_skip_ptr. If it does match, we return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject position that corresponds to this mark. Otherwise, pass back the return code unaltered. */ if (rrc == MATCH_SKIP_ARG && PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0) { mb->verb_skip_ptr = Feptr; /* Pass back current position */ RRETURN(MATCH_SKIP); } RRETURN(rrc); case OP_FAIL: RRETURN(MATCH_NOMATCH); /* Record the current recursing group number in mb->verb_current_recurse when a backtracking return such as MATCH_COMMIT is given. This enables the recurse processing to catch verbs from within the recursion. */ case OP_COMMIT: RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_COMMIT); case OP_COMMIT_ARG: Fmark = mb->nomatch_mark = Fecode + 2; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_COMMIT); case OP_PRUNE: RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_PRUNE); case OP_PRUNE_ARG: Fmark = mb->nomatch_mark = Fecode + 2; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_PRUNE); case OP_SKIP: RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_skip_ptr = Feptr; /* Pass back current position */ mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_SKIP); /* Note that, for Perl compatibility, SKIP with an argument does NOT set nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was not a matching mark, we have to re-run the match, ignoring the SKIP_ARG that failed and any that precede it (either they also failed, or were not triggered). To do this, we maintain a count of executed SKIP_ARGs. If a SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg set to the count of the one that failed. */ case OP_SKIP_ARG: mb->skip_arg_count++; if (mb->skip_arg_count <= mb->ignore_skip_arg) { Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1]; break; } RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17); if (rrc != MATCH_NOMATCH) RRETURN(rrc); /* Pass back the current skip name and return the special MATCH_SKIP_ARG return code. This will either be caught by a matching MARK, or get to the top, where it causes a rematch with mb->ignore_skip_arg set to the value of mb->skip_arg_count. */ mb->verb_skip_ptr = Fecode + 2; mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_SKIP_ARG); /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that the branch in which it occurs can be determined. */ case OP_THEN: RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_ecode_ptr = Fecode; mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_THEN); case OP_THEN_ARG: Fmark = mb->nomatch_mark = Fecode + 2; RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19); if (rrc != MATCH_NOMATCH) RRETURN(rrc); mb->verb_ecode_ptr = Fecode; mb->verb_current_recurse = Fcurrent_recurse; RRETURN(MATCH_THEN); /* ===================================================================== */ /* There's been some horrible disaster. Arrival here can only mean there is something seriously wrong in the code above or the OP_xxx definitions. */ default: return PCRE2_ERROR_INTERNAL; } /* Do not insert any code in here without much thought; it is assumed that "continue" in the code above comes out to here to repeat the main loop. */ } /* End of main loop */ /* Control never reaches here */ /* ========================================================================= */ /* The RRETURN() macro jumps here. The number that is saved in Freturn_id indicates which label we actually want to return to. The value in Frdepth is the index number of the frame in the vector. The return value has been placed in rrc. */ #define LBL(val) case val: goto L_RM##val; RETURN_SWITCH: if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; if (Frdepth == 0) return rrc; /* Exit from the top level */ F = (heapframe *)((char *)F - Fback_frame); /* Backtrack */ mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */ #ifdef DEBUG_SHOW_RMATCH fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id); #endif switch (Freturn_id) { LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16) LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24) LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32) LBL(33) LBL(34) LBL(35) LBL(36) #ifdef SUPPORT_WIDE_CHARS LBL(100) LBL(101) #endif #ifdef SUPPORT_UNICODE LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206) LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213) LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220) LBL(221) LBL(222) #endif default: return PCRE2_ERROR_INTERNAL; } #undef LBL } /************************************************* * Match a Regular Expression * *************************************************/ /* This function applies a compiled pattern to a subject string and picks out portions of the string if it matches. Two elements in the vector are set for each substring: the offsets to the start and end of the substring. Arguments: code points to the compiled expression subject points to the subject string length length of subject string (may contain binary zeros) start_offset where to start in the subject string options option bits match_data points to a match_data block mcontext points a PCRE2 context Returns: > 0 => success; value is the number of ovector pairs filled = 0 => success, but ovector is not big enough = -1 => failed to match (PCRE2_ERROR_NOMATCH) = -2 => partial match (PCRE2_ERROR_PARTIAL) < -2 => some kind of unexpected problem */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext) { int rc; int was_zero_terminated = 0; const uint8_t *start_bits = NULL; const pcre2_real_code *re = (const pcre2_real_code *)code; BOOL anchored; BOOL firstline; BOOL has_first_cu = FALSE; BOOL has_req_cu = FALSE; BOOL startline; #if PCRE2_CODE_UNIT_WIDTH == 8 PCRE2_SPTR memchr_found_first_cu; PCRE2_SPTR memchr_found_first_cu2; #endif PCRE2_UCHAR first_cu = 0; PCRE2_UCHAR first_cu2 = 0; PCRE2_UCHAR req_cu = 0; PCRE2_UCHAR req_cu2 = 0; PCRE2_SPTR bumpalong_limit; PCRE2_SPTR end_subject; PCRE2_SPTR true_end_subject; PCRE2_SPTR start_match = subject + start_offset; PCRE2_SPTR req_cu_ptr = start_match - 1; PCRE2_SPTR start_partial; PCRE2_SPTR match_partial; #ifdef SUPPORT_JIT BOOL use_jit; #endif /* This flag is needed even when Unicode is not supported for convenience (it is used by the IS_NEWLINE macro). */ BOOL utf = FALSE; #ifdef SUPPORT_UNICODE BOOL ucp = FALSE; BOOL allow_invalid; uint32_t fragment_options = 0; #ifdef SUPPORT_JIT BOOL jit_checked_utf = FALSE; #endif #endif /* SUPPORT_UNICODE */ PCRE2_SIZE frame_size; /* We need to have mb as a pointer to a match block, because the IS_NEWLINE macro is used below, and it expects NLBLOCK to be defined as a pointer. */ pcre2_callout_block cb; match_block actual_match_block; match_block *mb = &actual_match_block; /* Allocate an initial vector of backtracking frames on the stack. If this proves to be too small, it is replaced by a larger one on the heap. To get a vector of the size required that is aligned for pointers, allocate it as a vector of pointers. */ PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)] PCRE2_KEEP_UNINITIALIZED; mb->stack_frames = (heapframe *)stack_frames_vector; /* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated subject string. */ if (length == PCRE2_ZERO_TERMINATED) { length = PRIV(strlen)(subject); was_zero_terminated = 1; } true_end_subject = end_subject = subject + length; /* Plausibility checks */ if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION; if (code == NULL || subject == NULL || match_data == NULL) return PCRE2_ERROR_NULL; if (start_offset > length) return PCRE2_ERROR_BADOFFSET; /* Check that the first field in the block is the magic number. */ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; /* Check the code unit width. */ if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8) return PCRE2_ERROR_BADMODE; /* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the options variable for this function. Users of PCRE2 who are not calling the function directly would like to have a way of setting these flags, in the same way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and (*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now transfer to the options for this function. The bits are guaranteed to be adjacent, but do not have the same values. This bit of Boolean trickery assumes that the match-time bits are not more significant than the flag bits. If by accident this is not the case, a compile-time division by zero error will occur. */ #define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) #define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO /* If the pattern was successfully studied with JIT support, we will run the JIT executable instead of the rest of this function. Most options must be set at compile time for the JIT code to be usable. */ #ifdef SUPPORT_JIT use_jit = (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0); #endif /* Initialize UTF/UCP parameters. */ #ifdef SUPPORT_UNICODE utf = (re->overall_options & PCRE2_UTF) != 0; allow_invalid = (re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0; ucp = (re->overall_options & PCRE2_UCP) != 0; #endif /* SUPPORT_UNICODE */ /* Convert the partial matching flags into an integer. */ mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 : ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0; /* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same time. */ if (mb->partial != 0 && ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) return PCRE2_ERROR_BADOPTION; /* It is an error to set an offset limit without setting the flag at compile time. */ if (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET && (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0) return PCRE2_ERROR_BADOFFSETLIMIT; /* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT, free the memory that was obtained. Set the field to NULL for no match cases. */ if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0) { match_data->memctl.free((void *)match_data->subject, match_data->memctl.memory_data); match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT; } match_data->subject = NULL; /* Zero the error offset in case the first code unit is invalid UTF. */ match_data->startchar = 0; /* ============================= JIT matching ============================== */ /* Prepare for JIT matching. Check a UTF string for validity unless no check is requested or invalid UTF can be handled. We check only the portion of the subject that might be be inspected during matching - from the offset minus the maximum lookbehind to the given length. This saves time when a small part of a large subject is being matched by the use of a starting offset. Note that the maximum lookbehind is a number of characters, not code units. */ #ifdef SUPPORT_JIT if (use_jit) { #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid) { #if PCRE2_CODE_UNIT_WIDTH != 32 unsigned int i; #endif /* For 8-bit and 16-bit UTF, check that the first code unit is a valid character start. */ #if PCRE2_CODE_UNIT_WIDTH != 32 if (start_match < end_subject && NOT_FIRSTCU(*start_match)) { if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET; #if PCRE2_CODE_UNIT_WIDTH == 8 return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */ #else return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */ #endif } #endif /* WIDTH != 32 */ /* Move back by the maximum lookbehind, just in case it happens at the very start of matching. */ #if PCRE2_CODE_UNIT_WIDTH != 32 for (i = re->max_lookbehind; i > 0 && start_match > subject; i--) { start_match--; while (start_match > subject && #if PCRE2_CODE_UNIT_WIDTH == 8 (*start_match & 0xc0) == 0x80) #else /* 16-bit */ (*start_match & 0xfc00) == 0xdc00) #endif start_match--; } #else /* PCRE2_CODE_UNIT_WIDTH != 32 */ /* In the 32-bit library, one code unit equals one character. However, we cannot just subtract the lookbehind and then compare pointers, because a very large lookbehind could create an invalid pointer. */ if (start_offset >= re->max_lookbehind) start_match -= re->max_lookbehind; else start_match = subject; #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ /* Validate the relevant portion of the subject. Adjust the offset of an invalid code point to be an absolute offset in the whole string. */ match_data->rc = PRIV(valid_utf)(start_match, length - (start_match - subject), &(match_data->startchar)); if (match_data->rc != 0) { match_data->startchar += start_match - subject; return match_data->rc; } jit_checked_utf = TRUE; } #endif /* SUPPORT_UNICODE */ /* If JIT returns BADOPTION, which means that the selected complete or partial matching mode was not compiled, fall through to the interpreter. */ rc = pcre2_jit_match(code, subject, length, start_offset, options, match_data, mcontext); if (rc != PCRE2_ERROR_JIT_BADOPTION) { if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { length = CU2BYTES(length + was_zero_terminated); match_data->subject = match_data->memctl.malloc(length, match_data->memctl.memory_data); if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY; memcpy((void *)match_data->subject, subject, length); match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } return rc; } } #endif /* SUPPORT_JIT */ /* ========================= End of JIT matching ========================== */ /* Proceed with non-JIT matching. The default is to allow lookbehinds to the start of the subject. A UTF check when there is a non-zero offset may change this. */ mb->check_subject = subject; /* If a UTF subject string was not checked for validity in the JIT code above, check it here, and handle support for invalid UTF strings. The check above happens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset. If we get here in those circumstances, it means the subject string is valid, but for some reason JIT matching was not successful. There is no need to check the subject again. We check only the portion of the subject that might be be inspected during matching - from the offset minus the maximum lookbehind to the given length. This saves time when a small part of a large subject is being matched by the use of a starting offset. Note that the maximum lookbehind is a number of characters, not code units. Note also that support for invalid UTF forces a check, overriding the setting of PCRE2_NO_CHECK_UTF. */ #ifdef SUPPORT_UNICODE if (utf && #ifdef SUPPORT_JIT !jit_checked_utf && #endif ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid)) { #if PCRE2_CODE_UNIT_WIDTH != 32 BOOL skipped_bad_start = FALSE; #endif /* For 8-bit and 16-bit UTF, check that the first code unit is a valid character start. If we are handling invalid UTF, just skip over such code units. Otherwise, give an appropriate error. */ #if PCRE2_CODE_UNIT_WIDTH != 32 if (allow_invalid) { while (start_match < end_subject && NOT_FIRSTCU(*start_match)) { start_match++; skipped_bad_start = TRUE; } } else if (start_match < end_subject && NOT_FIRSTCU(*start_match)) { if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET; #if PCRE2_CODE_UNIT_WIDTH == 8 return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */ #else return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */ #endif } #endif /* WIDTH != 32 */ /* The mb->check_subject field points to the start of UTF checking; lookbehinds can go back no further than this. */ mb->check_subject = start_match; /* Move back by the maximum lookbehind, just in case it happens at the very start of matching, but don't do this if we skipped bad 8-bit or 16-bit code units above. */ #if PCRE2_CODE_UNIT_WIDTH != 32 if (!skipped_bad_start) { unsigned int i; for (i = re->max_lookbehind; i > 0 && mb->check_subject > subject; i--) { mb->check_subject--; while (mb->check_subject > subject && #if PCRE2_CODE_UNIT_WIDTH == 8 (*mb->check_subject & 0xc0) == 0x80) #else /* 16-bit */ (*mb->check_subject & 0xfc00) == 0xdc00) #endif mb->check_subject--; } } #else /* PCRE2_CODE_UNIT_WIDTH != 32 */ /* In the 32-bit library, one code unit equals one character. However, we cannot just subtract the lookbehind and then compare pointers, because a very large lookbehind could create an invalid pointer. */ if (start_offset >= re->max_lookbehind) mb->check_subject -= re->max_lookbehind; else mb->check_subject = subject; #endif /* PCRE2_CODE_UNIT_WIDTH != 32 */ /* Validate the relevant portion of the subject. There's a loop in case we encounter bad UTF in the characters preceding start_match which we are scanning because of a lookbehind. */ for (;;) { match_data->rc = PRIV(valid_utf)(mb->check_subject, length - (mb->check_subject - subject), &(match_data->startchar)); if (match_data->rc == 0) break; /* Valid UTF string */ /* Invalid UTF string. Adjust the offset to be an absolute offset in the whole string. If we are handling invalid UTF strings, set end_subject to stop before the bad code unit, and set the options to "not end of line". Otherwise return the error. */ match_data->startchar += mb->check_subject - subject; if (!allow_invalid || match_data->rc > 0) return match_data->rc; end_subject = subject + match_data->startchar; /* If the end precedes start_match, it means there is invalid UTF in the extra code units we reversed over because of a lookbehind. Advance past the first bad code unit, and then skip invalid character starting code units in 8-bit and 16-bit modes, and try again. */ if (end_subject < start_match) { mb->check_subject = end_subject + 1; #if PCRE2_CODE_UNIT_WIDTH != 32 while (mb->check_subject < start_match && NOT_FIRSTCU(*mb->check_subject)) mb->check_subject++; #endif } /* Otherwise, set the not end of line option, and do the match. */ else { fragment_options = PCRE2_NOTEOL; break; } } } #endif /* SUPPORT_UNICODE */ /* A NULL match context means "use a default context", but we take the memory control functions from the pattern. */ if (mcontext == NULL) { mcontext = (pcre2_match_context *)(&PRIV(default_match_context)); mb->memctl = re->memctl; } else mb->memctl = mcontext->memctl; anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; startline = (re->flags & PCRE2_STARTLINE) != 0; bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)? true_end_subject : subject + mcontext->offset_limit; /* Initialize and set up the fixed fields in the callout block, with a pointer in the match block. */ mb->cb = &cb; cb.version = 2; cb.subject = subject; cb.subject_length = (PCRE2_SIZE)(end_subject - subject); cb.callout_flags = 0; /* Fill in the remaining fields in the match block, except for moptions, which gets set later. */ mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->start_subject = subject; mb->start_offset = start_offset; mb->end_subject = end_subject; mb->hasthen = (re->flags & PCRE2_HASTHEN) != 0; mb->allowemptypartial = (re->max_lookbehind > 0) || (re->flags & PCRE2_MATCH_EMPTY) != 0; mb->poptions = re->overall_options; /* Pattern options */ mb->ignore_skip_arg = 0; mb->mark = mb->nomatch_mark = NULL; /* In case never set */ /* The name table is needed for finding all the numbers associated with a given name, for condition testing. The code follows the name table. */ mb->name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); mb->name_count = re->name_count; mb->name_entry_size = re->name_entry_size; mb->start_code = mb->name_table + re->name_count * re->name_entry_size; /* Process the \R and newline settings. */ mb->bsr_convention = re->bsr_convention; mb->nltype = NLTYPE_FIXED; switch(re->newline_convention) { case PCRE2_NEWLINE_CR: mb->nllen = 1; mb->nl[0] = CHAR_CR; break; case PCRE2_NEWLINE_LF: mb->nllen = 1; mb->nl[0] = CHAR_NL; break; case PCRE2_NEWLINE_NUL: mb->nllen = 1; mb->nl[0] = CHAR_NUL; break; case PCRE2_NEWLINE_CRLF: mb->nllen = 2; mb->nl[0] = CHAR_CR; mb->nl[1] = CHAR_NL; break; case PCRE2_NEWLINE_ANY: mb->nltype = NLTYPE_ANY; break; case PCRE2_NEWLINE_ANYCRLF: mb->nltype = NLTYPE_ANYCRLF; break; default: return PCRE2_ERROR_INTERNAL; } /* The backtracking frames have fixed data at the front, and a PCRE2_SIZE vector at the end, whose size depends on the number of capturing parentheses in the pattern. It is not used at all if there are no capturing parentheses. frame_size is the total size of each frame mb->frame_vector_size is the total usable size of the vector (rounded down to a whole number of frames) The last of these is changed within the match() function if the frame vector has to be expanded. We therefore put it into the match block so that it is correct when calling match() more than once for non-anchored patterns. */ frame_size = offsetof(heapframe, ovector) + re->top_bracket * 2 * sizeof(PCRE2_SIZE); /* Limits set in the pattern override the match context only if they are smaller. */ mb->heap_limit = (mcontext->heap_limit < re->limit_heap)? mcontext->heap_limit : re->limit_heap; mb->match_limit = (mcontext->match_limit < re->limit_match)? mcontext->match_limit : re->limit_match; mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)? mcontext->depth_limit : re->limit_depth; /* If a pattern has very many capturing parentheses, the frame size may be very large. Ensure that there are at least 10 available frames by getting an initial vector on the heap if necessary, except when the heap limit prevents this. Get fewer if possible. (The heap limit is in kibibytes.) */ if (frame_size <= START_FRAMES_SIZE/10) { mb->match_frames = mb->stack_frames; /* Initial frame vector on the stack */ mb->frame_vector_size = ((START_FRAMES_SIZE/frame_size) * frame_size); } else { mb->frame_vector_size = frame_size * 10; if ((mb->frame_vector_size / 1024) > mb->heap_limit) { if (frame_size > mb->heap_limit * 1024) return PCRE2_ERROR_HEAPLIMIT; mb->frame_vector_size = ((mb->heap_limit * 1024)/frame_size) * frame_size; } mb->match_frames = mb->memctl.malloc(mb->frame_vector_size, mb->memctl.memory_data); if (mb->match_frames == NULL) return PCRE2_ERROR_NOMEMORY; } mb->match_frames_top = (heapframe *)((char *)mb->match_frames + mb->frame_vector_size); /* Write to the ovector within the first frame to mark every capture unset and to avoid uninitialized memory read errors when it is copied to a new frame. */ memset((char *)(mb->match_frames) + offsetof(heapframe, ovector), 0xff, re->top_bracket * 2 * sizeof(PCRE2_SIZE)); /* Pointers to the individual character tables */ mb->lcc = re->tables + lcc_offset; mb->fcc = re->tables + fcc_offset; mb->ctypes = re->tables + ctypes_offset; /* Set up the first code unit to match, if available. If there's no first code unit there may be a bitmap of possible first characters. */ if ((re->flags & PCRE2_FIRSTSET) != 0) { has_first_cu = TRUE; first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); if ((re->flags & PCRE2_FIRSTCASELESS) != 0) { first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (first_cu > 127 && ucp && !utf) first_cu2 = UCD_OTHERCASE(first_cu); #else if (first_cu > 127 && (utf || ucp)) first_cu2 = UCD_OTHERCASE(first_cu); #endif #endif /* SUPPORT_UNICODE */ } } else if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) start_bits = re->start_bitmap; /* There may also be a "last known required character" set. */ if ((re->flags & PCRE2_LASTSET) != 0) { has_req_cu = TRUE; req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit); if ((re->flags & PCRE2_LASTCASELESS) != 0) { req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu); #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (req_cu > 127 && ucp && !utf) req_cu2 = UCD_OTHERCASE(req_cu); #else if (req_cu > 127 && (utf || ucp)) req_cu2 = UCD_OTHERCASE(req_cu); #endif #endif /* SUPPORT_UNICODE */ } } /* ==========================================================================*/ /* Loop for handling unanchored repeated matching attempts; for anchored regexs the loop runs just once. */ #ifdef SUPPORT_UNICODE FRAGMENT_RESTART: #endif start_partial = match_partial = NULL; mb->hitend = FALSE; #if PCRE2_CODE_UNIT_WIDTH == 8 memchr_found_first_cu = NULL; memchr_found_first_cu2 = NULL; #endif for(;;) { PCRE2_SPTR new_start_match; /* ----------------- Start of match optimizations ---------------- */ /* There are some optimizations that avoid running the match if a known starting point is not found, or if a known later code unit is not present. However, there is an option (settable at compile time) that disables these, for testing and for ensuring that all callouts do actually occur. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { /* If firstline is TRUE, the start of the match is constrained to the first line of a multiline string. That is, the match must be before or at the first newline following the start of matching. Temporarily adjust end_subject so that we stop the scans for a first code unit at a newline. If the match fails at the newline, later code breaks the loop. */ if (firstline) { PCRE2_SPTR t = start_match; #ifdef SUPPORT_UNICODE if (utf) { while (t < end_subject && !IS_NEWLINE(t)) { t++; ACROSSCHAR(t < end_subject, t, t++); } } else #endif while (t < end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } /* Anchored: check the first code unit if one is recorded. This may seem pointless but it can help in detecting a no match case without scanning for the required code unit. */ if (anchored) { if (has_first_cu || start_bits != NULL) { BOOL ok = start_match < end_subject; if (ok) { PCRE2_UCHAR c = UCHAR21TEST(start_match); ok = has_first_cu && (c == first_cu || c == first_cu2); if (!ok && start_bits != NULL) { #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif ok = (start_bits[c/8] & (1u << (c&7))) != 0; } } if (!ok) { rc = MATCH_NOMATCH; break; } } } /* Not anchored. Advance to a unique first code unit if there is one. */ else { if (has_first_cu) { if (first_cu != first_cu2) /* Caseless */ { /* In 16-bit and 32_bit modes we have to do our own search, so can look for both cases at once. */ #if PCRE2_CODE_UNIT_WIDTH != 8 PCRE2_UCHAR smc; while (start_match < end_subject && (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) start_match++; #else /* In 8-bit mode, the use of memchr() gives a big speed up, even though we have to call it twice in order to find the earliest occurrence of the code unit in either of its cases. Caching is used to remember the positions of previously found code units. This can make a huge difference when the strings are very long and only one case is actually present. */ PCRE2_SPTR pp1 = NULL; PCRE2_SPTR pp2 = NULL; PCRE2_SIZE searchlength = end_subject - start_match; /* If we haven't got a previously found position for first_cu, or if the current starting position is later, we need to do a search. If the code unit is not found, set it to the end. */ if (memchr_found_first_cu == NULL || start_match > memchr_found_first_cu) { pp1 = memchr(start_match, first_cu, searchlength); memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1; } /* If the start is before a previously found position, use the previous position, or NULL if a previous search failed. */ else pp1 = (memchr_found_first_cu == end_subject)? NULL : memchr_found_first_cu; /* Do the same thing for the other case. */ if (memchr_found_first_cu2 == NULL || start_match > memchr_found_first_cu2) { pp2 = memchr(start_match, first_cu2, searchlength); memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2; } else pp2 = (memchr_found_first_cu2 == end_subject)? NULL : memchr_found_first_cu2; /* Set the start to the end of the subject if neither case was found. Otherwise, use the earlier found point. */ if (pp1 == NULL) start_match = (pp2 == NULL)? end_subject : pp2; else start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; #endif /* 8-bit handling */ } /* The caseful case is much simpler. */ else { #if PCRE2_CODE_UNIT_WIDTH != 8 while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) start_match++; #else start_match = memchr(start_match, first_cu, end_subject - start_match); if (start_match == NULL) start_match = end_subject; #endif } /* If we can't find the required first code unit, having reached the true end of the subject, break the bumpalong loop, to force a match failure, except when doing partial matching, when we let the next cycle run at the end of the subject. To see why, consider the pattern /(?<=abc)def/, which partially matches "abc", even though the string does not contain the starting character "d". If we have not reached the true end of the subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) we also let the cycle run, because the matching string is legitimately allowed to start with the first code unit of a newline. */ if (mb->partial == 0 && start_match >= mb->end_subject) { rc = MATCH_NOMATCH; break; } } /* If there's no first code unit, advance to just after a linebreak for a multiline match if required. */ else if (startline) { if (start_match > mb->start_subject + start_offset) { #ifdef SUPPORT_UNICODE if (utf) { while (start_match < end_subject && !WAS_NEWLINE(start_match)) { start_match++; ACROSSCHAR(start_match < end_subject, start_match, start_match++); } } else #endif while (start_match < end_subject && !WAS_NEWLINE(start_match)) start_match++; /* If we have just passed a CR and the newline option is ANY or ANYCRLF, and we are now at a LF, advance the match position by one more code unit. */ if (start_match[-1] == CHAR_CR && (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && start_match < end_subject && UCHAR21TEST(start_match) == CHAR_NL) start_match++; } } /* If there's no first code unit or a requirement for a multiline line start, advance to a non-unique first code unit if any have been identified. The bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all code units greater than 254 set the 255 bit. */ else if (start_bits != NULL) { while (start_match < end_subject) { uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif if ((start_bits[c/8] & (1u << (c&7))) != 0) break; start_match++; } /* See comment above in first_cu checking about the next few lines. */ if (mb->partial == 0 && start_match >= mb->end_subject) { rc = MATCH_NOMATCH; break; } } } /* End first code unit handling */ /* Restore fudged end_subject */ end_subject = mb->end_subject; /* The following two optimizations must be disabled for partial matching. */ if (mb->partial == 0) { PCRE2_SPTR p; /* The minimum matching length is a lower bound; no string of that length may actually match the pattern. Although the value is, strictly, in characters, we treat it as code units to avoid spending too much time in this optimization. */ if (end_subject - start_match < re->minlength) { rc = MATCH_NOMATCH; break; } /* If req_cu is set, we know that that code unit must appear in the subject for the (non-partial) match to succeed. If the first code unit is set, req_cu must be later in the subject; otherwise the test starts at the match point. This optimization can save a huge amount of backtracking in patterns with nested unlimited repeats that aren't going to match. Writing separate code for caseful/caseless versions makes it go faster, as does using an autoincrement and backing off on a match. As in the case of the first code unit, using memchr() in the 8-bit library gives a big speed up. Unlike the first_cu check above, we do not need to call memchr() twice in the caseless case because we only need to check for the presence of the character in either case, not find the first occurrence. The search can be skipped if the code unit was found later than the current starting point in a previous iteration of the bumpalong loop. HOWEVER: when the subject string is very, very long, searching to its end can take a long time, and give bad performance on quite ordinary anchored patterns. This showed up when somebody was matching something like /^\d+C/ on a 32-megabyte string... so we don't do this when the string is sufficiently long, but it's worth searching a lot more for unanchored patterns. */ p = start_match + (has_first_cu? 1:0); if (has_req_cu && p > req_cu_ptr) { PCRE2_SIZE check_length = end_subject - start_match; if (check_length < REQ_CU_MAX || (!anchored && check_length < REQ_CU_MAX * 1000)) { if (req_cu != req_cu2) /* Caseless */ { #if PCRE2_CODE_UNIT_WIDTH != 8 while (p < end_subject) { uint32_t pp = UCHAR21INCTEST(p); if (pp == req_cu || pp == req_cu2) { p--; break; } } #else /* 8-bit code units */ PCRE2_SPTR pp = p; p = memchr(pp, req_cu, end_subject - pp); if (p == NULL) { p = memchr(pp, req_cu2, end_subject - pp); if (p == NULL) p = end_subject; } #endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ } /* The caseful case */ else { #if PCRE2_CODE_UNIT_WIDTH != 8 while (p < end_subject) { if (UCHAR21INCTEST(p) == req_cu) { p--; break; } } #else /* 8-bit code units */ p = memchr(p, req_cu, end_subject - p); if (p == NULL) p = end_subject; #endif } /* If we can't find the required code unit, break the bumpalong loop, forcing a match failure. */ if (p >= end_subject) { rc = MATCH_NOMATCH; break; } /* If we have found the required code unit, save the point where we found it, so that we don't search again next time round the bumpalong loop if the start hasn't yet passed this code unit. */ req_cu_ptr = p; } } } } /* ------------ End of start of match optimizations ------------ */ /* Give no match if we have passed the bumpalong limit. */ if (start_match > bumpalong_limit) { rc = MATCH_NOMATCH; break; } /* OK, we can now run the match. If "hitend" is set afterwards, remember the first starting point for which a partial match was found. */ cb.start_match = (PCRE2_SIZE)(start_match - subject); cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH; mb->start_used_ptr = start_match; mb->last_used_ptr = start_match; #ifdef SUPPORT_UNICODE mb->moptions = options | fragment_options; #else mb->moptions = options; #endif mb->match_call_count = 0; mb->end_offset_top = 0; mb->skip_arg_count = 0; rc = match(start_match, mb->start_code, match_data->ovector, match_data->oveccount, re->top_bracket, frame_size, mb); if (mb->hitend && start_partial == NULL) { start_partial = mb->start_used_ptr; match_partial = start_match; } switch(rc) { /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP entirely. The only way we can do that is to re-do the match at the same point, with a flag to force SKIP with an argument to be ignored. Just treating this case as NOMATCH does not work because it does not check other alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */ case MATCH_SKIP_ARG: new_start_match = start_match; mb->ignore_skip_arg = mb->skip_arg_count; break; /* SKIP passes back the next starting point explicitly, but if it is no greater than the match we have just done, treat it as NOMATCH. */ case MATCH_SKIP: if (mb->verb_skip_ptr > start_match) { new_start_match = mb->verb_skip_ptr; break; } /* Fall through */ /* NOMATCH and PRUNE advance by one character. THEN at this level acts exactly like PRUNE. Unset ignore SKIP-with-argument. */ case MATCH_NOMATCH: case MATCH_PRUNE: case MATCH_THEN: mb->ignore_skip_arg = 0; new_start_match = start_match + 1; #ifdef SUPPORT_UNICODE if (utf) ACROSSCHAR(new_start_match < end_subject, new_start_match, new_start_match++); #endif break; /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ case MATCH_COMMIT: rc = MATCH_NOMATCH; goto ENDLOOP; /* Any other return is either a match, or some kind of error. */ default: goto ENDLOOP; } /* Control reaches here for the various types of "no match at this point" result. Reset the code to MATCH_NOMATCH for subsequent checking. */ rc = MATCH_NOMATCH; /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first newline in the subject (though it may continue over the newline). Therefore, if we have just failed to match, starting at a newline, do not continue. */ if (firstline && IS_NEWLINE(start_match)) break; /* Advance to new matching position */ start_match = new_start_match; /* Break the loop if the pattern is anchored or if we have passed the end of the subject. */ if (anchored || start_match > end_subject) break; /* If we have just passed a CR and we are now at a LF, and the pattern does not contain any explicit matches for \r or \n, and the newline option is CRLF or ANY or ANYCRLF, advance the match position by one more code unit. In normal matching start_match will aways be greater than the first position at this stage, but a failed *SKIP can cause a return at the same point, which is why the first test exists. */ if (start_match > subject + start_offset && start_match[-1] == CHAR_CR && start_match < end_subject && *start_match == CHAR_NL && (re->flags & PCRE2_HASCRORLF) == 0 && (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF || mb->nllen == 2)) start_match++; mb->mark = NULL; /* Reset for start of next match attempt */ } /* End of for(;;) "bumpalong" loop */ /* ==========================================================================*/ /* When we reach here, one of the following stopping conditions is true: (1) The match succeeded, either completely, or partially; (2) The pattern is anchored or the match was failed after (*COMMIT); (3) We are past the end of the subject or the bumpalong limit; (4) PCRE2_FIRSTLINE is set and we have failed to match at a newline, because this option requests that a match occur at or before the first newline in the subject. (5) Some kind of error occurred. */ ENDLOOP: /* If end_subject != true_end_subject, it means we are handling invalid UTF, and have just processed a non-terminal fragment. If this resulted in no match or a partial match we must carry on to the next fragment (a partial match is returned to the caller only at the very end of the subject). A loop is used to avoid trying to match against empty fragments; if the pattern can match an empty string it would have done so already. */ #ifdef SUPPORT_UNICODE if (utf && end_subject != true_end_subject && (rc == MATCH_NOMATCH || rc == PCRE2_ERROR_PARTIAL)) { for (;;) { /* Advance past the first bad code unit, and then skip invalid character starting code units in 8-bit and 16-bit modes. */ start_match = end_subject + 1; #if PCRE2_CODE_UNIT_WIDTH != 32 while (start_match < true_end_subject && NOT_FIRSTCU(*start_match)) start_match++; #endif /* If we have hit the end of the subject, there isn't another non-empty fragment, so give up. */ if (start_match >= true_end_subject) { rc = MATCH_NOMATCH; /* In case it was partial */ break; } /* Check the rest of the subject */ mb->check_subject = start_match; rc = PRIV(valid_utf)(start_match, length - (start_match - subject), &(match_data->startchar)); /* The rest of the subject is valid UTF. */ if (rc == 0) { mb->end_subject = end_subject = true_end_subject; fragment_options = PCRE2_NOTBOL; goto FRAGMENT_RESTART; } /* A subsequent UTF error has been found; if the next fragment is non-empty, set up to process it. Otherwise, let the loop advance. */ else if (rc < 0) { mb->end_subject = end_subject = start_match + match_data->startchar; if (end_subject > start_match) { fragment_options = PCRE2_NOTBOL|PCRE2_NOTEOL; goto FRAGMENT_RESTART; } } } } #endif /* SUPPORT_UNICODE */ /* Release an enlarged frame vector that is on the heap. */ if (mb->match_frames != mb->stack_frames) mb->memctl.free(mb->match_frames, mb->memctl.memory_data); /* Fill in fields that are always returned in the match data. */ match_data->code = re; match_data->mark = mb->mark; match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER; /* Handle a fully successful match. Set the return code to the number of captured strings, or 0 if there were too many to fit into the ovector, and then set the remaining returned values before returning. Make a copy of the subject string if requested. */ if (rc == MATCH_MATCH) { match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)? 0 : (int)mb->end_offset_top/2 + 1; match_data->startchar = start_match - subject; match_data->leftchar = mb->start_used_ptr - subject; match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)? mb->last_used_ptr : mb->end_match_ptr) - subject; if ((options & PCRE2_COPY_MATCHED_SUBJECT) != 0) { length = CU2BYTES(length + was_zero_terminated); match_data->subject = match_data->memctl.malloc(length, match_data->memctl.memory_data); if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY; memcpy((void *)match_data->subject, subject, length); match_data->flags |= PCRE2_MD_COPIED_SUBJECT; } else match_data->subject = subject; return match_data->rc; } /* Control gets here if there has been a partial match, an error, or if the overall match attempt has failed at all permitted starting positions. Any mark data is in the nomatch_mark field. */ match_data->mark = mb->nomatch_mark; /* For anything other than nomatch or partial match, just return the code. */ if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc; /* Handle a partial match. If a "soft" partial match was requested, searching for a complete match will have continued, and the value of rc at this point will be MATCH_NOMATCH. For a "hard" partial match, it will already be PCRE2_ERROR_PARTIAL. */ else if (match_partial != NULL) { match_data->subject = subject; match_data->ovector[0] = match_partial - subject; match_data->ovector[1] = end_subject - subject; match_data->startchar = match_partial - subject; match_data->leftchar = start_partial - subject; match_data->rightchar = end_subject - subject; match_data->rc = PCRE2_ERROR_PARTIAL; } /* Else this is the classic nomatch case. */ else match_data->rc = PCRE2_ERROR_NOMATCH; return match_data->rc; } /* End of pcre2_match.c */ vfu-4.22/vstring/pcre2/pcre2_jit_misc.c0000644000175000017500000001537314145574056016406 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE #error This file must be included from pcre2_jit_compile.c. #endif /************************************************* * Free JIT read-only data * *************************************************/ void PRIV(jit_free_rodata)(void *current, void *allocator_data) { #ifndef SUPPORT_JIT (void)current; (void)allocator_data; #else /* SUPPORT_JIT */ void *next; SLJIT_UNUSED_ARG(allocator_data); while (current != NULL) { next = *(void**)current; SLJIT_FREE(current, allocator_data); current = next; } #endif /* SUPPORT_JIT */ } /************************************************* * Free JIT compiled code * *************************************************/ void PRIV(jit_free)(void *executable_jit, pcre2_memctl *memctl) { #ifndef SUPPORT_JIT (void)executable_jit; (void)memctl; #else /* SUPPORT_JIT */ executable_functions *functions = (executable_functions *)executable_jit; void *allocator_data = memctl; int i; for (i = 0; i < JIT_NUMBER_OF_COMPILE_MODES; i++) { if (functions->executable_funcs[i] != NULL) sljit_free_code(functions->executable_funcs[i], NULL); PRIV(jit_free_rodata)(functions->read_only_data_heads[i], allocator_data); } SLJIT_FREE(functions, allocator_data); #endif /* SUPPORT_JIT */ } /************************************************* * Free unused JIT memory * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_jit_free_unused_memory(pcre2_general_context *gcontext) { #ifndef SUPPORT_JIT (void)gcontext; /* Suppress warning */ #else /* SUPPORT_JIT */ SLJIT_UNUSED_ARG(gcontext); sljit_free_unused_memory_exec(); #endif /* SUPPORT_JIT */ } /************************************************* * Allocate a JIT stack * *************************************************/ PCRE2_EXP_DEFN pcre2_jit_stack * PCRE2_CALL_CONVENTION pcre2_jit_stack_create(size_t startsize, size_t maxsize, pcre2_general_context *gcontext) { #ifndef SUPPORT_JIT (void)gcontext; (void)startsize; (void)maxsize; return NULL; #else /* SUPPORT_JIT */ pcre2_jit_stack *jit_stack; if (startsize < 1 || maxsize < 1) return NULL; if (startsize > maxsize) startsize = maxsize; startsize = (startsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); maxsize = (maxsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); jit_stack = PRIV(memctl_malloc)(sizeof(pcre2_real_jit_stack), (pcre2_memctl *)gcontext); if (jit_stack == NULL) return NULL; jit_stack->stack = sljit_allocate_stack(startsize, maxsize, &jit_stack->memctl); if (jit_stack->stack == NULL) { jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data); return NULL; } return jit_stack; #endif } /************************************************* * Assign a JIT stack to a pattern * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_jit_stack_assign(pcre2_match_context *mcontext, pcre2_jit_callback callback, void *callback_data) { #ifndef SUPPORT_JIT (void)mcontext; (void)callback; (void)callback_data; #else /* SUPPORT_JIT */ if (mcontext == NULL) return; mcontext->jit_callback = callback; mcontext->jit_callback_data = callback_data; #endif /* SUPPORT_JIT */ } /************************************************* * Free a JIT stack * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_jit_stack_free(pcre2_jit_stack *jit_stack) { #ifndef SUPPORT_JIT (void)jit_stack; #else /* SUPPORT_JIT */ if (jit_stack != NULL) { sljit_free_stack((struct sljit_stack *)(jit_stack->stack), &jit_stack->memctl); jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data); } #endif /* SUPPORT_JIT */ } /************************************************* * Get target CPU type * *************************************************/ const char* PRIV(jit_get_target)(void) { #ifndef SUPPORT_JIT return "JIT is not supported"; #else /* SUPPORT_JIT */ return sljit_get_platform_name(); #endif /* SUPPORT_JIT */ } /************************************************* * Get size of JIT code * *************************************************/ size_t PRIV(jit_get_size)(void *executable_jit) { #ifndef SUPPORT_JIT (void)executable_jit; return 0; #else /* SUPPORT_JIT */ sljit_uw *executable_sizes = ((executable_functions *)executable_jit)->executable_sizes; SLJIT_COMPILE_ASSERT(JIT_NUMBER_OF_COMPILE_MODES == 3, number_of_compile_modes_changed); return executable_sizes[0] + executable_sizes[1] + executable_sizes[2]; #endif } /* End of pcre2_jit_misc.c */ vfu-4.22/vstring/pcre2/pcre2_internal.h0000644000175000017500000026257714145574056016440 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE2 is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD #define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD /* We do not support both EBCDIC and Unicode at the same time. The "configure" script prevents both being selected, but not everybody uses "configure". EBCDIC is only supported for the 8-bit library, but the check for this has to be later in this file, because the first part is not width-dependent, and is included by pcre2test.c with CODE_UNIT_WIDTH == 0. */ #if defined EBCDIC && defined SUPPORT_UNICODE #error The use of both EBCDIC and SUPPORT_UNICODE is not supported. #endif /* Standard C headers */ #include #include #include #include #include #include /* Macros to make boolean values more obvious. The #ifndef is to pacify compiler warnings in environments where these macros are defined elsewhere. Unfortunately, there is no way to do the same for the typedef. */ typedef int BOOL; #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif /* Valgrind (memcheck) support */ #ifdef SUPPORT_VALGRIND #include #endif /* -ftrivial-auto-var-init support supports initializing all local variables to avoid some classes of bug, but this can cause an unacceptable slowdown for large on-stack arrays in hot functions. This macro lets us annotate such arrays. */ #ifdef HAVE_ATTRIBUTE_UNINITIALIZED #define PCRE2_KEEP_UNINITIALIZED __attribute__((uninitialized)) #else #define PCRE2_KEEP_UNINITIALIZED #endif /* Older versions of MSVC lack snprintf(). This define allows for warning/error-free compilation and testing with MSVC compilers back to at least MSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */ #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf #endif /* When compiling a DLL for Windows, the exported symbols have to be declared using some MS magic. I found some useful information on this web page: http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the information there, using __declspec(dllexport) without "extern" we have a definition; with "extern" we have a declaration. The settings here override the setting in pcre2.h (which is included below); it defines only PCRE2_EXP_DECL, which is all that is needed for applications (they just import the symbols). We use: PCRE2_EXP_DECL for declarations PCRE2_EXP_DEFN for definitions The reason for wrapping this in #ifndef PCRE2_EXP_DECL is so that pcre2test, which is an application, but needs to import this file in order to "peek" at internals, can #include pcre2.h first to get an application's-eye view. In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon, special-purpose environments) might want to stick other stuff in front of exported symbols. That's why, in the non-Windows case, we set PCRE2_EXP_DEFN only if it is not already set. */ #ifndef PCRE2_EXP_DECL # ifdef _WIN32 # ifndef PCRE2_STATIC # define PCRE2_EXP_DECL extern __declspec(dllexport) # define PCRE2_EXP_DEFN __declspec(dllexport) # else # define PCRE2_EXP_DECL extern # define PCRE2_EXP_DEFN # endif # else # ifdef __cplusplus # define PCRE2_EXP_DECL extern "C" # else # define PCRE2_EXP_DECL extern # endif # ifndef PCRE2_EXP_DEFN # define PCRE2_EXP_DEFN PCRE2_EXP_DECL # endif # endif #endif /* Include the public PCRE2 header and the definitions of UCP character property values. This must follow the setting of PCRE2_EXP_DECL above. */ #include "pcre2.h" #include "pcre2_ucp.h" /* When PCRE2 is compiled as a C++ library, the subject pointer can be replaced with a custom type. This makes it possible, for example, to allow pcre2_match() to process subject strings that are discontinuous by using a smart pointer class. It must always be possible to inspect all of the subject string in pcre2_match() because of the way it backtracks. */ /* WARNING: This is as yet untested for PCRE2. */ #ifdef CUSTOM_SUBJECT_PTR #undef PCRE2_SPTR #define PCRE2_SPTR CUSTOM_SUBJECT_PTR #endif /* When checking for integer overflow in pcre2_compile(), we need to handle large integers. If a 64-bit integer type is available, we can use that. Otherwise we have to cast to double, which of course requires floating point arithmetic. Handle this by defining a macro for the appropriate type. */ #if defined INT64_MAX || defined int64_t #define INT64_OR_DOUBLE int64_t #else #define INT64_OR_DOUBLE double #endif /* External (in the C sense) functions and tables that are private to the libraries are always referenced using the PRIV macro. This makes it possible for pcre2test.c to include some of the source files from the libraries using a different PRIV definition to avoid name clashes. It also makes it clear in the code that a non-static object is being referenced. */ #ifndef PRIV #define PRIV(name) _pcre2_##name #endif /* When compiling for use with the Virtual Pascal compiler, these functions need to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT option on the command line. */ #ifdef VPCOMPAT #define strlen(s) _strlen(s) #define strncmp(s1,s2,m) _strncmp(s1,s2,m) #define memcmp(s,c,n) _memcmp(s,c,n) #define memcpy(d,s,n) _memcpy(d,s,n) #define memmove(d,s,n) _memmove(d,s,n) #define memset(s,c,n) _memset(s,c,n) #else /* VPCOMPAT */ /* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define a macro that calls an emulating function. */ #ifndef HAVE_MEMMOVE #undef memmove /* Some systems may have a macro */ #define memmove(a, b, c) PRIV(memmove)(a, b, c) #endif /* not HAVE_MEMMOVE */ #endif /* not VPCOMPAT */ /* This is an unsigned int value that no UTF character can ever have, as Unicode doesn't go beyond 0x0010ffff. */ #define NOTACHAR 0xffffffff /* This is the largest valid UTF/Unicode code point. */ #define MAX_UTF_CODE_POINT 0x10ffff /* Compile-time positive error numbers (all except UTF errors, which are negative) start at this value. It should probably never be changed, in case some application is checking for specific numbers. There is a copy of this #define in pcre2posix.c (which now no longer includes this file). Ideally, a way of having a single definition should be found, but as the number is unlikely to change, this is not a pressing issue. The original reason for having a base other than 0 was to keep the absolute values of compile-time and run-time error numbers numerically different, but in the event the code does not rely on this. */ #define COMPILE_ERROR_BASE 100 /* The initial frames vector for remembering backtracking points in pcre2_match() is allocated on the system stack, of this size (bytes). The size must be a multiple of sizeof(PCRE2_SPTR) in all environments, so making it a multiple of 8 is best. Typical frame sizes are a few hundred bytes (it depends on the number of capturing parentheses) so 20KiB handles quite a few frames. A larger vector on the heap is obtained for patterns that need more frames. The maximum size of this can be limited. */ #define START_FRAMES_SIZE 20480 /* Similarly, for DFA matching, an initial internal workspace vector is allocated on the stack. */ #define DFA_START_RWS_SIZE 30720 /* Define the default BSR convention. */ #ifdef BSR_ANYCRLF #define BSR_DEFAULT PCRE2_BSR_ANYCRLF #else #define BSR_DEFAULT PCRE2_BSR_UNICODE #endif /* ---------------- Basic UTF-8 macros ---------------- */ /* These UTF-8 macros are always defined because they are used in pcre2test for handling wide characters in 16-bit and 32-bit modes, even if an 8-bit library is not supported. */ /* Tests whether a UTF-8 code point needs extra bytes to decode. */ #define HASUTF8EXTRALEN(c) ((c) >= 0xc0) /* The following macros were originally written in the form of loops that used data from the tables whose names start with PRIV(utf8_table). They were rewritten by a user so as not to use loops, because in some environments this gives a significant performance advantage, and it seems never to do any harm. */ /* Base macro to pick up the remaining bytes of a UTF-8 character, not advancing the pointer. */ #define GETUTF8(c, eptr) \ { \ if ((c & 0x20u) == 0) \ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \ else if ((c & 0x10u) == 0) \ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \ else if ((c & 0x08u) == 0) \ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \ else if ((c & 0x04u) == 0) \ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \ (eptr[4] & 0x3fu); \ else \ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \ } /* Base macro to pick up the remaining bytes of a UTF-8 character, advancing the pointer. */ #define GETUTF8INC(c, eptr) \ { \ if ((c & 0x20u) == 0) \ c = ((c & 0x1fu) << 6) | (*eptr++ & 0x3fu); \ else if ((c & 0x10u) == 0) \ { \ c = ((c & 0x0fu) << 12) | ((*eptr & 0x3fu) << 6) | (eptr[1] & 0x3fu); \ eptr += 2; \ } \ else if ((c & 0x08u) == 0) \ { \ c = ((c & 0x07u) << 18) | ((*eptr & 0x3fu) << 12) | \ ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \ eptr += 3; \ } \ else if ((c & 0x04u) == 0) \ { \ c = ((c & 0x03u) << 24) | ((*eptr & 0x3fu) << 18) | \ ((eptr[1] & 0x3fu) << 12) | ((eptr[2] & 0x3fu) << 6) | \ (eptr[3] & 0x3fu); \ eptr += 4; \ } \ else \ { \ c = ((c & 0x01u) << 30) | ((*eptr & 0x3fu) << 24) | \ ((eptr[1] & 0x3fu) << 18) | ((eptr[2] & 0x3fu) << 12) | \ ((eptr[3] & 0x3fu) << 6) | (eptr[4] & 0x3fu); \ eptr += 5; \ } \ } /* Base macro to pick up the remaining bytes of a UTF-8 character, not advancing the pointer, incrementing the length. */ #define GETUTF8LEN(c, eptr, len) \ { \ if ((c & 0x20u) == 0) \ { \ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \ len++; \ } \ else if ((c & 0x10u) == 0) \ { \ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \ len += 2; \ } \ else if ((c & 0x08u) == 0) \ {\ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \ len += 3; \ } \ else if ((c & 0x04u) == 0) \ { \ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \ (eptr[4] & 0x3fu); \ len += 4; \ } \ else \ {\ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \ len += 5; \ } \ } /* --------------- Whitespace macros ---------------- */ /* Tests for Unicode horizontal and vertical whitespace characters must check a number of different values. Using a switch statement for this generates the fastest code (no loop, no memory access), and there are several places in the interpreter code where this happens. In order to ensure that all the case lists remain in step, we use macros so that there is only one place where the lists are defined. These values are also required as lists in pcre2_compile.c when processing \h, \H, \v and \V in a character class. The lists are defined in pcre2_tables.c, but macros that define the values are here so that all the definitions are together. The lists must be in ascending character order, terminated by NOTACHAR (which is 0xffffffff). Any changes should ensure that the various macros are kept in step with each other. NOTE: The values also appear in pcre2_jit_compile.c. */ /* -------------- ASCII/Unicode environments -------------- */ #ifndef EBCDIC /* Character U+180E (Mongolian Vowel Separator) is not included in the list of spaces in the Unicode file PropList.txt, and Perl does not recognize it as a space. However, in many other sources it is listed as a space and has been in PCRE (both APIs) for a long time. */ #define HSPACE_LIST \ CHAR_HT, CHAR_SPACE, CHAR_NBSP, \ 0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, \ 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202f, 0x205f, 0x3000, \ NOTACHAR #define HSPACE_MULTIBYTE_CASES \ case 0x1680: /* OGHAM SPACE MARK */ \ case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ \ case 0x2000: /* EN QUAD */ \ case 0x2001: /* EM QUAD */ \ case 0x2002: /* EN SPACE */ \ case 0x2003: /* EM SPACE */ \ case 0x2004: /* THREE-PER-EM SPACE */ \ case 0x2005: /* FOUR-PER-EM SPACE */ \ case 0x2006: /* SIX-PER-EM SPACE */ \ case 0x2007: /* FIGURE SPACE */ \ case 0x2008: /* PUNCTUATION SPACE */ \ case 0x2009: /* THIN SPACE */ \ case 0x200A: /* HAIR SPACE */ \ case 0x202f: /* NARROW NO-BREAK SPACE */ \ case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ \ case 0x3000 /* IDEOGRAPHIC SPACE */ #define HSPACE_BYTE_CASES \ case CHAR_HT: \ case CHAR_SPACE: \ case CHAR_NBSP #define HSPACE_CASES \ HSPACE_BYTE_CASES: \ HSPACE_MULTIBYTE_CASES #define VSPACE_LIST \ CHAR_LF, CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, 0x2028, 0x2029, NOTACHAR #define VSPACE_MULTIBYTE_CASES \ case 0x2028: /* LINE SEPARATOR */ \ case 0x2029 /* PARAGRAPH SEPARATOR */ #define VSPACE_BYTE_CASES \ case CHAR_LF: \ case CHAR_VT: \ case CHAR_FF: \ case CHAR_CR: \ case CHAR_NEL #define VSPACE_CASES \ VSPACE_BYTE_CASES: \ VSPACE_MULTIBYTE_CASES /* -------------- EBCDIC environments -------------- */ #else #define HSPACE_LIST CHAR_HT, CHAR_SPACE, CHAR_NBSP, NOTACHAR #define HSPACE_BYTE_CASES \ case CHAR_HT: \ case CHAR_SPACE: \ case CHAR_NBSP #define HSPACE_CASES HSPACE_BYTE_CASES #ifdef EBCDIC_NL25 #define VSPACE_LIST \ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, CHAR_LF, NOTACHAR #else #define VSPACE_LIST \ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_LF, CHAR_NEL, NOTACHAR #endif #define VSPACE_BYTE_CASES \ case CHAR_LF: \ case CHAR_VT: \ case CHAR_FF: \ case CHAR_CR: \ case CHAR_NEL #define VSPACE_CASES VSPACE_BYTE_CASES #endif /* EBCDIC */ /* -------------- End of whitespace macros -------------- */ /* PCRE2 is able to support several different kinds of newline (CR, LF, CRLF, "any" and "anycrlf" at present). The following macros are used to package up testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various modules to indicate in which datablock the parameters exist, and what the start/end of string field names are. */ #define NLTYPE_FIXED 0 /* Newline is a fixed length string */ #define NLTYPE_ANY 1 /* Newline is any Unicode line ending */ #define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */ /* This macro checks for a newline at the given position */ #define IS_NEWLINE(p) \ ((NLBLOCK->nltype != NLTYPE_FIXED)? \ ((p) < NLBLOCK->PSEND && \ PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \ &(NLBLOCK->nllen), utf)) \ : \ ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ UCHAR21TEST(p) == NLBLOCK->nl[0] && \ (NLBLOCK->nllen == 1 || UCHAR21TEST(p+1) == NLBLOCK->nl[1]) \ ) \ ) /* This macro checks for a newline immediately preceding the given position */ #define WAS_NEWLINE(p) \ ((NLBLOCK->nltype != NLTYPE_FIXED)? \ ((p) > NLBLOCK->PSSTART && \ PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ &(NLBLOCK->nllen), utf)) \ : \ ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ UCHAR21TEST(p - NLBLOCK->nllen) == NLBLOCK->nl[0] && \ (NLBLOCK->nllen == 1 || UCHAR21TEST(p - NLBLOCK->nllen + 1) == NLBLOCK->nl[1]) \ ) \ ) /* Private flags containing information about the compiled pattern. The first three must not be changed, because whichever is set is actually the number of bytes in a code unit in that mode. */ #define PCRE2_MODE8 0x00000001 /* compiled in 8 bit mode */ #define PCRE2_MODE16 0x00000002 /* compiled in 16 bit mode */ #define PCRE2_MODE32 0x00000004 /* compiled in 32 bit mode */ #define PCRE2_FIRSTSET 0x00000010 /* first_code unit is set */ #define PCRE2_FIRSTCASELESS 0x00000020 /* caseless first code unit */ #define PCRE2_FIRSTMAPSET 0x00000040 /* bitmap of first code units is set */ #define PCRE2_LASTSET 0x00000080 /* last code unit is set */ #define PCRE2_LASTCASELESS 0x00000100 /* caseless last code unit */ #define PCRE2_STARTLINE 0x00000200 /* start after \n for multiline */ #define PCRE2_JCHANGED 0x00000400 /* j option used in pattern */ #define PCRE2_HASCRORLF 0x00000800 /* explicit \r or \n in pattern */ #define PCRE2_HASTHEN 0x00001000 /* pattern contains (*THEN) */ #define PCRE2_MATCH_EMPTY 0x00002000 /* pattern can match empty string */ #define PCRE2_BSR_SET 0x00004000 /* BSR was set in the pattern */ #define PCRE2_NL_SET 0x00008000 /* newline was set in the pattern */ #define PCRE2_NOTEMPTY_SET 0x00010000 /* (*NOTEMPTY) used ) keep */ #define PCRE2_NE_ATST_SET 0x00020000 /* (*NOTEMPTY_ATSTART) used) together */ #define PCRE2_DEREF_TABLES 0x00040000 /* release character tables */ #define PCRE2_NOJIT 0x00080000 /* (*NOJIT) used */ #define PCRE2_HASBKPORX 0x00100000 /* contains \P, \p, or \X */ #define PCRE2_DUPCAPUSED 0x00200000 /* contains (?| */ #define PCRE2_HASBKC 0x00400000 /* contains \C */ #define PCRE2_HASACCEPT 0x00800000 /* contains (*ACCEPT) */ #define PCRE2_MODE_MASK (PCRE2_MODE8 | PCRE2_MODE16 | PCRE2_MODE32) /* Values for the matchedby field in a match data block. */ enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */ PCRE2_MATCHEDBY_DFA_INTERPRETER, /* pcre2_dfa_match() */ PCRE2_MATCHEDBY_JIT }; /* pcre2_jit_match() */ /* Values for the flags field in a match data block. */ #define PCRE2_MD_COPIED_SUBJECT 0x01u /* Magic number to provide a small check against being handed junk. */ #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ /* The maximum remaining length of subject we are prepared to search for a req_unit match from an anchored pattern. In 8-bit mode, memchr() is used and is much faster than the search loop that has to be used in 16-bit and 32-bit modes. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define REQ_CU_MAX 5000 #else #define REQ_CU_MAX 2000 #endif /* Offsets for the bitmap tables in the cbits set of tables. Each table contains a set of bits for a class map. Some classes are built by combining these tables. */ #define cbit_space 0 /* [:space:] or \s */ #define cbit_xdigit 32 /* [:xdigit:] */ #define cbit_digit 64 /* [:digit:] or \d */ #define cbit_upper 96 /* [:upper:] */ #define cbit_lower 128 /* [:lower:] */ #define cbit_word 160 /* [:word:] or \w */ #define cbit_graph 192 /* [:graph:] */ #define cbit_print 224 /* [:print:] */ #define cbit_punct 256 /* [:punct:] */ #define cbit_cntrl 288 /* [:cntrl:] */ #define cbit_length 320 /* Length of the cbits table */ /* Bit definitions for entries in the ctypes table. Do not change these values without checking pcre2_jit_compile.c, which has an assertion to ensure that ctype_word has the value 16. */ #define ctype_space 0x01 #define ctype_letter 0x02 #define ctype_lcletter 0x04 #define ctype_digit 0x08 #define ctype_word 0x10 /* alphanumeric or '_' */ /* Offsets of the various tables from the base tables pointer, and total length of the tables. */ #define lcc_offset 0 /* Lower case */ #define fcc_offset 256 /* Flip case */ #define cbits_offset 512 /* Character classes */ #define ctypes_offset (cbits_offset + cbit_length) /* Character types */ #define TABLES_LENGTH (ctypes_offset + 256) /* -------------------- Character and string names ------------------------ */ /* If PCRE2 is to support UTF-8 on EBCDIC platforms, we cannot use normal character constants like '*' because the compiler would emit their EBCDIC code, which is different from their ASCII/UTF-8 code. Instead we define macros for the characters so that they always use the ASCII/UTF-8 code when UTF-8 support is enabled. When UTF-8 support is not enabled, the definitions use character literals. Both character and string versions of each character are needed, and there are some longer strings as well. This means that, on EBCDIC platforms, the PCRE2 library can handle either EBCDIC, or UTF-8, but not both. To support both in the same compiled library would need different lookups depending on whether PCRE2_UTF was set or not. This would make it impossible to use characters in switch/case statements, which would reduce performance. For a theoretical use (which nobody has asked for) in a minority area (EBCDIC platforms), this is not sensible. Any application that did need both could compile two versions of the library, using macros to give the functions distinct names. */ #ifndef SUPPORT_UNICODE /* UTF-8 support is not enabled; use the platform-dependent character literals so that PCRE2 works in both ASCII and EBCDIC environments, but only in non-UTF mode. Newline characters are problematic in EBCDIC. Though it has CR and LF characters, a common practice has been to use its NL (0x15) character as the line terminator in C-like processing environments. However, sometimes the LF (0x25) character is used instead, according to this Unicode document: http://unicode.org/standard/reports/tr13/tr13-5.html PCRE2 defaults EBCDIC NL to 0x15, but has a build-time option to select 0x25 instead. Whichever is *not* chosen is defined as NEL. In both ASCII and EBCDIC environments, CHAR_NL and CHAR_LF are synonyms for the same code point. */ #ifdef EBCDIC #ifndef EBCDIC_NL25 #define CHAR_NL '\x15' #define CHAR_NEL '\x25' #define STR_NL "\x15" #define STR_NEL "\x25" #else #define CHAR_NL '\x25' #define CHAR_NEL '\x15' #define STR_NL "\x25" #define STR_NEL "\x15" #endif #define CHAR_LF CHAR_NL #define STR_LF STR_NL #define CHAR_ESC '\047' #define CHAR_DEL '\007' #define CHAR_NBSP ((unsigned char)'\x41') #define STR_ESC "\047" #define STR_DEL "\007" #else /* Not EBCDIC */ /* In ASCII/Unicode, linefeed is '\n' and we equate this to NL for compatibility. NEL is the Unicode newline character; make sure it is a positive value. */ #define CHAR_LF '\n' #define CHAR_NL CHAR_LF #define CHAR_NEL ((unsigned char)'\x85') #define CHAR_ESC '\033' #define CHAR_DEL '\177' #define CHAR_NBSP ((unsigned char)'\xa0') #define STR_LF "\n" #define STR_NL STR_LF #define STR_NEL "\x85" #define STR_ESC "\033" #define STR_DEL "\177" #endif /* EBCDIC */ /* The remaining definitions work in both environments. */ #define CHAR_NUL '\0' #define CHAR_HT '\t' #define CHAR_VT '\v' #define CHAR_FF '\f' #define CHAR_CR '\r' #define CHAR_BS '\b' #define CHAR_BEL '\a' #define CHAR_SPACE ' ' #define CHAR_EXCLAMATION_MARK '!' #define CHAR_QUOTATION_MARK '"' #define CHAR_NUMBER_SIGN '#' #define CHAR_DOLLAR_SIGN '$' #define CHAR_PERCENT_SIGN '%' #define CHAR_AMPERSAND '&' #define CHAR_APOSTROPHE '\'' #define CHAR_LEFT_PARENTHESIS '(' #define CHAR_RIGHT_PARENTHESIS ')' #define CHAR_ASTERISK '*' #define CHAR_PLUS '+' #define CHAR_COMMA ',' #define CHAR_MINUS '-' #define CHAR_DOT '.' #define CHAR_SLASH '/' #define CHAR_0 '0' #define CHAR_1 '1' #define CHAR_2 '2' #define CHAR_3 '3' #define CHAR_4 '4' #define CHAR_5 '5' #define CHAR_6 '6' #define CHAR_7 '7' #define CHAR_8 '8' #define CHAR_9 '9' #define CHAR_COLON ':' #define CHAR_SEMICOLON ';' #define CHAR_LESS_THAN_SIGN '<' #define CHAR_EQUALS_SIGN '=' #define CHAR_GREATER_THAN_SIGN '>' #define CHAR_QUESTION_MARK '?' #define CHAR_COMMERCIAL_AT '@' #define CHAR_A 'A' #define CHAR_B 'B' #define CHAR_C 'C' #define CHAR_D 'D' #define CHAR_E 'E' #define CHAR_F 'F' #define CHAR_G 'G' #define CHAR_H 'H' #define CHAR_I 'I' #define CHAR_J 'J' #define CHAR_K 'K' #define CHAR_L 'L' #define CHAR_M 'M' #define CHAR_N 'N' #define CHAR_O 'O' #define CHAR_P 'P' #define CHAR_Q 'Q' #define CHAR_R 'R' #define CHAR_S 'S' #define CHAR_T 'T' #define CHAR_U 'U' #define CHAR_V 'V' #define CHAR_W 'W' #define CHAR_X 'X' #define CHAR_Y 'Y' #define CHAR_Z 'Z' #define CHAR_LEFT_SQUARE_BRACKET '[' #define CHAR_BACKSLASH '\\' #define CHAR_RIGHT_SQUARE_BRACKET ']' #define CHAR_CIRCUMFLEX_ACCENT '^' #define CHAR_UNDERSCORE '_' #define CHAR_GRAVE_ACCENT '`' #define CHAR_a 'a' #define CHAR_b 'b' #define CHAR_c 'c' #define CHAR_d 'd' #define CHAR_e 'e' #define CHAR_f 'f' #define CHAR_g 'g' #define CHAR_h 'h' #define CHAR_i 'i' #define CHAR_j 'j' #define CHAR_k 'k' #define CHAR_l 'l' #define CHAR_m 'm' #define CHAR_n 'n' #define CHAR_o 'o' #define CHAR_p 'p' #define CHAR_q 'q' #define CHAR_r 'r' #define CHAR_s 's' #define CHAR_t 't' #define CHAR_u 'u' #define CHAR_v 'v' #define CHAR_w 'w' #define CHAR_x 'x' #define CHAR_y 'y' #define CHAR_z 'z' #define CHAR_LEFT_CURLY_BRACKET '{' #define CHAR_VERTICAL_LINE '|' #define CHAR_RIGHT_CURLY_BRACKET '}' #define CHAR_TILDE '~' #define STR_HT "\t" #define STR_VT "\v" #define STR_FF "\f" #define STR_CR "\r" #define STR_BS "\b" #define STR_BEL "\a" #define STR_SPACE " " #define STR_EXCLAMATION_MARK "!" #define STR_QUOTATION_MARK "\"" #define STR_NUMBER_SIGN "#" #define STR_DOLLAR_SIGN "$" #define STR_PERCENT_SIGN "%" #define STR_AMPERSAND "&" #define STR_APOSTROPHE "'" #define STR_LEFT_PARENTHESIS "(" #define STR_RIGHT_PARENTHESIS ")" #define STR_ASTERISK "*" #define STR_PLUS "+" #define STR_COMMA "," #define STR_MINUS "-" #define STR_DOT "." #define STR_SLASH "/" #define STR_0 "0" #define STR_1 "1" #define STR_2 "2" #define STR_3 "3" #define STR_4 "4" #define STR_5 "5" #define STR_6 "6" #define STR_7 "7" #define STR_8 "8" #define STR_9 "9" #define STR_COLON ":" #define STR_SEMICOLON ";" #define STR_LESS_THAN_SIGN "<" #define STR_EQUALS_SIGN "=" #define STR_GREATER_THAN_SIGN ">" #define STR_QUESTION_MARK "?" #define STR_COMMERCIAL_AT "@" #define STR_A "A" #define STR_B "B" #define STR_C "C" #define STR_D "D" #define STR_E "E" #define STR_F "F" #define STR_G "G" #define STR_H "H" #define STR_I "I" #define STR_J "J" #define STR_K "K" #define STR_L "L" #define STR_M "M" #define STR_N "N" #define STR_O "O" #define STR_P "P" #define STR_Q "Q" #define STR_R "R" #define STR_S "S" #define STR_T "T" #define STR_U "U" #define STR_V "V" #define STR_W "W" #define STR_X "X" #define STR_Y "Y" #define STR_Z "Z" #define STR_LEFT_SQUARE_BRACKET "[" #define STR_BACKSLASH "\\" #define STR_RIGHT_SQUARE_BRACKET "]" #define STR_CIRCUMFLEX_ACCENT "^" #define STR_UNDERSCORE "_" #define STR_GRAVE_ACCENT "`" #define STR_a "a" #define STR_b "b" #define STR_c "c" #define STR_d "d" #define STR_e "e" #define STR_f "f" #define STR_g "g" #define STR_h "h" #define STR_i "i" #define STR_j "j" #define STR_k "k" #define STR_l "l" #define STR_m "m" #define STR_n "n" #define STR_o "o" #define STR_p "p" #define STR_q "q" #define STR_r "r" #define STR_s "s" #define STR_t "t" #define STR_u "u" #define STR_v "v" #define STR_w "w" #define STR_x "x" #define STR_y "y" #define STR_z "z" #define STR_LEFT_CURLY_BRACKET "{" #define STR_VERTICAL_LINE "|" #define STR_RIGHT_CURLY_BRACKET "}" #define STR_TILDE "~" #define STRING_ACCEPT0 "ACCEPT\0" #define STRING_COMMIT0 "COMMIT\0" #define STRING_F0 "F\0" #define STRING_FAIL0 "FAIL\0" #define STRING_MARK0 "MARK\0" #define STRING_PRUNE0 "PRUNE\0" #define STRING_SKIP0 "SKIP\0" #define STRING_THEN "THEN" #define STRING_atomic0 "atomic\0" #define STRING_pla0 "pla\0" #define STRING_plb0 "plb\0" #define STRING_napla0 "napla\0" #define STRING_naplb0 "naplb\0" #define STRING_nla0 "nla\0" #define STRING_nlb0 "nlb\0" #define STRING_sr0 "sr\0" #define STRING_asr0 "asr\0" #define STRING_positive_lookahead0 "positive_lookahead\0" #define STRING_positive_lookbehind0 "positive_lookbehind\0" #define STRING_non_atomic_positive_lookahead0 "non_atomic_positive_lookahead\0" #define STRING_non_atomic_positive_lookbehind0 "non_atomic_positive_lookbehind\0" #define STRING_negative_lookahead0 "negative_lookahead\0" #define STRING_negative_lookbehind0 "negative_lookbehind\0" #define STRING_script_run0 "script_run\0" #define STRING_atomic_script_run "atomic_script_run" #define STRING_alpha0 "alpha\0" #define STRING_lower0 "lower\0" #define STRING_upper0 "upper\0" #define STRING_alnum0 "alnum\0" #define STRING_ascii0 "ascii\0" #define STRING_blank0 "blank\0" #define STRING_cntrl0 "cntrl\0" #define STRING_digit0 "digit\0" #define STRING_graph0 "graph\0" #define STRING_print0 "print\0" #define STRING_punct0 "punct\0" #define STRING_space0 "space\0" #define STRING_word0 "word\0" #define STRING_xdigit "xdigit" #define STRING_DEFINE "DEFINE" #define STRING_VERSION "VERSION" #define STRING_WEIRD_STARTWORD "[:<:]]" #define STRING_WEIRD_ENDWORD "[:>:]]" #define STRING_CR_RIGHTPAR "CR)" #define STRING_LF_RIGHTPAR "LF)" #define STRING_CRLF_RIGHTPAR "CRLF)" #define STRING_ANY_RIGHTPAR "ANY)" #define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" #define STRING_NUL_RIGHTPAR "NUL)" #define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" #define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" #define STRING_UTF8_RIGHTPAR "UTF8)" #define STRING_UTF16_RIGHTPAR "UTF16)" #define STRING_UTF32_RIGHTPAR "UTF32)" #define STRING_UTF_RIGHTPAR "UTF)" #define STRING_UCP_RIGHTPAR "UCP)" #define STRING_NO_AUTO_POSSESS_RIGHTPAR "NO_AUTO_POSSESS)" #define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR "NO_DOTSTAR_ANCHOR)" #define STRING_NO_JIT_RIGHTPAR "NO_JIT)" #define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" #define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)" #define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)" #define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP=" #define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH=" #define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH=" #define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION=" #define STRING_MARK "MARK" #else /* SUPPORT_UNICODE */ /* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode only. */ #define CHAR_HT '\011' #define CHAR_VT '\013' #define CHAR_FF '\014' #define CHAR_CR '\015' #define CHAR_LF '\012' #define CHAR_NL CHAR_LF #define CHAR_NEL ((unsigned char)'\x85') #define CHAR_BS '\010' #define CHAR_BEL '\007' #define CHAR_ESC '\033' #define CHAR_DEL '\177' #define CHAR_NUL '\0' #define CHAR_SPACE '\040' #define CHAR_EXCLAMATION_MARK '\041' #define CHAR_QUOTATION_MARK '\042' #define CHAR_NUMBER_SIGN '\043' #define CHAR_DOLLAR_SIGN '\044' #define CHAR_PERCENT_SIGN '\045' #define CHAR_AMPERSAND '\046' #define CHAR_APOSTROPHE '\047' #define CHAR_LEFT_PARENTHESIS '\050' #define CHAR_RIGHT_PARENTHESIS '\051' #define CHAR_ASTERISK '\052' #define CHAR_PLUS '\053' #define CHAR_COMMA '\054' #define CHAR_MINUS '\055' #define CHAR_DOT '\056' #define CHAR_SLASH '\057' #define CHAR_0 '\060' #define CHAR_1 '\061' #define CHAR_2 '\062' #define CHAR_3 '\063' #define CHAR_4 '\064' #define CHAR_5 '\065' #define CHAR_6 '\066' #define CHAR_7 '\067' #define CHAR_8 '\070' #define CHAR_9 '\071' #define CHAR_COLON '\072' #define CHAR_SEMICOLON '\073' #define CHAR_LESS_THAN_SIGN '\074' #define CHAR_EQUALS_SIGN '\075' #define CHAR_GREATER_THAN_SIGN '\076' #define CHAR_QUESTION_MARK '\077' #define CHAR_COMMERCIAL_AT '\100' #define CHAR_A '\101' #define CHAR_B '\102' #define CHAR_C '\103' #define CHAR_D '\104' #define CHAR_E '\105' #define CHAR_F '\106' #define CHAR_G '\107' #define CHAR_H '\110' #define CHAR_I '\111' #define CHAR_J '\112' #define CHAR_K '\113' #define CHAR_L '\114' #define CHAR_M '\115' #define CHAR_N '\116' #define CHAR_O '\117' #define CHAR_P '\120' #define CHAR_Q '\121' #define CHAR_R '\122' #define CHAR_S '\123' #define CHAR_T '\124' #define CHAR_U '\125' #define CHAR_V '\126' #define CHAR_W '\127' #define CHAR_X '\130' #define CHAR_Y '\131' #define CHAR_Z '\132' #define CHAR_LEFT_SQUARE_BRACKET '\133' #define CHAR_BACKSLASH '\134' #define CHAR_RIGHT_SQUARE_BRACKET '\135' #define CHAR_CIRCUMFLEX_ACCENT '\136' #define CHAR_UNDERSCORE '\137' #define CHAR_GRAVE_ACCENT '\140' #define CHAR_a '\141' #define CHAR_b '\142' #define CHAR_c '\143' #define CHAR_d '\144' #define CHAR_e '\145' #define CHAR_f '\146' #define CHAR_g '\147' #define CHAR_h '\150' #define CHAR_i '\151' #define CHAR_j '\152' #define CHAR_k '\153' #define CHAR_l '\154' #define CHAR_m '\155' #define CHAR_n '\156' #define CHAR_o '\157' #define CHAR_p '\160' #define CHAR_q '\161' #define CHAR_r '\162' #define CHAR_s '\163' #define CHAR_t '\164' #define CHAR_u '\165' #define CHAR_v '\166' #define CHAR_w '\167' #define CHAR_x '\170' #define CHAR_y '\171' #define CHAR_z '\172' #define CHAR_LEFT_CURLY_BRACKET '\173' #define CHAR_VERTICAL_LINE '\174' #define CHAR_RIGHT_CURLY_BRACKET '\175' #define CHAR_TILDE '\176' #define CHAR_NBSP ((unsigned char)'\xa0') #define STR_HT "\011" #define STR_VT "\013" #define STR_FF "\014" #define STR_CR "\015" #define STR_NL "\012" #define STR_BS "\010" #define STR_BEL "\007" #define STR_ESC "\033" #define STR_DEL "\177" #define STR_SPACE "\040" #define STR_EXCLAMATION_MARK "\041" #define STR_QUOTATION_MARK "\042" #define STR_NUMBER_SIGN "\043" #define STR_DOLLAR_SIGN "\044" #define STR_PERCENT_SIGN "\045" #define STR_AMPERSAND "\046" #define STR_APOSTROPHE "\047" #define STR_LEFT_PARENTHESIS "\050" #define STR_RIGHT_PARENTHESIS "\051" #define STR_ASTERISK "\052" #define STR_PLUS "\053" #define STR_COMMA "\054" #define STR_MINUS "\055" #define STR_DOT "\056" #define STR_SLASH "\057" #define STR_0 "\060" #define STR_1 "\061" #define STR_2 "\062" #define STR_3 "\063" #define STR_4 "\064" #define STR_5 "\065" #define STR_6 "\066" #define STR_7 "\067" #define STR_8 "\070" #define STR_9 "\071" #define STR_COLON "\072" #define STR_SEMICOLON "\073" #define STR_LESS_THAN_SIGN "\074" #define STR_EQUALS_SIGN "\075" #define STR_GREATER_THAN_SIGN "\076" #define STR_QUESTION_MARK "\077" #define STR_COMMERCIAL_AT "\100" #define STR_A "\101" #define STR_B "\102" #define STR_C "\103" #define STR_D "\104" #define STR_E "\105" #define STR_F "\106" #define STR_G "\107" #define STR_H "\110" #define STR_I "\111" #define STR_J "\112" #define STR_K "\113" #define STR_L "\114" #define STR_M "\115" #define STR_N "\116" #define STR_O "\117" #define STR_P "\120" #define STR_Q "\121" #define STR_R "\122" #define STR_S "\123" #define STR_T "\124" #define STR_U "\125" #define STR_V "\126" #define STR_W "\127" #define STR_X "\130" #define STR_Y "\131" #define STR_Z "\132" #define STR_LEFT_SQUARE_BRACKET "\133" #define STR_BACKSLASH "\134" #define STR_RIGHT_SQUARE_BRACKET "\135" #define STR_CIRCUMFLEX_ACCENT "\136" #define STR_UNDERSCORE "\137" #define STR_GRAVE_ACCENT "\140" #define STR_a "\141" #define STR_b "\142" #define STR_c "\143" #define STR_d "\144" #define STR_e "\145" #define STR_f "\146" #define STR_g "\147" #define STR_h "\150" #define STR_i "\151" #define STR_j "\152" #define STR_k "\153" #define STR_l "\154" #define STR_m "\155" #define STR_n "\156" #define STR_o "\157" #define STR_p "\160" #define STR_q "\161" #define STR_r "\162" #define STR_s "\163" #define STR_t "\164" #define STR_u "\165" #define STR_v "\166" #define STR_w "\167" #define STR_x "\170" #define STR_y "\171" #define STR_z "\172" #define STR_LEFT_CURLY_BRACKET "\173" #define STR_VERTICAL_LINE "\174" #define STR_RIGHT_CURLY_BRACKET "\175" #define STR_TILDE "\176" #define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0" #define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0" #define STRING_F0 STR_F "\0" #define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0" #define STRING_MARK0 STR_M STR_A STR_R STR_K "\0" #define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0" #define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0" #define STRING_THEN STR_T STR_H STR_E STR_N #define STRING_atomic0 STR_a STR_t STR_o STR_m STR_i STR_c "\0" #define STRING_pla0 STR_p STR_l STR_a "\0" #define STRING_plb0 STR_p STR_l STR_b "\0" #define STRING_napla0 STR_n STR_a STR_p STR_l STR_a "\0" #define STRING_naplb0 STR_n STR_a STR_p STR_l STR_b "\0" #define STRING_nla0 STR_n STR_l STR_a "\0" #define STRING_nlb0 STR_n STR_l STR_b "\0" #define STRING_sr0 STR_s STR_r "\0" #define STRING_asr0 STR_a STR_s STR_r "\0" #define STRING_positive_lookahead0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0" #define STRING_positive_lookbehind0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0" #define STRING_non_atomic_positive_lookahead0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0" #define STRING_non_atomic_positive_lookbehind0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0" #define STRING_negative_lookahead0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0" #define STRING_negative_lookbehind0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0" #define STRING_script_run0 STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n "\0" #define STRING_atomic_script_run STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n #define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0" #define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0" #define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0" #define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0" #define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0" #define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0" #define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0" #define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0" #define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0" #define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0" #define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0" #define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0" #define STRING_word0 STR_w STR_o STR_r STR_d "\0" #define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t #define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E #define STRING_VERSION STR_V STR_E STR_R STR_S STR_I STR_O STR_N #define STRING_WEIRD_STARTWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_LESS_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET #define STRING_WEIRD_ENDWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_GREATER_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET #define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS #define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS #define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS #define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS #define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS #define STRING_UTF16_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS #define STRING_UTF32_RIGHTPAR STR_U STR_T STR_F STR_3 STR_2 STR_RIGHT_PARENTHESIS #define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_RIGHT_PARENTHESIS #define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHESIS #define STRING_NO_AUTO_POSSESS_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_A STR_U STR_T STR_O STR_UNDERSCORE STR_P STR_O STR_S STR_S STR_E STR_S STR_S STR_RIGHT_PARENTHESIS #define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_D STR_O STR_T STR_S STR_T STR_A STR_R STR_UNDERSCORE STR_A STR_N STR_C STR_H STR_O STR_R STR_RIGHT_PARENTHESIS #define STRING_NO_JIT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_J STR_I STR_T STR_RIGHT_PARENTHESIS #define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS #define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS #define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS #define STRING_LIMIT_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN #define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN #define STRING_LIMIT_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN #define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN #define STRING_MARK STR_M STR_A STR_R STR_K #endif /* SUPPORT_UNICODE */ /* -------------------- End of character and string names -------------------*/ /* -------------------- Definitions for compiled patterns -------------------*/ /* Codes for different types of Unicode property */ #define PT_ANY 0 /* Any property - matches all chars */ #define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ #define PT_GC 2 /* Specified general characteristic (e.g. L) */ #define PT_PC 3 /* Specified particular characteristic (e.g. Lu) */ #define PT_SC 4 /* Script (e.g. Han) */ #define PT_ALNUM 5 /* Alphanumeric - the union of L and N */ #define PT_SPACE 6 /* Perl space - Z plus 9,10,12,13 */ #define PT_PXSPACE 7 /* POSIX space - Z plus 9,10,11,12,13 */ #define PT_WORD 8 /* Word - L plus N plus underscore */ #define PT_CLIST 9 /* Pseudo-property: match character list */ #define PT_UCNC 10 /* Universal Character nameable character */ #define PT_TABSIZE 11 /* Size of square table for autopossessify tests */ /* The following special properties are used only in XCLASS items, when POSIX classes are specified and PCRE2_UCP is set - in other words, for Unicode handling of these classes. They are not available via the \p or \P escapes like those in the above list, and so they do not take part in the autopossessifying table. */ #define PT_PXGRAPH 11 /* [:graph:] - characters that mark the paper */ #define PT_PXPRINT 12 /* [:print:] - [:graph:] plus non-control spaces */ #define PT_PXPUNCT 13 /* [:punct:] - punctuation characters */ /* Flag bits and data types for the extended class (OP_XCLASS) for classes that contain characters with values greater than 255. */ #define XCL_NOT 0x01 /* Flag: this is a negative class */ #define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ #define XCL_HASPROP 0x04 /* Flag: property checks are present. */ #define XCL_END 0 /* Marks end of individual items */ #define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ #define XCL_RANGE 2 /* A range (two multibyte chars) follows */ #define XCL_PROP 3 /* Unicode property (2-byte property code follows) */ #define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns 0 for a data character. In the escapes[] table in pcre2_compile.c their values are negated in order to distinguish them from data values. They must appear here in the same order as in the opcode definitions below, up to ESC_z. There's a dummy for OP_ALLANY because it corresponds to "." in DOTALL mode rather than an escape sequence. It is also used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. In non-DOTALL mode, "." behaves like \N. Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in check_escape(). There are tests in the code for an escape greater than ESC_b and less than ESC_Z to detect the types that may be repeated. These are the types that consume characters. If any new escapes are put in between that don't consume a character, that code will have to change. */ enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_g, ESC_k }; /********************** Opcode definitions ******************/ /****** NOTE NOTE NOTE ****** Starting from 1 (i.e. after OP_END), the values up to OP_EOD must correspond in order to the list of escapes immediately above. Furthermore, values up to OP_DOLLM must not be changed without adjusting the table called autoposstab in pcre2_auto_possess.c. Whenever this list is updated, the two macro definitions that follow must be updated to match. The possessification table called "opcode_possessify" in pcre2_compile.c must also be updated, and also the tables called "coptable" and "poptable" in pcre2_dfa_match.c. ****** NOTE NOTE NOTE ******/ /* The values between FIRST_AUTOTAB_OP and LAST_AUTOTAB_RIGHT_OP, inclusive, are used in a table for deciding whether a repeated character type can be auto-possessified. */ #define FIRST_AUTOTAB_OP OP_NOT_DIGIT #define LAST_AUTOTAB_LEFT_OP OP_EXTUNI #define LAST_AUTOTAB_RIGHT_OP OP_DOLLM enum { OP_END, /* 0 End of pattern */ /* Values corresponding to backslashed metacharacters */ OP_SOD, /* 1 Start of data: \A */ OP_SOM, /* 2 Start of match (subject + offset): \G */ OP_SET_SOM, /* 3 Set start of match (\K) */ OP_NOT_WORD_BOUNDARY, /* 4 \B */ OP_WORD_BOUNDARY, /* 5 \b */ OP_NOT_DIGIT, /* 6 \D */ OP_DIGIT, /* 7 \d */ OP_NOT_WHITESPACE, /* 8 \S */ OP_WHITESPACE, /* 9 \s */ OP_NOT_WORDCHAR, /* 10 \W */ OP_WORDCHAR, /* 11 \w */ OP_ANY, /* 12 Match any character except newline (\N) */ OP_ALLANY, /* 13 Match any character */ OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */ OP_NOTPROP, /* 15 \P (not Unicode property) */ OP_PROP, /* 16 \p (Unicode property) */ OP_ANYNL, /* 17 \R (any newline sequence) */ OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */ OP_HSPACE, /* 19 \h (horizontal whitespace) */ OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */ OP_VSPACE, /* 21 \v (vertical whitespace) */ OP_EXTUNI, /* 22 \X (extended Unicode sequence */ OP_EODN, /* 23 End of data or \n at end of data (\Z) */ OP_EOD, /* 24 End of data (\z) */ /* Line end assertions */ OP_DOLL, /* 25 End of line - not multiline */ OP_DOLLM, /* 26 End of line - multiline */ OP_CIRC, /* 27 Start of line - not multiline */ OP_CIRCM, /* 28 Start of line - multiline */ /* Single characters; caseful must precede the caseless ones, and these must remain in this order, and adjacent. */ OP_CHAR, /* 29 Match one character, casefully */ OP_CHARI, /* 30 Match one character, caselessly */ OP_NOT, /* 31 Match one character, not the given one, casefully */ OP_NOTI, /* 32 Match one character, not the given one, caselessly */ /* The following sets of 13 opcodes must always be kept in step because the offset from the first one is used to generate the others. */ /* Repeated characters; caseful must precede the caseless ones */ OP_STAR, /* 33 The maximizing and minimizing versions of */ OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */ OP_PLUS, /* 35 the minimizing one second. */ OP_MINPLUS, /* 36 */ OP_QUERY, /* 37 */ OP_MINQUERY, /* 38 */ OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/ OP_MINUPTO, /* 40 */ OP_EXACT, /* 41 Exactly n matches */ OP_POSSTAR, /* 42 Possessified star, caseful */ OP_POSPLUS, /* 43 Possessified plus, caseful */ OP_POSQUERY, /* 44 Posesssified query, caseful */ OP_POSUPTO, /* 45 Possessified upto, caseful */ /* Repeated characters; caseless must follow the caseful ones */ OP_STARI, /* 46 */ OP_MINSTARI, /* 47 */ OP_PLUSI, /* 48 */ OP_MINPLUSI, /* 49 */ OP_QUERYI, /* 50 */ OP_MINQUERYI, /* 51 */ OP_UPTOI, /* 52 From 0 to n matches of one character, caseless */ OP_MINUPTOI, /* 53 */ OP_EXACTI, /* 54 */ OP_POSSTARI, /* 55 Possessified star, caseless */ OP_POSPLUSI, /* 56 Possessified plus, caseless */ OP_POSQUERYI, /* 57 Posesssified query, caseless */ OP_POSUPTOI, /* 58 Possessified upto, caseless */ /* The negated ones must follow the non-negated ones, and match them */ /* Negated repeated character, caseful; must precede the caseless ones */ OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */ OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */ OP_NOTPLUS, /* 61 the minimizing one second. They must be in */ OP_NOTMINPLUS, /* 62 exactly the same order as those above. */ OP_NOTQUERY, /* 63 */ OP_NOTMINQUERY, /* 64 */ OP_NOTUPTO, /* 65 From 0 to n matches, caseful */ OP_NOTMINUPTO, /* 66 */ OP_NOTEXACT, /* 67 Exactly n matches */ OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */ OP_NOTPOSPLUS, /* 69 */ OP_NOTPOSQUERY, /* 70 */ OP_NOTPOSUPTO, /* 71 */ /* Negated repeated character, caseless; must follow the caseful ones */ OP_NOTSTARI, /* 72 */ OP_NOTMINSTARI, /* 73 */ OP_NOTPLUSI, /* 74 */ OP_NOTMINPLUSI, /* 75 */ OP_NOTQUERYI, /* 76 */ OP_NOTMINQUERYI, /* 77 */ OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */ OP_NOTMINUPTOI, /* 79 */ OP_NOTEXACTI, /* 80 Exactly n matches */ OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */ OP_NOTPOSPLUSI, /* 82 */ OP_NOTPOSQUERYI, /* 83 */ OP_NOTPOSUPTOI, /* 84 */ /* Character types */ OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */ OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */ OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */ OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */ OP_TYPEQUERY, /* 89 */ OP_TYPEMINQUERY, /* 90 */ OP_TYPEUPTO, /* 91 From 0 to n matches */ OP_TYPEMINUPTO, /* 92 */ OP_TYPEEXACT, /* 93 Exactly n matches */ OP_TYPEPOSSTAR, /* 94 Possessified versions */ OP_TYPEPOSPLUS, /* 95 */ OP_TYPEPOSQUERY, /* 96 */ OP_TYPEPOSUPTO, /* 97 */ /* These are used for character classes and back references; only the first six are the same as the sets above. */ OP_CRSTAR, /* 98 The maximizing and minimizing versions of */ OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */ OP_CRPLUS, /* 100 the minimizing one second. These codes must */ OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */ OP_CRQUERY, /* 102 */ OP_CRMINQUERY, /* 103 */ OP_CRRANGE, /* 104 These are different to the three sets above. */ OP_CRMINRANGE, /* 105 */ OP_CRPOSSTAR, /* 106 Possessified versions */ OP_CRPOSPLUS, /* 107 */ OP_CRPOSQUERY, /* 108 */ OP_CRPOSRANGE, /* 109 */ /* End of quantifier opcodes */ OP_CLASS, /* 110 Match a character class, chars < 256 only */ OP_NCLASS, /* 111 Same, but the bitmap was created from a negative class - the difference is relevant only when a character > 255 is encountered. */ OP_XCLASS, /* 112 Extended class for handling > 255 chars within the class. This does both positive and negative. */ OP_REF, /* 113 Match a back reference, casefully */ OP_REFI, /* 114 Match a back reference, caselessly */ OP_DNREF, /* 115 Match a duplicate name backref, casefully */ OP_DNREFI, /* 116 Match a duplicate name backref, caselessly */ OP_RECURSE, /* 117 Match a numbered subpattern (possibly recursive) */ OP_CALLOUT, /* 118 Call out to external function if provided */ OP_CALLOUT_STR, /* 119 Call out with string argument */ OP_ALT, /* 120 Start of alternation */ OP_KET, /* 121 End of group that doesn't have an unbounded repeat */ OP_KETRMAX, /* 122 These two must remain together and in this */ OP_KETRMIN, /* 123 order. They are for groups the repeat for ever. */ OP_KETRPOS, /* 124 Possessive unlimited repeat. */ /* The assertions must come before BRA, CBRA, ONCE, and COND. */ OP_REVERSE, /* 125 Move pointer back - used in lookbehind assertions */ OP_ASSERT, /* 126 Positive lookahead */ OP_ASSERT_NOT, /* 127 Negative lookahead */ OP_ASSERTBACK, /* 128 Positive lookbehind */ OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */ OP_ASSERT_NA, /* 130 Positive non-atomic lookahead */ OP_ASSERTBACK_NA, /* 131 Positive non-atomic lookbehind */ /* ONCE, SCRIPT_RUN, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately after the assertions, with ONCE first, as there's a test for >= ONCE for a subpattern that isn't an assertion. The POS versions must immediately follow the non-POS versions in each case. */ OP_ONCE, /* 132 Atomic group, contains captures */ OP_SCRIPT_RUN, /* 133 Non-capture, but check characters' scripts */ OP_BRA, /* 134 Start of non-capturing bracket */ OP_BRAPOS, /* 135 Ditto, with unlimited, possessive repeat */ OP_CBRA, /* 136 Start of capturing bracket */ OP_CBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */ OP_COND, /* 138 Conditional group */ /* These five must follow the previous five, in the same order. There's a check for >= SBRA to distinguish the two sets. */ OP_SBRA, /* 139 Start of non-capturing bracket, check empty */ OP_SBRAPOS, /* 149 Ditto, with unlimited, possessive repeat */ OP_SCBRA, /* 141 Start of capturing bracket, check empty */ OP_SCBRAPOS, /* 142 Ditto, with unlimited, possessive repeat */ OP_SCOND, /* 143 Conditional group, check empty */ /* The next two pairs must (respectively) be kept together. */ OP_CREF, /* 144 Used to hold a capture number as condition */ OP_DNCREF, /* 145 Used to point to duplicate names as a condition */ OP_RREF, /* 146 Used to hold a recursion number as condition */ OP_DNRREF, /* 147 Used to point to duplicate names as a condition */ OP_FALSE, /* 148 Always false (used by DEFINE and VERSION) */ OP_TRUE, /* 149 Always true (used by VERSION) */ OP_BRAZERO, /* 150 These two must remain together and in this */ OP_BRAMINZERO, /* 151 order. */ OP_BRAPOSZERO, /* 152 */ /* These are backtracking control verbs */ OP_MARK, /* 153 always has an argument */ OP_PRUNE, /* 154 */ OP_PRUNE_ARG, /* 155 same, but with argument */ OP_SKIP, /* 156 */ OP_SKIP_ARG, /* 157 same, but with argument */ OP_THEN, /* 158 */ OP_THEN_ARG, /* 159 same, but with argument */ OP_COMMIT, /* 160 */ OP_COMMIT_ARG, /* 161 same, but with argument */ /* These are forced failure and success verbs. FAIL and ACCEPT do accept an argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL) without the need for a special opcode. */ OP_FAIL, /* 162 */ OP_ACCEPT, /* 163 */ OP_ASSERT_ACCEPT, /* 164 Used inside assertions */ OP_CLOSE, /* 165 Used before OP_ACCEPT to close open captures */ /* This is used to skip a subpattern with a {0} quantifier */ OP_SKIPZERO, /* 166 */ /* This is used to identify a DEFINE group during compilation so that it can be checked for having only one branch. It is changed to OP_FALSE before compilation finishes. */ OP_DEFINE, /* 167 */ /* This is not an opcode, but is used to check that tables indexed by opcode are the correct length, in order to catch updating errors - there have been some in the past. */ OP_TABLE_LENGTH }; /* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro definitions that follow must also be updated to match. There are also tables called "opcode_possessify" in pcre2_compile.c and "coptable" and "poptable" in pcre2_dfa_match.c that must be updated. */ /* This macro defines textual names for all the opcodes. These are used only for debugging, and some of them are only partial names. The macro is referenced only in pcre2_printint.c, which fills out the full names in many cases (and in some cases doesn't actually use these names at all). */ #define OP_NAME_LIST \ "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \ "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ "extuni", "\\Z", "\\z", \ "$", "$", "^", "^", "char", "chari", "not", "noti", \ "*", "*?", "+", "+?", "?", "??", \ "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", \ "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", \ "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", \ "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ "*+","++", "?+", "{", \ "*", "*?", "+", "+?", "?", "??", "{", "{", \ "*+","++", "?+", "{", \ "class", "nclass", "xclass", "Ref", "Refi", "DnRef", "DnRefi", \ "Recurse", "Callout", "CalloutStr", \ "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ "Reverse", "Assert", "Assert not", \ "Assert back", "Assert back not", \ "Non-atomic assert", "Non-atomic assert back", \ "Once", \ "Script run", \ "Bra", "BraPos", "CBra", "CBraPos", \ "Cond", \ "SBra", "SBraPos", "SCBra", "SCBraPos", \ "SCond", \ "Cond ref", "Cond dnref", "Cond rec", "Cond dnrec", \ "Cond false", "Cond true", \ "Brazero", "Braminzero", "Braposzero", \ "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ "*THEN", "*THEN", "*COMMIT", "*COMMIT", "*FAIL", \ "*ACCEPT", "*ASSERT_ACCEPT", \ "Close", "Skip zero", "Define" /* This macro defines the length of fixed length operations in the compiled regex. The lengths are used when searching for specific things, and also in the debugging printing of a compiled regex. We use a macro so that it can be defined close to the definitions of the opcodes themselves. As things have been extended, some of these are no longer fixed lenths, but are minima instead. For example, the length of a single-character repeat may vary in UTF-8 mode. The code that uses this table must know about such things. */ #define OP_LENGTHS \ 1, /* End */ \ 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ 1, 1, 1, /* Any, AllAny, Anybyte */ \ 3, 3, /* \P, \p */ \ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ 1, /* \X */ \ 1, 1, 1, 1, 1, 1, /* \Z, \z, $, $M ^, ^M */ \ 2, /* Char - the minimum length */ \ 2, /* Chari - the minimum length */ \ 2, /* not */ \ 2, /* noti */ \ /* Positive single-char repeats ** These are */ \ 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode */ \ 2+IMM2_SIZE, /* exact */ \ 2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ */ \ 2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 */ \ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I */ \ 2+IMM2_SIZE, /* exact I */ \ 2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ \ /* Negative single-char repeats - only for chars < 256 */ \ 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto */ \ 2+IMM2_SIZE, /* NOT exact */ \ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto */ \ 2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I */ \ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I */ \ 2+IMM2_SIZE, /* NOT exact I */ \ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I */ \ /* Positive type repeats */ \ 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto */ \ 2+IMM2_SIZE, /* Type exact */ \ 2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ */ \ /* Character class & ref repeats */ \ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE */ \ 1, 1, 1, 1+2*IMM2_SIZE, /* Possessive *+, ++, ?+, CRPOSRANGE */ \ 1+(32/sizeof(PCRE2_UCHAR)), /* CLASS */ \ 1+(32/sizeof(PCRE2_UCHAR)), /* NCLASS */ \ 0, /* XCLASS - variable length */ \ 1+IMM2_SIZE, /* REF */ \ 1+IMM2_SIZE, /* REFI */ \ 1+2*IMM2_SIZE, /* DNREF */ \ 1+2*IMM2_SIZE, /* DNREFI */ \ 1+LINK_SIZE, /* RECURSE */ \ 1+2*LINK_SIZE+1, /* CALLOUT */ \ 0, /* CALLOUT_STR - variable length */ \ 1+LINK_SIZE, /* Alt */ \ 1+LINK_SIZE, /* Ket */ \ 1+LINK_SIZE, /* KetRmax */ \ 1+LINK_SIZE, /* KetRmin */ \ 1+LINK_SIZE, /* KetRpos */ \ 1+LINK_SIZE, /* Reverse */ \ 1+LINK_SIZE, /* Assert */ \ 1+LINK_SIZE, /* Assert not */ \ 1+LINK_SIZE, /* Assert behind */ \ 1+LINK_SIZE, /* Assert behind not */ \ 1+LINK_SIZE, /* NA Assert */ \ 1+LINK_SIZE, /* NA Assert behind */ \ 1+LINK_SIZE, /* ONCE */ \ 1+LINK_SIZE, /* SCRIPT_RUN */ \ 1+LINK_SIZE, /* BRA */ \ 1+LINK_SIZE, /* BRAPOS */ \ 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \ 1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS */ \ 1+LINK_SIZE, /* COND */ \ 1+LINK_SIZE, /* SBRA */ \ 1+LINK_SIZE, /* SBRAPOS */ \ 1+LINK_SIZE+IMM2_SIZE, /* SCBRA */ \ 1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS */ \ 1+LINK_SIZE, /* SCOND */ \ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* CREF, DNCREF */ \ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* RREF, DNRREF */ \ 1, 1, /* FALSE, TRUE */ \ 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ \ 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \ 1, 3, /* SKIP, SKIP_ARG */ \ 1, 3, /* THEN, THEN_ARG */ \ 1, 3, /* COMMIT, COMMIT_ARG */ \ 1, 1, 1, /* FAIL, ACCEPT, ASSERT_ACCEPT */ \ 1+IMM2_SIZE, 1, /* CLOSE, SKIPZERO */ \ 1 /* DEFINE */ /* A magic value for OP_RREF to indicate the "any recursion" condition. */ #define RREF_ANY 0xffff /* ---------- Private structures that are mode-independent. ---------- */ /* Structure to hold data for custom memory management. */ typedef struct pcre2_memctl { void * (*malloc)(size_t, void *); void (*free)(void *, void *); void *memory_data; } pcre2_memctl; /* Structure for building a chain of open capturing subpatterns during compiling, so that instructions to close them can be compiled when (*ACCEPT) is encountered. */ typedef struct open_capitem { struct open_capitem *next; /* Chain link */ uint16_t number; /* Capture number */ uint16_t assert_depth; /* Assertion depth when opened */ } open_capitem; /* Layout of the UCP type table that translates property names into types and codes. Each entry used to point directly to a name, but to reduce the number of relocations in shared libraries, it now has an offset into a single string instead. */ typedef struct { uint16_t name_offset; uint16_t type; uint16_t value; } ucp_type_table; /* Unicode character database (UCD) record format */ typedef struct { uint8_t script; /* ucp_Arabic, etc. */ uint8_t chartype; /* ucp_Cc, etc. (general categories) */ uint8_t gbprop; /* ucp_gbControl, etc. (grapheme break property) */ uint8_t caseset; /* offset to multichar other cases or zero */ int32_t other_case; /* offset to other case, or zero if none */ int16_t scriptx; /* script extension value */ int16_t dummy; /* spare - to round to multiple of 4 bytes */ } ucd_record; /* UCD access macros */ #define UCD_BLOCK_SIZE 128 #define REAL_GET_UCD(ch) (PRIV(ucd_records) + \ PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \ UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE]) #if PCRE2_CODE_UNIT_WIDTH == 32 #define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \ PRIV(dummy_ucd_record) : REAL_GET_UCD(ch)) #else #define GET_UCD(ch) REAL_GET_UCD(ch) #endif #define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype #define UCD_SCRIPT(ch) GET_UCD(ch)->script #define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] #define UCD_GRAPHBREAK(ch) GET_UCD(ch)->gbprop #define UCD_CASESET(ch) GET_UCD(ch)->caseset #define UCD_OTHERCASE(ch) ((uint32_t)((int)ch + (int)(GET_UCD(ch)->other_case))) #define UCD_SCRIPTX(ch) GET_UCD(ch)->scriptx /* Header for serialized pcre2 codes. */ typedef struct pcre2_serialized_data { uint32_t magic; uint32_t version; uint32_t config; int32_t number_of_codes; } pcre2_serialized_data; /* ----------------- Items that need PCRE2_CODE_UNIT_WIDTH ----------------- */ /* When this file is included by pcre2test, PCRE2_CODE_UNIT_WIDTH is defined as 0, so the following items are omitted. */ #if defined PCRE2_CODE_UNIT_WIDTH && PCRE2_CODE_UNIT_WIDTH != 0 /* EBCDIC is supported only for the 8-bit library. */ #if defined EBCDIC && PCRE2_CODE_UNIT_WIDTH != 8 #error EBCDIC is not supported for the 16-bit or 32-bit libraries #endif /* This is the largest non-UTF code point. */ #define MAX_NON_UTF_CHAR (0xffffffffU >> (32 - PCRE2_CODE_UNIT_WIDTH)) /* Internal shared data tables and variables. These are used by more than one of the exported public functions. They have to be "external" in the C sense, but are not part of the PCRE2 public API. Although the data for some of them is identical in all libraries, they must have different names so that multiple libraries can be simultaneously linked to a single application. However, UTF-8 tables are needed only when compiling the 8-bit library. */ #if PCRE2_CODE_UNIT_WIDTH == 8 extern const int PRIV(utf8_table1)[]; extern const int PRIV(utf8_table1_size); extern const int PRIV(utf8_table2)[]; extern const int PRIV(utf8_table3)[]; extern const uint8_t PRIV(utf8_table4)[]; #endif #define _pcre2_OP_lengths PCRE2_SUFFIX(_pcre2_OP_lengths_) #define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_) #define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_) #define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_) #define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_) #define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_) #define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_) #if PCRE2_CODE_UNIT_WIDTH == 32 #define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_) #endif #define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_) #define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_) #define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_) #define _pcre2_ucd_digit_sets PCRE2_SUFFIX(_pcre2_ucd_digit_sets_) #define _pcre2_ucd_script_sets PCRE2_SUFFIX(_pcre2_ucd_script_sets_) #define _pcre2_ucd_records PCRE2_SUFFIX(_pcre2_ucd_records_) #define _pcre2_ucd_stage1 PCRE2_SUFFIX(_pcre2_ucd_stage1_) #define _pcre2_ucd_stage2 PCRE2_SUFFIX(_pcre2_ucd_stage2_) #define _pcre2_ucp_gbtable PCRE2_SUFFIX(_pcre2_ucp_gbtable_) #define _pcre2_ucp_gentype PCRE2_SUFFIX(_pcre2_ucp_gentype_) #define _pcre2_ucp_typerange PCRE2_SUFFIX(_pcre2_ucp_typerange_) #define _pcre2_unicode_version PCRE2_SUFFIX(_pcre2_unicode_version_) #define _pcre2_utt PCRE2_SUFFIX(_pcre2_utt_) #define _pcre2_utt_names PCRE2_SUFFIX(_pcre2_utt_names_) #define _pcre2_utt_size PCRE2_SUFFIX(_pcre2_utt_size_) extern const uint8_t PRIV(OP_lengths)[]; extern const uint32_t PRIV(callout_end_delims)[]; extern const uint32_t PRIV(callout_start_delims)[]; extern const pcre2_compile_context PRIV(default_compile_context); extern const pcre2_convert_context PRIV(default_convert_context); extern const pcre2_match_context PRIV(default_match_context); extern const uint8_t PRIV(default_tables)[]; extern const uint32_t PRIV(hspace_list)[]; extern const uint32_t PRIV(vspace_list)[]; extern const uint32_t PRIV(ucd_caseless_sets)[]; extern const uint32_t PRIV(ucd_digit_sets)[]; extern const uint8_t PRIV(ucd_script_sets)[]; extern const ucd_record PRIV(ucd_records)[]; #if PCRE2_CODE_UNIT_WIDTH == 32 extern const ucd_record PRIV(dummy_ucd_record)[]; #endif extern const uint16_t PRIV(ucd_stage1)[]; extern const uint16_t PRIV(ucd_stage2)[]; extern const uint32_t PRIV(ucp_gbtable)[]; extern const uint32_t PRIV(ucp_gentype)[]; #ifdef SUPPORT_JIT extern const int PRIV(ucp_typerange)[]; #endif extern const char *PRIV(unicode_version); extern const ucp_type_table PRIV(utt)[]; extern const char PRIV(utt_names)[]; extern const size_t PRIV(utt_size); /* Mode-dependent macros and hidden and private structures are defined in a separate file so that pcre2test can include them at all supported widths. When compiling the library, PCRE2_CODE_UNIT_WIDTH will be defined, and we can include them at the appropriate width, after setting up suffix macros for the private structures. */ #define branch_chain PCRE2_SUFFIX(branch_chain_) #define compile_block PCRE2_SUFFIX(compile_block_) #define dfa_match_block PCRE2_SUFFIX(dfa_match_block_) #define match_block PCRE2_SUFFIX(match_block_) #define named_group PCRE2_SUFFIX(named_group_) #include "pcre2_intmodedep.h" /* Private "external" functions. These are internal functions that are called from modules other than the one in which they are defined. They have to be "external" in the C sense, but are not part of the PCRE2 public API. They are not referenced from pcre2test, and must not be defined when no code unit width is available. */ #define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_) #define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_) #define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_) #define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_) #define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_) #define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_) #define _pcre2_jit_free PCRE2_SUFFIX(_pcre2_jit_free_) #define _pcre2_jit_get_size PCRE2_SUFFIX(_pcre2_jit_get_size_) #define _pcre2_jit_get_target PCRE2_SUFFIX(_pcre2_jit_get_target_) #define _pcre2_memctl_malloc PCRE2_SUFFIX(_pcre2_memctl_malloc_) #define _pcre2_ord2utf PCRE2_SUFFIX(_pcre2_ord2utf_) #define _pcre2_script_run PCRE2_SUFFIX(_pcre2_script_run_) #define _pcre2_strcmp PCRE2_SUFFIX(_pcre2_strcmp_) #define _pcre2_strcmp_c8 PCRE2_SUFFIX(_pcre2_strcmp_c8_) #define _pcre2_strcpy_c8 PCRE2_SUFFIX(_pcre2_strcpy_c8_) #define _pcre2_strlen PCRE2_SUFFIX(_pcre2_strlen_) #define _pcre2_strncmp PCRE2_SUFFIX(_pcre2_strncmp_) #define _pcre2_strncmp_c8 PCRE2_SUFFIX(_pcre2_strncmp_c8_) #define _pcre2_study PCRE2_SUFFIX(_pcre2_study_) #define _pcre2_valid_utf PCRE2_SUFFIX(_pcre2_valid_utf_) #define _pcre2_was_newline PCRE2_SUFFIX(_pcre2_was_newline_) #define _pcre2_xclass PCRE2_SUFFIX(_pcre2_xclass_) extern int _pcre2_auto_possessify(PCRE2_UCHAR *, const compile_block *); extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *, int *, uint32_t, uint32_t, BOOL, compile_block *); extern PCRE2_SPTR _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR, BOOL, int *); extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int); extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); extern void _pcre2_jit_free_rodata(void *, void *); extern void _pcre2_jit_free(void *, pcre2_memctl *); extern size_t _pcre2_jit_get_size(void *); const char * _pcre2_jit_get_target(void); extern void * _pcre2_memctl_malloc(size_t, pcre2_memctl *); extern unsigned int _pcre2_ord2utf(uint32_t, PCRE2_UCHAR *); extern BOOL _pcre2_script_run(PCRE2_SPTR, PCRE2_SPTR, BOOL); extern int _pcre2_strcmp(PCRE2_SPTR, PCRE2_SPTR); extern int _pcre2_strcmp_c8(PCRE2_SPTR, const char *); extern PCRE2_SIZE _pcre2_strcpy_c8(PCRE2_UCHAR *, const char *); extern PCRE2_SIZE _pcre2_strlen(PCRE2_SPTR); extern int _pcre2_strncmp(PCRE2_SPTR, PCRE2_SPTR, size_t); extern int _pcre2_strncmp_c8(PCRE2_SPTR, const char *, size_t); extern int _pcre2_study(pcre2_real_code *); extern int _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *); extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL); /* This function is needed only when memmove() is not available. */ #if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) #define _pcre2_memmove PCRE2_SUFFIX(_pcre2_memmove) extern void * _pcre2_memmove(void *, const void *, size_t); #endif #endif /* PCRE2_CODE_UNIT_WIDTH */ #endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */ /* End of pcre2_internal.h */ vfu-4.22/vstring/pcre2/pcre2_context.c0000644000175000017500000003542014145574056016264 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Default malloc/free functions * *************************************************/ /* Ignore the "user data" argument in each case. */ static void *default_malloc(size_t size, void *data) { (void)data; return malloc(size); } static void default_free(void *block, void *data) { (void)data; free(block); } /************************************************* * Get a block and save memory control * *************************************************/ /* This internal function is called to get a block of memory in which the memory control data is to be stored at the start for future use. Arguments: size amount of memory required memctl pointer to a memctl block or NULL Returns: pointer to memory or NULL on failure */ extern void * PRIV(memctl_malloc)(size_t size, pcre2_memctl *memctl) { pcre2_memctl *newmemctl; void *yield = (memctl == NULL)? malloc(size) : memctl->malloc(size, memctl->memory_data); if (yield == NULL) return NULL; newmemctl = (pcre2_memctl *)yield; if (memctl == NULL) { newmemctl->malloc = default_malloc; newmemctl->free = default_free; newmemctl->memory_data = NULL; } else *newmemctl = *memctl; return yield; } /************************************************* * Create and initialize contexts * *************************************************/ /* Initializing for compile and match contexts is done in separate, private functions so that these can be called from functions such as pcre2_compile() when an external context is not supplied. The initializing functions have an option to set up default memory management. */ PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION pcre2_general_context_create(void *(*private_malloc)(size_t, void *), void (*private_free)(void *, void *), void *memory_data) { pcre2_general_context *gcontext; if (private_malloc == NULL) private_malloc = default_malloc; if (private_free == NULL) private_free = default_free; gcontext = private_malloc(sizeof(pcre2_real_general_context), memory_data); if (gcontext == NULL) return NULL; gcontext->memctl.malloc = private_malloc; gcontext->memctl.free = private_free; gcontext->memctl.memory_data = memory_data; return gcontext; } /* A default compile context is set up to save having to initialize at run time when no context is supplied to the compile function. */ const pcre2_compile_context PRIV(default_compile_context) = { { default_malloc, default_free, NULL }, /* Default memory handling */ NULL, /* Stack guard */ NULL, /* Stack guard data */ PRIV(default_tables), /* Character tables */ PCRE2_UNSET, /* Max pattern length */ BSR_DEFAULT, /* Backslash R default */ NEWLINE_DEFAULT, /* Newline convention */ PARENS_NEST_LIMIT, /* As it says */ 0 }; /* Extra options */ /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION pcre2_compile_context_create(pcre2_general_context *gcontext) { pcre2_compile_context *ccontext = PRIV(memctl_malloc)( sizeof(pcre2_real_compile_context), (pcre2_memctl *)gcontext); if (ccontext == NULL) return NULL; *ccontext = PRIV(default_compile_context); if (gcontext != NULL) *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext); return ccontext; } /* A default match context is set up to save having to initialize at run time when no context is supplied to a match function. */ const pcre2_match_context PRIV(default_match_context) = { { default_malloc, default_free, NULL }, #ifdef SUPPORT_JIT NULL, /* JIT callback */ NULL, /* JIT callback data */ #endif NULL, /* Callout function */ NULL, /* Callout data */ NULL, /* Substitute callout function */ NULL, /* Substitute callout data */ PCRE2_UNSET, /* Offset limit */ HEAP_LIMIT, MATCH_LIMIT, MATCH_LIMIT_DEPTH }; /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION pcre2_match_context_create(pcre2_general_context *gcontext) { pcre2_match_context *mcontext = PRIV(memctl_malloc)( sizeof(pcre2_real_match_context), (pcre2_memctl *)gcontext); if (mcontext == NULL) return NULL; *mcontext = PRIV(default_match_context); if (gcontext != NULL) *((pcre2_memctl *)mcontext) = *((pcre2_memctl *)gcontext); return mcontext; } /* A default convert context is set up to save having to initialize at run time when no context is supplied to the convert function. */ const pcre2_convert_context PRIV(default_convert_context) = { { default_malloc, default_free, NULL }, /* Default memory handling */ #ifdef _WIN32 CHAR_BACKSLASH, /* Default path separator */ CHAR_GRAVE_ACCENT /* Default escape character */ #else /* Not Windows */ CHAR_SLASH, /* Default path separator */ CHAR_BACKSLASH /* Default escape character */ #endif }; /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION pcre2_convert_context_create(pcre2_general_context *gcontext) { pcre2_convert_context *ccontext = PRIV(memctl_malloc)( sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext); if (ccontext == NULL) return NULL; *ccontext = PRIV(default_convert_context); if (gcontext != NULL) *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext); return ccontext; } /************************************************* * Context copy functions * *************************************************/ PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION pcre2_general_context_copy(pcre2_general_context *gcontext) { pcre2_general_context *new = gcontext->memctl.malloc(sizeof(pcre2_real_general_context), gcontext->memctl.memory_data); if (new == NULL) return NULL; memcpy(new, gcontext, sizeof(pcre2_real_general_context)); return new; } PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION pcre2_compile_context_copy(pcre2_compile_context *ccontext) { pcre2_compile_context *new = ccontext->memctl.malloc(sizeof(pcre2_real_compile_context), ccontext->memctl.memory_data); if (new == NULL) return NULL; memcpy(new, ccontext, sizeof(pcre2_real_compile_context)); return new; } PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION pcre2_match_context_copy(pcre2_match_context *mcontext) { pcre2_match_context *new = mcontext->memctl.malloc(sizeof(pcre2_real_match_context), mcontext->memctl.memory_data); if (new == NULL) return NULL; memcpy(new, mcontext, sizeof(pcre2_real_match_context)); return new; } PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION pcre2_convert_context_copy(pcre2_convert_context *ccontext) { pcre2_convert_context *new = ccontext->memctl.malloc(sizeof(pcre2_real_convert_context), ccontext->memctl.memory_data); if (new == NULL) return NULL; memcpy(new, ccontext, sizeof(pcre2_real_convert_context)); return new; } /************************************************* * Context free functions * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_general_context_free(pcre2_general_context *gcontext) { if (gcontext != NULL) gcontext->memctl.free(gcontext, gcontext->memctl.memory_data); } PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_compile_context_free(pcre2_compile_context *ccontext) { if (ccontext != NULL) ccontext->memctl.free(ccontext, ccontext->memctl.memory_data); } PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_match_context_free(pcre2_match_context *mcontext) { if (mcontext != NULL) mcontext->memctl.free(mcontext, mcontext->memctl.memory_data); } PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_convert_context_free(pcre2_convert_context *ccontext) { if (ccontext != NULL) ccontext->memctl.free(ccontext, ccontext->memctl.memory_data); } /************************************************* * Set values in contexts * *************************************************/ /* All these functions return 0 for success or PCRE2_ERROR_BADDATA if invalid data is given. Only some of the functions are able to test the validity of the data. */ /* ------------ Compile context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_character_tables(pcre2_compile_context *ccontext, const uint8_t *tables) { ccontext->tables = tables; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_bsr(pcre2_compile_context *ccontext, uint32_t value) { switch(value) { case PCRE2_BSR_ANYCRLF: case PCRE2_BSR_UNICODE: ccontext->bsr_convention = value; return 0; default: return PCRE2_ERROR_BADDATA; } } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_max_pattern_length(pcre2_compile_context *ccontext, PCRE2_SIZE length) { ccontext->max_pattern_length = length; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_newline(pcre2_compile_context *ccontext, uint32_t newline) { switch(newline) { case PCRE2_NEWLINE_CR: case PCRE2_NEWLINE_LF: case PCRE2_NEWLINE_CRLF: case PCRE2_NEWLINE_ANY: case PCRE2_NEWLINE_ANYCRLF: case PCRE2_NEWLINE_NUL: ccontext->newline_convention = newline; return 0; default: return PCRE2_ERROR_BADDATA; } } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_parens_nest_limit(pcre2_compile_context *ccontext, uint32_t limit) { ccontext->parens_nest_limit = limit; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options) { ccontext->extra_options = options; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext, int (*guard)(uint32_t, void *), void *user_data) { ccontext->stack_guard = guard; ccontext->stack_guard_data = user_data; return 0; } /* ------------ Match context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_callout(pcre2_match_context *mcontext, int (*callout)(pcre2_callout_block *, void *), void *callout_data) { mcontext->callout = callout; mcontext->callout_data = callout_data; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_substitute_callout(pcre2_match_context *mcontext, int (*substitute_callout)(pcre2_substitute_callout_block *, void *), void *substitute_callout_data) { mcontext->substitute_callout = substitute_callout; mcontext->substitute_callout_data = substitute_callout_data; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit) { mcontext->heap_limit = limit; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit) { mcontext->match_limit = limit; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit) { mcontext->depth_limit = limit; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit) { mcontext->offset_limit = limit; return 0; } /* This function became obsolete at release 10.30. It is kept as a synonym for backwards compatibility. */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit) { return pcre2_set_depth_limit(mcontext, limit); } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_memory_management(pcre2_match_context *mcontext, void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *), void *mydata) { (void)mcontext; (void)mymalloc; (void)myfree; (void)mydata; return 0; } /* ------------ Convert context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator) { if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH && separator != CHAR_DOT) return PCRE2_ERROR_BADDATA; ccontext->glob_separator = separator; return 0; } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape) { if (escape > 255 || (escape != 0 && !ispunct(escape))) return PCRE2_ERROR_BADDATA; ccontext->glob_escape = escape; return 0; } /* End of pcre2_context.c */ vfu-4.22/vstring/pcre2/mm.conf0000644000175000017500000000143414145574056014617 0ustar cadecade############################################################################ CC = gcc LD = gcc ############################################################################ # make pcre2 library [libpcre2.a] CCFLAGS = -DHAVE_CONFIG_H -DPCRE2_CODE_UNIT_WIDTH=8 -fPIC -I. -O2 $(CCDEF) LDFLAGS = $(LDDEF) SRC = pcre2_auto_possess.c pcre2_compile.c pcre2_config.c pcre2_context.c pcre2_convert.c pcre2_dfa_match.c pcre2_error.c pcre2_extuni.c pcre2_find_bracket.c pcre2_fuzzsupport.c pcre2_jit_compile.c pcre2_maketables.c pcre2_match.c pcre2_match_data.c pcre2_newline.c pcre2_ord2utf.c pcre2_pattern_info.c pcre2posix.c pcre2_script_run.c pcre2_serialize.c pcre2_string_utils.c pcre2_study.c pcre2_substitute.c pcre2_substring.c pcre2_tables.c pcre2_ucd.c pcre2_valid_utf.c pcre2_xclass.c vfu-4.22/vstring/pcre2/pcre2_find_bracket.c0000644000175000017500000001525114145574056017213 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains a single function that scans through a compiled pattern until it finds a capturing bracket with the given number, or, if the number is negative, an instance of OP_REVERSE for a lookbehind. The function is called from pcre2_compile.c and also from pcre2_study.c when finding the minimum matching length. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Scan compiled regex for specific bracket * *************************************************/ /* Arguments: code points to start of expression utf TRUE in UTF mode number the required bracket number or negative to find a lookbehind Returns: pointer to the opcode for the bracket, or NULL if not found */ PCRE2_SPTR PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number) { for (;;) { PCRE2_UCHAR c = *code; if (c == OP_END) return NULL; /* XCLASS is used for classes that cannot be represented just by a bit map. This includes negated single high-valued characters. CALLOUT_STR is used for callouts with string arguments. In both cases the length in the table is zero; the actual length is stored in the compiled code. */ if (c == OP_XCLASS) code += GET(code, 1); else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); /* Handle lookbehind */ else if (c == OP_REVERSE) { if (number < 0) return (PCRE2_UCHAR *)code; code += PRIV(OP_lengths)[c]; } /* Handle capturing bracket */ else if (c == OP_CBRA || c == OP_SCBRA || c == OP_CBRAPOS || c == OP_SCBRAPOS) { int n = (int)GET2(code, 1+LINK_SIZE); if (n == number) return (PCRE2_UCHAR *)code; code += PRIV(OP_lengths)[c]; } /* Otherwise, we can get the item's length from the table, except that for repeated character types, we have to test for \p and \P, which have an extra two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we must add in its length. */ else { switch(c) { case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; break; case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: case OP_TYPEPOSUPTO: if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: code += code[1]; break; } /* Add in the fixed length from the table */ code += PRIV(OP_lengths)[c]; /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be followed by a multi-byte character. The length in the table is a minimum, so we have to arrange to skip the extra bytes. */ #ifdef MAYBE_UTF_MULTI if (utf) switch(c) { case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_EXACT: case OP_EXACTI: case OP_NOTEXACT: case OP_NOTEXACTI: case OP_UPTO: case OP_UPTOI: case OP_NOTUPTO: case OP_NOTUPTOI: case OP_MINUPTO: case OP_MINUPTOI: case OP_NOTMINUPTO: case OP_NOTMINUPTOI: case OP_POSUPTO: case OP_POSUPTOI: case OP_NOTPOSUPTO: case OP_NOTPOSUPTOI: case OP_STAR: case OP_STARI: case OP_NOTSTAR: case OP_NOTSTARI: case OP_MINSTAR: case OP_MINSTARI: case OP_NOTMINSTAR: case OP_NOTMINSTARI: case OP_POSSTAR: case OP_POSSTARI: case OP_NOTPOSSTAR: case OP_NOTPOSSTARI: case OP_PLUS: case OP_PLUSI: case OP_NOTPLUS: case OP_NOTPLUSI: case OP_MINPLUS: case OP_MINPLUSI: case OP_NOTMINPLUS: case OP_NOTMINPLUSI: case OP_POSPLUS: case OP_POSPLUSI: case OP_NOTPOSPLUS: case OP_NOTPOSPLUSI: case OP_QUERY: case OP_QUERYI: case OP_NOTQUERY: case OP_NOTQUERYI: case OP_MINQUERY: case OP_MINQUERYI: case OP_NOTMINQUERY: case OP_NOTMINQUERYI: case OP_POSQUERY: case OP_POSQUERYI: case OP_NOTPOSQUERY: case OP_NOTPOSQUERYI: if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); break; } #else (void)(utf); /* Keep compiler happy by referencing function argument */ #endif /* MAYBE_UTF_MULTI */ } } } /* End of pcre2_find_bracket.c */ vfu-4.22/vstring/pcre2/pcre2_script_run.c0000644000175000017500000003536614145574056017001 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains the function for checking a script run. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /************************************************* * Check script run * *************************************************/ /* A script run is conceptually a sequence of characters all in the same Unicode script. However, it isn't quite that simple. There are special rules for scripts that are commonly used together, and also special rules for digits. This function implements the appropriate checks, which is possible only when PCRE2 is compiled with Unicode support. The function returns TRUE if there is no Unicode support; however, it should never be called in that circumstance because an error is given by pcre2_compile() if a script run is called for in a version of PCRE2 compiled without Unicode support. Arguments: pgr point to the first character endptr point after the last character utf TRUE if in UTF mode Returns: TRUE if this is a valid script run */ /* These dummy values must be less than the negation of the largest offset in the PRIV(ucd_script_sets) vector, which is held in a 16-bit field in UCD records (and is only likely to be a few hundred). */ #define SCRIPT_UNSET (-99999) #define SCRIPT_HANPENDING (-99998) #define SCRIPT_HANHIRAKATA (-99997) #define SCRIPT_HANBOPOMOFO (-99996) #define SCRIPT_HANHANGUL (-99995) #define SCRIPT_LIST (-99994) #define INTERSECTION_LIST_SIZE 50 BOOL PRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf) { #ifdef SUPPORT_UNICODE int require_script = SCRIPT_UNSET; uint8_t intersection_list[INTERSECTION_LIST_SIZE]; const uint8_t *require_list = NULL; uint32_t require_digitset = 0; uint32_t c; #if PCRE2_CODE_UNIT_WIDTH == 32 (void)utf; /* Avoid compiler warning */ #endif /* Any string containing fewer than 2 characters is a valid script run. */ if (ptr >= endptr) return TRUE; GETCHARINCTEST(c, ptr); if (ptr >= endptr) return TRUE; /* Scan strings of two or more characters, checking the Unicode characteristics of each code point. We make use of the Script Extensions property. There is special code for scripts that can be combined with characters from the Han Chinese script. This may be used in conjunction with four other scripts in these combinations: . Han with Hiragana and Katakana is allowed (for Japanese). . Han with Bopomofo is allowed (for Taiwanese Mandarin). . Han with Hangul is allowed (for Korean). If the first significant character's script is one of the four, the required script type is immediately known. However, if the first significant character's script is Han, we have to keep checking for a non-Han character. Hence the SCRIPT_HANPENDING state. */ for (;;) { const ucd_record *ucd = GET_UCD(c); int32_t scriptx = ucd->scriptx; /* If the script extension is Unknown, the string is not a valid script run. Such characters can only form script runs of length one. */ if (scriptx == ucp_Unknown) return FALSE; /* A character whose script extension is Inherited is always accepted with any script, and plays no further part in this testing. A character whose script is Common is always accepted, but must still be tested for a digit below. The scriptx value at this point is non-zero, because zero is ucp_Unknown, tested for above. */ if (scriptx != ucp_Inherited) { if (scriptx != ucp_Common) { /* If the script extension value is positive, the character is not a mark that can be used with many scripts. In the simple case we either set or compare with the required script. However, handling the scripts that can combine with Han are more complicated, as is the case when the previous characters have been man-script marks. */ if (scriptx > 0) { switch(require_script) { /* Either the first significant character (require_script unset) or after only Han characters. */ case SCRIPT_UNSET: case SCRIPT_HANPENDING: switch(scriptx) { case ucp_Han: require_script = SCRIPT_HANPENDING; break; case ucp_Hiragana: case ucp_Katakana: require_script = SCRIPT_HANHIRAKATA; break; case ucp_Bopomofo: require_script = SCRIPT_HANBOPOMOFO; break; case ucp_Hangul: require_script = SCRIPT_HANHANGUL; break; /* Not a Han-related script. If expecting one, fail. Otherise set the requirement to this script. */ default: if (require_script == SCRIPT_HANPENDING) return FALSE; require_script = scriptx; break; } break; /* Previously encountered one of the "with Han" scripts. Check that this character is appropriate. */ case SCRIPT_HANHIRAKATA: if (scriptx != ucp_Han && scriptx != ucp_Hiragana && scriptx != ucp_Katakana) return FALSE; break; case SCRIPT_HANBOPOMOFO: if (scriptx != ucp_Han && scriptx != ucp_Bopomofo) return FALSE; break; case SCRIPT_HANHANGUL: if (scriptx != ucp_Han && scriptx != ucp_Hangul) return FALSE; break; /* We have a list of scripts to check that is derived from one or more previous characters. This is either one of the lists in ucd_script_sets[] (for one previous character) or the intersection of several lists for multiple characters. */ case SCRIPT_LIST: { const uint8_t *list; for (list = require_list; *list != 0; list++) { if (*list == scriptx) break; } if (*list == 0) return FALSE; } /* The rest of the string must be in this script, but we have to allow for the Han complications. */ switch(scriptx) { case ucp_Han: require_script = SCRIPT_HANPENDING; break; case ucp_Hiragana: case ucp_Katakana: require_script = SCRIPT_HANHIRAKATA; break; case ucp_Bopomofo: require_script = SCRIPT_HANBOPOMOFO; break; case ucp_Hangul: require_script = SCRIPT_HANHANGUL; break; default: require_script = scriptx; break; } break; /* This is the easy case when a single script is required. */ default: if (scriptx != require_script) return FALSE; break; } } /* End of handing positive scriptx */ /* If scriptx is negative, this character is a mark-type character that has a list of permitted scripts. */ else { uint32_t chspecial; const uint8_t *clist, *rlist; const uint8_t *list = PRIV(ucd_script_sets) - scriptx; switch(require_script) { case SCRIPT_UNSET: require_list = PRIV(ucd_script_sets) - scriptx; require_script = SCRIPT_LIST; break; /* An inspection of the Unicode 11.0.0 files shows that there are the following types of Script Extension list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul scripts: . Bopomofo + Han . Han + Hiragana + Katakana . Hiragana + Katakana . Bopopmofo + Hangul + Han + Hiragana + Katakana The following code tries to make sense of this. */ #define FOUND_BOPOMOFO 1 #define FOUND_HIRAGANA 2 #define FOUND_KATAKANA 4 #define FOUND_HANGUL 8 case SCRIPT_HANPENDING: chspecial = 0; for (; *list != 0; list++) { switch (*list) { case ucp_Bopomofo: chspecial |= FOUND_BOPOMOFO; break; case ucp_Hiragana: chspecial |= FOUND_HIRAGANA; break; case ucp_Katakana: chspecial |= FOUND_KATAKANA; break; case ucp_Hangul: chspecial |= FOUND_HANGUL; break; default: break; } } if (chspecial == 0) return FALSE; if (chspecial == FOUND_BOPOMOFO) { require_script = SCRIPT_HANBOPOMOFO; } else if (chspecial == (FOUND_HIRAGANA|FOUND_KATAKANA)) { require_script = SCRIPT_HANHIRAKATA; } /* Otherwise it must be allowed with all of them, so remain in the pending state. */ break; case SCRIPT_HANHIRAKATA: for (; *list != 0; list++) { if (*list == ucp_Hiragana || *list == ucp_Katakana) break; } if (*list == 0) return FALSE; break; case SCRIPT_HANBOPOMOFO: for (; *list != 0; list++) { if (*list == ucp_Bopomofo) break; } if (*list == 0) return FALSE; break; case SCRIPT_HANHANGUL: for (; *list != 0; list++) { if (*list == ucp_Hangul) break; } if (*list == 0) return FALSE; break; /* Previously encountered one or more characters that are allowed with a list of scripts. Build the intersection of the required list with this character's list in intersection_list[]. This code is written so that it still works OK if the required list is already in that vector. */ case SCRIPT_LIST: { int i = 0; for (rlist = require_list; *rlist != 0; rlist++) { for (clist = list; *clist != 0; clist++) { if (*rlist == *clist) { intersection_list[i++] = *rlist; break; } } } if (i == 0) return FALSE; /* No scripts in common */ /* If there's just one script in common, we can set it as the unique required script. Otherwise, terminate the intersection list and make it the required list. */ if (i == 1) { require_script = intersection_list[0]; } else { intersection_list[i] = 0; require_list = intersection_list; } } break; /* The previously set required script is a single script, not Han-related. Check that it is in this character's list. */ default: for (; *list != 0; list++) { if (*list == require_script) break; } if (*list == 0) return FALSE; break; } } /* End of handling negative scriptx */ } /* End of checking non-Common character */ /* The character is in an acceptable script. We must now ensure that all decimal digits in the string come from the same set. Some scripts (e.g. Common, Arabic) have more than one set of decimal digits. This code does not allow mixing sets, even within the same script. The vector called PRIV(ucd_digit_sets)[] contains, in its first element, the number of following elements, and then, in ascending order, the code points of the '9' characters in every set of 10 digits. Each set is identified by the offset in the vector of its '9' character. An initial check of the first value picks up ASCII digits quickly. Otherwise, a binary chop is used. */ if (ucd->chartype == ucp_Nd) { uint32_t digitset; if (c <= PRIV(ucd_digit_sets)[1]) digitset = 1; else { int mid; int bot = 1; int top = PRIV(ucd_digit_sets)[0]; for (;;) { if (top <= bot + 1) /* <= rather than == is paranoia */ { digitset = top; break; } mid = (top + bot) / 2; if (c <= PRIV(ucd_digit_sets)[mid]) top = mid; else bot = mid; } } /* A required value of 0 means "unset". */ if (require_digitset == 0) require_digitset = digitset; else if (digitset != require_digitset) return FALSE; } /* End digit handling */ } /* End checking non-Inherited character */ /* If we haven't yet got to the end, pick up the next character. */ if (ptr >= endptr) return TRUE; GETCHARINCTEST(c, ptr); } /* End checking loop */ #else /* NOT SUPPORT_UNICODE */ (void)ptr; (void)endptr; (void)utf; return TRUE; #endif /* SUPPORT_UNICODE */ } /* End of pcre2_script_run.c */ vfu-4.22/vstring/pcre2/pcre2_extuni.c0000644000175000017500000001132014145574056016105 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2019 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* This module contains an internal function that is used to match a Unicode extended grapheme sequence. It is used by both pcre2_match() and pcre2_def_match(). However, it is called only when Unicode support is being compiled. Nevertheless, we provide a dummy function when there is no Unicode support, because some compilers do not like functionless source files. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" /* Dummy function */ #ifndef SUPPORT_UNICODE PCRE2_SPTR PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, PCRE2_SPTR end_subject, BOOL utf, int *xcount) { (void)c; (void)eptr; (void)start_subject; (void)end_subject; (void)utf; (void)xcount; return NULL; } #else /************************************************* * Match an extended grapheme sequence * *************************************************/ /* Arguments: c the first character eptr pointer to next character start_subject pointer to start of subject end_subject pointer to end of subject utf TRUE if in UTF mode xcount pointer to count of additional characters, or NULL if count not needed Returns: pointer after the end of the sequence */ PCRE2_SPTR PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, PCRE2_SPTR end_subject, BOOL utf, int *xcount) { int lgb = UCD_GRAPHBREAK(c); while (eptr < end_subject) { int rgb; int len = 1; if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } rgb = UCD_GRAPHBREAK(c); if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; /* Not breaking between Regional Indicators is allowed only if there are an even number of preceding RIs. */ if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) { int ricount = 0; PCRE2_SPTR bptr = eptr - 1; if (utf) BACKCHAR(bptr); /* bptr is pointing to the left-hand character */ while (bptr > start_subject) { bptr--; if (utf) { BACKCHAR(bptr); GETCHAR(c, bptr); } else c = *bptr; if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; ricount++; } if ((ricount & 1) != 0) break; /* Grapheme break required */ } /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this allows any number of them before a following Extended_Pictographic. */ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || lgb != ucp_gbExtended_Pictographic) lgb = rgb; eptr += len; if (xcount != NULL) *xcount += 1; } return eptr; } #endif /* SUPPORT_UNICODE */ /* End of pcre2_extuni.c */ vfu-4.22/vstring/pcre2/pcre2_substitute.c0000644000175000017500000007171614145574056017023 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #define PTR_STACK_SIZE 20 #define SUBSTITUTE_OPTIONS \ (PCRE2_SUBSTITUTE_EXTENDED|PCRE2_SUBSTITUTE_GLOBAL| \ PCRE2_SUBSTITUTE_LITERAL|PCRE2_SUBSTITUTE_MATCHED| \ PCRE2_SUBSTITUTE_OVERFLOW_LENGTH|PCRE2_SUBSTITUTE_REPLACEMENT_ONLY| \ PCRE2_SUBSTITUTE_UNKNOWN_UNSET|PCRE2_SUBSTITUTE_UNSET_EMPTY) /************************************************* * Find end of substitute text * *************************************************/ /* In extended mode, we recognize ${name:+set text:unset text} and similar constructions. This requires the identification of unescaped : and } characters. This function scans for such. It must deal with nested ${ constructions. The pointer to the text is updated, either to the required end character, or to where an error was detected. Arguments: code points to the compiled expression (for options) ptrptr points to the pointer to the start of the text (updated) ptrend end of the whole string last TRUE if the last expected string (only } recognized) Returns: 0 on success negative error code on failure */ static int find_text_end(const pcre2_code *code, PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL last) { int rc = 0; uint32_t nestlevel = 0; BOOL literal = FALSE; PCRE2_SPTR ptr = *ptrptr; for (; ptr < ptrend; ptr++) { if (literal) { if (ptr[0] == CHAR_BACKSLASH && ptr < ptrend - 1 && ptr[1] == CHAR_E) { literal = FALSE; ptr += 1; } } else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) { if (nestlevel == 0) goto EXIT; nestlevel--; } else if (*ptr == CHAR_COLON && !last && nestlevel == 0) goto EXIT; else if (*ptr == CHAR_DOLLAR_SIGN) { if (ptr < ptrend - 1 && ptr[1] == CHAR_LEFT_CURLY_BRACKET) { nestlevel++; ptr += 1; } } else if (*ptr == CHAR_BACKSLASH) { int erc; int errorcode; uint32_t ch; if (ptr < ptrend - 1) switch (ptr[1]) { case CHAR_L: case CHAR_l: case CHAR_U: case CHAR_u: ptr += 1; continue; } ptr += 1; /* Must point after \ */ erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode, code->overall_options, code->extra_options, FALSE, NULL); ptr -= 1; /* Back to last code unit of escape */ if (errorcode != 0) { rc = errorcode; goto EXIT; } switch(erc) { case 0: /* Data character */ case ESC_E: /* Isolated \E is ignored */ break; case ESC_Q: literal = TRUE; break; default: rc = PCRE2_ERROR_BADREPESCAPE; goto EXIT; } } } rc = PCRE2_ERROR_REPMISSINGBRACE; /* Terminator not found */ EXIT: *ptrptr = ptr; return rc; } /************************************************* * Match and substitute * *************************************************/ /* This function applies a compiled re to a subject string and creates a new string with substitutions. The first 7 arguments are the same as for pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED. Arguments: code points to the compiled expression subject points to the subject string length length of subject string (may contain binary zeros) start_offset where to start in the subject string options option bits match_data points to a match_data block, or is NULL context points a PCRE2 context replacement points to the replacement string rlength length of replacement string buffer where to put the substituted string blength points to length of buffer; updated to length of string Returns: >= 0 number of substitutions made < 0 an error code PCRE2_ERROR_BADREPLACEMENT means invalid use of $ */ /* This macro checks for space in the buffer before copying into it. On overflow, either give an error immediately, or keep on, accumulating the length. */ #define CHECKMEMCPY(from,length) \ { \ if (!overflowed && lengthleft < length) \ { \ if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \ overflowed = TRUE; \ extra_needed = length - lengthleft; \ } \ else if (overflowed) \ { \ extra_needed += length; \ } \ else \ { \ memcpy(buffer + buff_offset, from, CU2BYTES(length)); \ buff_offset += length; \ lengthleft -= length; \ } \ } /* Here's the function */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength, PCRE2_UCHAR *buffer, PCRE2_SIZE *blength) { int rc; int subs; int forcecase = 0; int forcecasereset = 0; uint32_t ovector_count; uint32_t goptions = 0; uint32_t suboptions; pcre2_match_data *internal_match_data = NULL; BOOL escaped_literal = FALSE; BOOL overflowed = FALSE; BOOL use_existing_match; BOOL replacement_only; #ifdef SUPPORT_UNICODE BOOL utf = (code->overall_options & PCRE2_UTF) != 0; BOOL ucp = (code->overall_options & PCRE2_UCP) != 0; #endif PCRE2_UCHAR temp[6]; PCRE2_SPTR ptr; PCRE2_SPTR repend; PCRE2_SIZE extra_needed = 0; PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength; PCRE2_SIZE *ovector; PCRE2_SIZE ovecsave[3]; pcre2_substitute_callout_block scb; /* General initialization */ buff_offset = 0; lengthleft = buff_length = *blength; *blength = PCRE2_UNSET; ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET; /* Partial matching is not valid. This must come after setting *blength to PCRE2_UNSET, so as not to imply an offset in the replacement. */ if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0) return PCRE2_ERROR_BADOPTION; /* Check for using a match that has already happened. Note that the subject pointer in the match data may be NULL after a no-match. */ use_existing_match = ((options & PCRE2_SUBSTITUTE_MATCHED) != 0); replacement_only = ((options & PCRE2_SUBSTITUTE_REPLACEMENT_ONLY) != 0); /* If starting from an existing match, there must be an externally provided match data block. We create an internal match_data block in two cases: (a) an external one is not supplied (and we are not starting from an existing match); (b) an existing match is to be used for the first substitution. In the latter case, we copy the existing match into the internal block. This ensures that no changes are made to the existing match data block. */ if (match_data == NULL) { pcre2_general_context *gcontext; if (use_existing_match) return PCRE2_ERROR_NULL; gcontext = (mcontext == NULL)? (pcre2_general_context *)code : (pcre2_general_context *)mcontext; match_data = internal_match_data = pcre2_match_data_create_from_pattern(code, gcontext); if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY; } else if (use_existing_match) { pcre2_general_context *gcontext = (mcontext == NULL)? (pcre2_general_context *)code : (pcre2_general_context *)mcontext; int pairs = (code->top_bracket + 1 < match_data->oveccount)? code->top_bracket + 1 : match_data->oveccount; internal_match_data = pcre2_match_data_create(match_data->oveccount, gcontext); if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY; memcpy(internal_match_data, match_data, offsetof(pcre2_match_data, ovector) + 2*pairs*sizeof(PCRE2_SIZE)); match_data = internal_match_data; } /* Remember ovector details */ ovector = pcre2_get_ovector_pointer(match_data); ovector_count = pcre2_get_ovector_count(match_data); /* Fixed things in the callout block */ scb.version = 0; scb.input = subject; scb.output = (PCRE2_SPTR)buffer; scb.ovector = ovector; /* Find lengths of zero-terminated strings and the end of the replacement. */ if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement); repend = replacement + rlength; /* Check UTF replacement string if necessary. */ #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) { rc = PRIV(valid_utf)(replacement, rlength, &(match_data->startchar)); if (rc != 0) { match_data->leftchar = 0; goto EXIT; } } #endif /* SUPPORT_UNICODE */ /* Save the substitute options and remove them from the match options. */ suboptions = options & SUBSTITUTE_OPTIONS; options &= ~SUBSTITUTE_OPTIONS; /* Error if the start match offset is greater than the length of the subject. */ if (start_offset > length) { match_data->leftchar = 0; rc = PCRE2_ERROR_BADOFFSET; goto EXIT; } /* Copy up to the start offset, unless only the replacement is required. */ if (!replacement_only) CHECKMEMCPY(subject, start_offset); /* Loop for global substituting. If PCRE2_SUBSTITUTE_MATCHED is set, the first match is taken from the match_data that was passed in. */ subs = 0; do { PCRE2_SPTR ptrstack[PTR_STACK_SIZE]; uint32_t ptrstackptr = 0; if (use_existing_match) { rc = match_data->rc; use_existing_match = FALSE; } else rc = pcre2_match(code, subject, length, start_offset, options|goptions, match_data, mcontext); #ifdef SUPPORT_UNICODE if (utf) options |= PCRE2_NO_UTF_CHECK; /* Only need to check once */ #endif /* Any error other than no match returns the error code. No match when not doing the special after-empty-match global rematch, or when at the end of the subject, breaks the global loop. Otherwise, advance the starting point by one character, copying it to the output, and try again. */ if (rc < 0) { PCRE2_SIZE save_start; if (rc != PCRE2_ERROR_NOMATCH) goto EXIT; if (goptions == 0 || start_offset >= length) break; /* Advance by one code point. Then, if CRLF is a valid newline sequence and we have advanced into the middle of it, advance one more code point. In other words, do not start in the middle of CRLF, even if CR and LF on their own are valid newlines. */ save_start = start_offset++; if (subject[start_offset-1] == CHAR_CR && code->newline_convention != PCRE2_NEWLINE_CR && code->newline_convention != PCRE2_NEWLINE_LF && start_offset < length && subject[start_offset] == CHAR_LF) start_offset++; /* Otherwise, in UTF mode, advance past any secondary code points. */ else if ((code->overall_options & PCRE2_UTF) != 0) { #if PCRE2_CODE_UNIT_WIDTH == 8 while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80) start_offset++; #elif PCRE2_CODE_UNIT_WIDTH == 16 while (start_offset < length && (subject[start_offset] & 0xfc00) == 0xdc00) start_offset++; #endif } /* Copy what we have advanced past (unless not required), reset the special global options, and continue to the next match. */ fraglength = start_offset - save_start; if (!replacement_only) CHECKMEMCPY(subject + save_start, fraglength); goptions = 0; continue; } /* Handle a successful match. Matches that use \K to end before they start or start before the current point in the subject are not supported. */ if (ovector[1] < ovector[0] || ovector[0] < start_offset) { rc = PCRE2_ERROR_BADSUBSPATTERN; goto EXIT; } /* Check for the same match as previous. This is legitimate after matching an empty string that starts after the initial match offset. We have tried again at the match point in case the pattern is one like /(?<=\G.)/ which can never match at its starting point, so running the match achieves the bumpalong. If we do get the same (null) match at the original match point, it isn't such a pattern, so we now do the empty string magic. In all other cases, a repeat match should never occur. */ if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1]) { if (ovector[0] == ovector[1] && ovecsave[2] != start_offset) { goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; ovecsave[2] = start_offset; continue; /* Back to the top of the loop */ } rc = PCRE2_ERROR_INTERNAL_DUPMATCH; goto EXIT; } /* Count substitutions with a paranoid check for integer overflow; surely no real call to this function would ever hit this! */ if (subs == INT_MAX) { rc = PCRE2_ERROR_TOOMANYREPLACE; goto EXIT; } subs++; /* Copy the text leading up to the match (unless not required), and remember where the insert begins and how many ovector pairs are set. */ if (rc == 0) rc = ovector_count; fraglength = ovector[0] - start_offset; if (!replacement_only) CHECKMEMCPY(subject + start_offset, fraglength); scb.output_offsets[0] = buff_offset; scb.oveccount = rc; /* Process the replacement string. If the entire replacement is literal, just copy it with length check. */ ptr = replacement; if ((suboptions & PCRE2_SUBSTITUTE_LITERAL) != 0) { CHECKMEMCPY(ptr, rlength); } /* Within a non-literal replacement, which must be scanned character by character, local literal mode can be set by \Q, but only in extended mode when backslashes are being interpreted. In extended mode we must handle nested substrings that are to be reprocessed. */ else for (;;) { uint32_t ch; unsigned int chlen; /* If at the end of a nested substring, pop the stack. */ if (ptr >= repend) { if (ptrstackptr == 0) break; /* End of replacement string */ repend = ptrstack[--ptrstackptr]; ptr = ptrstack[--ptrstackptr]; continue; } /* Handle the next character */ if (escaped_literal) { if (ptr[0] == CHAR_BACKSLASH && ptr < repend - 1 && ptr[1] == CHAR_E) { escaped_literal = FALSE; ptr += 2; continue; } goto LOADLITERAL; } /* Not in literal mode. */ if (*ptr == CHAR_DOLLAR_SIGN) { int group, n; uint32_t special = 0; BOOL inparens; BOOL star; PCRE2_SIZE sublength; PCRE2_SPTR text1_start = NULL; PCRE2_SPTR text1_end = NULL; PCRE2_SPTR text2_start = NULL; PCRE2_SPTR text2_end = NULL; PCRE2_UCHAR next; PCRE2_UCHAR name[33]; if (++ptr >= repend) goto BAD; if ((next = *ptr) == CHAR_DOLLAR_SIGN) goto LOADLITERAL; group = -1; n = 0; inparens = FALSE; star = FALSE; if (next == CHAR_LEFT_CURLY_BRACKET) { if (++ptr >= repend) goto BAD; next = *ptr; inparens = TRUE; } if (next == CHAR_ASTERISK) { if (++ptr >= repend) goto BAD; next = *ptr; star = TRUE; } if (!star && next >= CHAR_0 && next <= CHAR_9) { group = next - CHAR_0; while (++ptr < repend) { next = *ptr; if (next < CHAR_0 || next > CHAR_9) break; group = group * 10 + next - CHAR_0; /* A check for a number greater than the hightest captured group is sufficient here; no need for a separate overflow check. If unknown groups are to be treated as unset, just skip over any remaining digits and carry on. */ if (group > code->top_bracket) { if ((suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) { while (++ptr < repend && *ptr >= CHAR_0 && *ptr <= CHAR_9); break; } else { rc = PCRE2_ERROR_NOSUBSTRING; goto PTREXIT; } } } } else { const uint8_t *ctypes = code->tables + ctypes_offset; while (MAX_255(next) && (ctypes[next] & ctype_word) != 0) { name[n++] = next; if (n > 32) goto BAD; if (++ptr >= repend) break; next = *ptr; } if (n == 0) goto BAD; name[n] = 0; } /* In extended mode we recognize ${name:+set text:unset text} and ${name:-default text}. */ if (inparens) { if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && !star && ptr < repend - 2 && next == CHAR_COLON) { special = *(++ptr); if (special != CHAR_PLUS && special != CHAR_MINUS) { rc = PCRE2_ERROR_BADSUBSTITUTION; goto PTREXIT; } text1_start = ++ptr; rc = find_text_end(code, &ptr, repend, special == CHAR_MINUS); if (rc != 0) goto PTREXIT; text1_end = ptr; if (special == CHAR_PLUS && *ptr == CHAR_COLON) { text2_start = ++ptr; rc = find_text_end(code, &ptr, repend, TRUE); if (rc != 0) goto PTREXIT; text2_end = ptr; } } else { if (ptr >= repend || *ptr != CHAR_RIGHT_CURLY_BRACKET) { rc = PCRE2_ERROR_REPMISSINGBRACE; goto PTREXIT; } } ptr++; } /* Have found a syntactically correct group number or name, or *name. Only *MARK is currently recognized. */ if (star) { if (PRIV(strcmp_c8)(name, STRING_MARK) == 0) { PCRE2_SPTR mark = pcre2_get_mark(match_data); if (mark != NULL) { PCRE2_SPTR mark_start = mark; while (*mark != 0) mark++; fraglength = mark - mark_start; CHECKMEMCPY(mark_start, fraglength); } } else goto BAD; } /* Substitute the contents of a group. We don't use substring_copy functions any more, in order to support case forcing. */ else { PCRE2_SPTR subptr, subptrend; /* Find a number for a named group. In case there are duplicate names, search for the first one that is set. If the name is not found when PCRE2_SUBSTITUTE_UNKNOWN_EMPTY is set, set the group number to a non-existent group. */ if (group < 0) { PCRE2_SPTR first, last, entry; rc = pcre2_substring_nametable_scan(code, name, &first, &last); if (rc == PCRE2_ERROR_NOSUBSTRING && (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) { group = code->top_bracket + 1; } else { if (rc < 0) goto PTREXIT; for (entry = first; entry <= last; entry += rc) { uint32_t ng = GET2(entry, 0); if (ng < ovector_count) { if (group < 0) group = ng; /* First in ovector */ if (ovector[ng*2] != PCRE2_UNSET) { group = ng; /* First that is set */ break; } } } /* If group is still negative, it means we did not find a group that is in the ovector. Just set the first group. */ if (group < 0) group = GET2(first, 0); } } /* We now have a group that is identified by number. Find the length of the captured string. If a group in a non-special substitution is unset when PCRE2_SUBSTITUTE_UNSET_EMPTY is set, substitute nothing. */ rc = pcre2_substring_length_bynumber(match_data, group, &sublength); if (rc < 0) { if (rc == PCRE2_ERROR_NOSUBSTRING && (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0) { rc = PCRE2_ERROR_UNSET; } if (rc != PCRE2_ERROR_UNSET) goto PTREXIT; /* Non-unset errors */ if (special == 0) /* Plain substitution */ { if ((suboptions & PCRE2_SUBSTITUTE_UNSET_EMPTY) != 0) continue; goto PTREXIT; /* Else error */ } } /* If special is '+' we have a 'set' and possibly an 'unset' text, both of which are reprocessed when used. If special is '-' we have a default text for when the group is unset; it must be reprocessed. */ if (special != 0) { if (special == CHAR_MINUS) { if (rc == 0) goto LITERAL_SUBSTITUTE; text2_start = text1_start; text2_end = text1_end; } if (ptrstackptr >= PTR_STACK_SIZE) goto BAD; ptrstack[ptrstackptr++] = ptr; ptrstack[ptrstackptr++] = repend; if (rc == 0) { ptr = text1_start; repend = text1_end; } else { ptr = text2_start; repend = text2_end; } continue; } /* Otherwise we have a literal substitution of a group's contents. */ LITERAL_SUBSTITUTE: subptr = subject + ovector[group*2]; subptrend = subject + ovector[group*2 + 1]; /* Substitute a literal string, possibly forcing alphabetic case. */ while (subptr < subptrend) { GETCHARINCTEST(ch, subptr); if (forcecase != 0) { #ifdef SUPPORT_UNICODE if (utf || ucp) { uint32_t type = UCD_CHARTYPE(ch); if (PRIV(ucp_gentype)[type] == ucp_L && type != ((forcecase > 0)? ucp_Lu : ucp_Ll)) ch = UCD_OTHERCASE(ch); } else #endif { if (((code->tables + cbits_offset + ((forcecase > 0)? cbit_upper:cbit_lower) )[ch/8] & (1u << (ch%8))) == 0) ch = (code->tables + fcc_offset)[ch]; } forcecase = forcecasereset; } #ifdef SUPPORT_UNICODE if (utf) chlen = PRIV(ord2utf)(ch, temp); else #endif { temp[0] = ch; chlen = 1; } CHECKMEMCPY(temp, chlen); } } } /* Handle an escape sequence in extended mode. We can use check_escape() to process \Q, \E, \c, \o, \x and \ followed by non-alphanumerics, but the case-forcing escapes are not supported in pcre2_compile() so must be recognized here. */ else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && *ptr == CHAR_BACKSLASH) { int errorcode; if (ptr < repend - 1) switch (ptr[1]) { case CHAR_L: forcecase = forcecasereset = -1; ptr += 2; continue; case CHAR_l: forcecase = -1; forcecasereset = 0; ptr += 2; continue; case CHAR_U: forcecase = forcecasereset = 1; ptr += 2; continue; case CHAR_u: forcecase = 1; forcecasereset = 0; ptr += 2; continue; default: break; } ptr++; /* Point after \ */ rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode, code->overall_options, code->extra_options, FALSE, NULL); if (errorcode != 0) goto BADESCAPE; switch(rc) { case ESC_E: forcecase = forcecasereset = 0; continue; case ESC_Q: escaped_literal = TRUE; continue; case 0: /* Data character */ goto LITERAL; default: goto BADESCAPE; } } /* Handle a literal code unit */ else { LOADLITERAL: GETCHARINCTEST(ch, ptr); /* Get character value, increment pointer */ LITERAL: if (forcecase != 0) { #ifdef SUPPORT_UNICODE if (utf || ucp) { uint32_t type = UCD_CHARTYPE(ch); if (PRIV(ucp_gentype)[type] == ucp_L && type != ((forcecase > 0)? ucp_Lu : ucp_Ll)) ch = UCD_OTHERCASE(ch); } else #endif { if (((code->tables + cbits_offset + ((forcecase > 0)? cbit_upper:cbit_lower) )[ch/8] & (1u << (ch%8))) == 0) ch = (code->tables + fcc_offset)[ch]; } forcecase = forcecasereset; } #ifdef SUPPORT_UNICODE if (utf) chlen = PRIV(ord2utf)(ch, temp); else #endif { temp[0] = ch; chlen = 1; } CHECKMEMCPY(temp, chlen); } /* End handling a literal code unit */ } /* End of loop for scanning the replacement. */ /* The replacement has been copied to the output, or its size has been remembered. Do the callout if there is one and we have done an actual replacement. */ if (!overflowed && mcontext != NULL && mcontext->substitute_callout != NULL) { scb.subscount = subs; scb.output_offsets[1] = buff_offset; rc = mcontext->substitute_callout(&scb, mcontext->substitute_callout_data); /* A non-zero return means cancel this substitution. Instead, copy the matched string fragment. */ if (rc != 0) { PCRE2_SIZE newlength = scb.output_offsets[1] - scb.output_offsets[0]; PCRE2_SIZE oldlength = ovector[1] - ovector[0]; buff_offset -= newlength; lengthleft += newlength; if (!replacement_only) CHECKMEMCPY(subject + ovector[0], oldlength); /* A negative return means do not do any more. */ if (rc < 0) suboptions &= (~PCRE2_SUBSTITUTE_GLOBAL); } } /* Save the details of this match. See above for how this data is used. If we matched an empty string, do the magic for global matches. Update the start offset to point to the rest of the subject string. If we re-used an existing match for the first match, switch to the internal match data block. */ ovecsave[0] = ovector[0]; ovecsave[1] = ovector[1]; ovecsave[2] = start_offset; goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 : PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; start_offset = ovector[1]; } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */ /* Copy the rest of the subject unless not required, and terminate the output with a binary zero. */ if (!replacement_only) { fraglength = length - start_offset; CHECKMEMCPY(subject + start_offset, fraglength); } temp[0] = 0; CHECKMEMCPY(temp, 1); /* If overflowed is set it means the PCRE2_SUBSTITUTE_OVERFLOW_LENGTH is set, and matching has carried on after a full buffer, in order to compute the length needed. Otherwise, an overflow generates an immediate error return. */ if (overflowed) { rc = PCRE2_ERROR_NOMEMORY; *blength = buff_length + extra_needed; } /* After a successful execution, return the number of substitutions and set the length of buffer used, excluding the trailing zero. */ else { rc = subs; *blength = buff_offset - 1; } EXIT: if (internal_match_data != NULL) pcre2_match_data_free(internal_match_data); else match_data->rc = rc; return rc; NOROOM: rc = PCRE2_ERROR_NOMEMORY; goto EXIT; BAD: rc = PCRE2_ERROR_BADREPLACEMENT; goto PTREXIT; BADESCAPE: rc = PCRE2_ERROR_BADREPESCAPE; PTREXIT: *blength = (PCRE2_SIZE)(ptr - replacement); goto EXIT; } /* End of pcre2_substitute.c */ vfu-4.22/vstring/pcre2/AUTHORS0000644000175000017500000000135514145574056014411 0ustar cadecadeTHE MAIN PCRE2 LIBRARY CODE --------------------------- Written by: Philip Hazel Email local part: Philip.Hazel Email domain: gmail.com Retired from University of Cambridge Computing Service, Cambridge, England. Copyright (c) 1997-2021 University of Cambridge All rights reserved PCRE2 JUST-IN-TIME COMPILATION SUPPORT -------------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2010-2021 Zoltan Herczeg All rights reserved. STACK-LESS JUST-IN-TIME COMPILER -------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2009-2021 Zoltan Herczeg All rights reserved. #### vfu-4.22/vstring/pcre2/pcre2_config.c0000644000175000017500000001707414145574056016052 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2020 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Save the configured link size, which is in bytes. In 16-bit and 32-bit modes its value gets changed by pcre2_intmodedep.h (included by pcre2_internal.h) to be in code units. */ static int configured_link_size = LINK_SIZE; #include "pcre2_internal.h" /* These macros are the standard way of turning unquoted text into C strings. They allow macros like PCRE2_MAJOR to be defined without quotes, which is convenient for user programs that want to test their values. */ #define STRING(a) # a #define XSTRING(s) STRING(s) /************************************************* * Return info about what features are configured * *************************************************/ /* If where is NULL, the length of memory required is returned. Arguments: what what information is required where where to put the information Returns: 0 if a numerical value is returned >= 0 if a string value PCRE2_ERROR_BADOPTION if "where" not recognized or JIT target requested when JIT not enabled */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_config(uint32_t what, void *where) { if (where == NULL) /* Requests a length */ { switch(what) { default: return PCRE2_ERROR_BADOPTION; case PCRE2_CONFIG_BSR: case PCRE2_CONFIG_COMPILED_WIDTHS: case PCRE2_CONFIG_DEPTHLIMIT: case PCRE2_CONFIG_HEAPLIMIT: case PCRE2_CONFIG_JIT: case PCRE2_CONFIG_LINKSIZE: case PCRE2_CONFIG_MATCHLIMIT: case PCRE2_CONFIG_NEVER_BACKSLASH_C: case PCRE2_CONFIG_NEWLINE: case PCRE2_CONFIG_PARENSLIMIT: case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */ case PCRE2_CONFIG_TABLES_LENGTH: case PCRE2_CONFIG_UNICODE: return sizeof(uint32_t); /* These are handled below */ case PCRE2_CONFIG_JITTARGET: case PCRE2_CONFIG_UNICODE_VERSION: case PCRE2_CONFIG_VERSION: break; } } switch (what) { default: return PCRE2_ERROR_BADOPTION; case PCRE2_CONFIG_BSR: #ifdef BSR_ANYCRLF *((uint32_t *)where) = PCRE2_BSR_ANYCRLF; #else *((uint32_t *)where) = PCRE2_BSR_UNICODE; #endif break; case PCRE2_CONFIG_COMPILED_WIDTHS: *((uint32_t *)where) = 0 #ifdef SUPPORT_PCRE2_8 + 1 #endif #ifdef SUPPORT_PCRE2_16 + 2 #endif #ifdef SUPPORT_PCRE2_32 + 4 #endif ; break; case PCRE2_CONFIG_DEPTHLIMIT: *((uint32_t *)where) = MATCH_LIMIT_DEPTH; break; case PCRE2_CONFIG_HEAPLIMIT: *((uint32_t *)where) = HEAP_LIMIT; break; case PCRE2_CONFIG_JIT: #ifdef SUPPORT_JIT *((uint32_t *)where) = 1; #else *((uint32_t *)where) = 0; #endif break; case PCRE2_CONFIG_JITTARGET: #ifdef SUPPORT_JIT { const char *v = PRIV(jit_get_target)(); return (int)(1 + ((where == NULL)? strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v))); } #else return PCRE2_ERROR_BADOPTION; #endif case PCRE2_CONFIG_LINKSIZE: *((uint32_t *)where) = (uint32_t)configured_link_size; break; case PCRE2_CONFIG_MATCHLIMIT: *((uint32_t *)where) = MATCH_LIMIT; break; case PCRE2_CONFIG_NEWLINE: *((uint32_t *)where) = NEWLINE_DEFAULT; break; case PCRE2_CONFIG_NEVER_BACKSLASH_C: #ifdef NEVER_BACKSLASH_C *((uint32_t *)where) = 1; #else *((uint32_t *)where) = 0; #endif break; case PCRE2_CONFIG_PARENSLIMIT: *((uint32_t *)where) = PARENS_NEST_LIMIT; break; /* This is now obsolete. The stack is no longer used via recursion for handling backtracking in pcre2_match(). */ case PCRE2_CONFIG_STACKRECURSE: *((uint32_t *)where) = 0; break; case PCRE2_CONFIG_TABLES_LENGTH: *((uint32_t *)where) = TABLES_LENGTH; break; case PCRE2_CONFIG_UNICODE_VERSION: { #if defined SUPPORT_UNICODE const char *v = PRIV(unicode_version); #else const char *v = "Unicode not supported"; #endif return (int)(1 + ((where == NULL)? strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v))); } break; case PCRE2_CONFIG_UNICODE: #if defined SUPPORT_UNICODE *((uint32_t *)where) = 1; #else *((uint32_t *)where) = 0; #endif break; /* The hackery in setting "v" below is to cope with the case when PCRE2_PRERELEASE is set to an empty string (which it is for real releases). If the second alternative is used in this case, it does not leave a space before the date. On the other hand, if all four macros are put into a single XSTRING when PCRE2_PRERELEASE is not empty, an unwanted space is inserted. There are problems using an "obvious" approach like this: XSTRING(PCRE2_MAJOR) "." XSTRING(PCRE_MINOR) XSTRING(PCRE2_PRERELEASE) " " XSTRING(PCRE_DATE) because, when PCRE2_PRERELEASE is empty, this leads to an attempted expansion of STRING(). The C standard states: "If (before argument substitution) any argument consists of no preprocessing tokens, the behavior is undefined." It turns out the gcc treats this case as a single empty string - which is what we really want - but Visual C grumbles about the lack of an argument for the macro. Unfortunately, both are within their rights. As there seems to be no way to test for a macro's value being empty at compile time, we have to resort to a runtime test. */ case PCRE2_CONFIG_VERSION: { const char *v = (XSTRING(Z PCRE2_PRERELEASE)[1] == 0)? XSTRING(PCRE2_MAJOR.PCRE2_MINOR PCRE2_DATE) : XSTRING(PCRE2_MAJOR.PCRE2_MINOR) XSTRING(PCRE2_PRERELEASE PCRE2_DATE); return (int)(1 + ((where == NULL)? strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v))); } } return 0; } /* End of pcre2_config.c */ vfu-4.22/vstring/pcre2/pcre2_compile.c0000644000175000017500000125443314145574056016240 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define NLBLOCK cb /* Block containing newline information */ #define PSSTART start_pattern /* Field containing processed string start */ #define PSEND end_pattern /* Field containing processed string end */ #include "pcre2_internal.h" /* In rare error cases debugging might require calling pcre2_printint(). */ #if 0 #ifdef EBCDIC #define PRINTABLE(c) ((c) >= 64 && (c) < 255) #else #define PRINTABLE(c) ((c) >= 32 && (c) < 127) #endif #include "pcre2_printint.c" #define DEBUG_CALL_PRINTINT #endif /* Other debugging code can be enabled by these defines. */ /* #define DEBUG_SHOW_CAPTURES */ /* #define DEBUG_SHOW_PARSED */ /* There are a few things that vary with different code unit sizes. Handle them by defining macros in order to minimize #if usage. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5 #define XDIGIT(c) xdigitab[c] #else /* Either 16-bit or 32-bit */ #define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff) #if PCRE2_CODE_UNIT_WIDTH == 16 #define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6 #else /* 32-bit */ #define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6 #endif #endif /* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which consists of uint32_t elements. Assume that if uint32_t can't hold it, two of them will be able to (i.e. assume a 64-bit world). */ #if PCRE2_SIZE_MAX <= UINT32_MAX #define PUTOFFSET(s,p) *p++ = s #define GETOFFSET(s,p) s = *p++ #define GETPLUSOFFSET(s,p) s = *(++p) #define READPLUSOFFSET(s,p) s = p[1] #define SKIPOFFSET(p) p++ #define SIZEOFFSET 1 #else #define PUTOFFSET(s,p) \ { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); } #define GETOFFSET(s,p) \ { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; } #define GETPLUSOFFSET(s,p) \ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; } #define READPLUSOFFSET(s,p) \ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; } #define SKIPOFFSET(p) p += 2 #define SIZEOFFSET 2 #endif /* Macros for manipulating elements of the parsed pattern vector. */ #define META_CODE(x) (x & 0xffff0000u) #define META_DATA(x) (x & 0x0000ffffu) #define META_DIFF(x,y) ((x-y)>>16) /* Function definitions to allow mutual recursion */ #ifdef SUPPORT_UNICODE static unsigned int add_list_to_class_internal(uint8_t *, PCRE2_UCHAR **, uint32_t, compile_block *, const uint32_t *, unsigned int); #endif static int compile_regex(uint32_t, PCRE2_UCHAR **, uint32_t **, int *, uint32_t, uint32_t *, int32_t *, uint32_t *, int32_t *, branch_chain *, compile_block *, PCRE2_SIZE *); static int get_branchlength(uint32_t **, int *, int *, parsed_recurse_check *, compile_block *); static BOOL set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *, compile_block *); static int check_lookbehinds(uint32_t *, uint32_t **, parsed_recurse_check *, compile_block *, int *); /************************************************* * Code parameters and static tables * *************************************************/ #define MAX_GROUP_NUMBER 65535u #define MAX_REPEAT_COUNT 65535u #define REPEAT_UNLIMITED (MAX_REPEAT_COUNT+1) /* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in different ways in the different pattern scans. The parsing and group- identifying pre-scan uses it to handle nesting, and needs it to be 16-bit aligned for this. Having defined the size in code units, we set up C16_WORK_SIZE as the number of elements in the 16-bit vector. During the first compiling phase, when determining how much memory is required, the regex is partly compiled into this space, but the compiled parts are discarded as soon as they can be, so that hopefully there will never be an overrun. The code does, however, check for an overrun, which can occur for pathological patterns. The size of the workspace depends on LINK_SIZE because the length of compiled items varies with this. In the real compile phase, this workspace is not currently used. */ #define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */ #define C16_WORK_SIZE \ ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t)) /* A uint32_t vector is used for caching information about the size of capturing groups, to improve performance. A default is created on the stack of this size. */ #define GROUPINFO_DEFAULT_SIZE 256 /* The overrun tests check for a slightly smaller size so that they detect the overrun before it actually does run off the end of the data block. */ #define WORK_SIZE_SAFETY_MARGIN (100) /* This value determines the size of the initial vector that is used for remembering named groups during the pre-compile. It is allocated on the stack, but if it is too small, it is expanded, in a similar way to the workspace. The value is the number of slots in the list. */ #define NAMED_GROUP_LIST_SIZE 20 /* The pre-compiling pass over the pattern creates a parsed pattern in a vector of uint32_t. For short patterns this lives on the stack, with this size. Heap memory is used for longer patterns. */ #define PARSED_PATTERN_DEFAULT_SIZE 1024 /* Maximum length value to check against when making sure that the variable that holds the compiled pattern length does not overflow. We make it a bit less than INT_MAX to allow for adding in group terminating code units, so that we don't have to check them every time. */ #define OFLOW_MAX (INT_MAX - 20) /* Code values for parsed patterns, which are stored in a vector of 32-bit unsigned ints. Values less than META_END are literal data values. The coding for identifying the item is in the top 16-bits, leaving 16 bits for the additional data that some of them need. The META_CODE, META_DATA, and META_DIFF macros are used to manipulate parsed pattern elements. NOTE: When these definitions are changed, the table of extra lengths for each code (meta_extra_lengths, just below) must be updated to remain in step. */ #define META_END 0x80000000u /* End of pattern */ #define META_ALT 0x80010000u /* alternation */ #define META_ATOMIC 0x80020000u /* atomic group */ #define META_BACKREF 0x80030000u /* Back ref */ #define META_BACKREF_BYNAME 0x80040000u /* \k'name' */ #define META_BIGVALUE 0x80050000u /* Next is a literal > META_END */ #define META_CALLOUT_NUMBER 0x80060000u /* (?C with numerical argument */ #define META_CALLOUT_STRING 0x80070000u /* (?C with string argument */ #define META_CAPTURE 0x80080000u /* Capturing parenthesis */ #define META_CIRCUMFLEX 0x80090000u /* ^ metacharacter */ #define META_CLASS 0x800a0000u /* start non-empty class */ #define META_CLASS_EMPTY 0x800b0000u /* empty class */ #define META_CLASS_EMPTY_NOT 0x800c0000u /* negative empty class */ #define META_CLASS_END 0x800d0000u /* end of non-empty class */ #define META_CLASS_NOT 0x800e0000u /* start non-empty negative class */ #define META_COND_ASSERT 0x800f0000u /* (?(?assertion)... */ #define META_COND_DEFINE 0x80100000u /* (?(DEFINE)... */ #define META_COND_NAME 0x80110000u /* (?()... */ #define META_COND_NUMBER 0x80120000u /* (?(digits)... */ #define META_COND_RNAME 0x80130000u /* (?(R&name)... */ #define META_COND_RNUMBER 0x80140000u /* (?(Rdigits)... */ #define META_COND_VERSION 0x80150000u /* (?(VERSIONx.y)... */ #define META_DOLLAR 0x80160000u /* $ metacharacter */ #define META_DOT 0x80170000u /* . metacharacter */ #define META_ESCAPE 0x80180000u /* \d and friends */ #define META_KET 0x80190000u /* closing parenthesis */ #define META_NOCAPTURE 0x801a0000u /* no capture parens */ #define META_OPTIONS 0x801b0000u /* (?i) and friends */ #define META_POSIX 0x801c0000u /* POSIX class item */ #define META_POSIX_NEG 0x801d0000u /* negative POSIX class item */ #define META_RANGE_ESCAPED 0x801e0000u /* range with at least one escape */ #define META_RANGE_LITERAL 0x801f0000u /* range defined literally */ #define META_RECURSE 0x80200000u /* Recursion */ #define META_RECURSE_BYNAME 0x80210000u /* (?&name) */ #define META_SCRIPT_RUN 0x80220000u /* (*script_run:...) */ /* These must be kept together to make it easy to check that an assertion is present where expected in a conditional group. */ #define META_LOOKAHEAD 0x80230000u /* (?= */ #define META_LOOKAHEADNOT 0x80240000u /* (?! */ #define META_LOOKBEHIND 0x80250000u /* (?<= */ #define META_LOOKBEHINDNOT 0x80260000u /* (?= 10 */ 1+SIZEOFFSET, /* META_BACKREF_BYNAME */ 1, /* META_BIGVALUE */ 3, /* META_CALLOUT_NUMBER */ 3+SIZEOFFSET, /* META_CALLOUT_STRING */ 0, /* META_CAPTURE */ 0, /* META_CIRCUMFLEX */ 0, /* META_CLASS */ 0, /* META_CLASS_EMPTY */ 0, /* META_CLASS_EMPTY_NOT */ 0, /* META_CLASS_END */ 0, /* META_CLASS_NOT */ 0, /* META_COND_ASSERT */ SIZEOFFSET, /* META_COND_DEFINE */ 1+SIZEOFFSET, /* META_COND_NAME */ 1+SIZEOFFSET, /* META_COND_NUMBER */ 1+SIZEOFFSET, /* META_COND_RNAME */ 1+SIZEOFFSET, /* META_COND_RNUMBER */ 3, /* META_COND_VERSION */ 0, /* META_DOLLAR */ 0, /* META_DOT */ 0, /* META_ESCAPE - more for ESC_P, ESC_p, ESC_g, ESC_k */ 0, /* META_KET */ 0, /* META_NOCAPTURE */ 1, /* META_OPTIONS */ 1, /* META_POSIX */ 1, /* META_POSIX_NEG */ 0, /* META_RANGE_ESCAPED */ 0, /* META_RANGE_LITERAL */ SIZEOFFSET, /* META_RECURSE */ 1+SIZEOFFSET, /* META_RECURSE_BYNAME */ 0, /* META_SCRIPT_RUN */ 0, /* META_LOOKAHEAD */ 0, /* META_LOOKAHEADNOT */ SIZEOFFSET, /* META_LOOKBEHIND */ SIZEOFFSET, /* META_LOOKBEHINDNOT */ 0, /* META_LOOKAHEAD_NA */ SIZEOFFSET, /* META_LOOKBEHIND_NA */ 1, /* META_MARK - plus the string length */ 0, /* META_ACCEPT */ 0, /* META_FAIL */ 0, /* META_COMMIT */ 1, /* META_COMMIT_ARG - plus the string length */ 0, /* META_PRUNE */ 1, /* META_PRUNE_ARG - plus the string length */ 0, /* META_SKIP */ 1, /* META_SKIP_ARG - plus the string length */ 0, /* META_THEN */ 1, /* META_THEN_ARG - plus the string length */ 0, /* META_ASTERISK */ 0, /* META_ASTERISK_PLUS */ 0, /* META_ASTERISK_QUERY */ 0, /* META_PLUS */ 0, /* META_PLUS_PLUS */ 0, /* META_PLUS_QUERY */ 0, /* META_QUERY */ 0, /* META_QUERY_PLUS */ 0, /* META_QUERY_QUERY */ 2, /* META_MINMAX */ 2, /* META_MINMAX_PLUS */ 2 /* META_MINMAX_QUERY */ }; /* Types for skipping parts of a parsed pattern. */ enum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET }; /* Macro for setting individual bits in class bitmaps. It took some experimenting to figure out how to stop gcc 5.3.0 from warning with -Wconversion. This version gets a warning: #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1u << ((b)&7)) Let's hope the apparently less efficient version isn't actually so bad if the compiler is clever with identical subexpressions. */ #define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1u << ((b)&7))) /* Private flags added to firstcu and reqcu. */ #define REQ_CASELESS (1u << 0) /* Indicates caselessness */ #define REQ_VARY (1u << 1) /* reqcu followed non-literal item */ /* Negative values for the firstcu and reqcu flags */ #define REQ_UNSET (-2) /* Not yet found anything */ #define REQ_NONE (-1) /* Found not fixed char */ /* These flags are used in the groupinfo vector. */ #define GI_SET_FIXED_LENGTH 0x80000000u #define GI_NOT_FIXED_LENGTH 0x40000000u #define GI_FIXED_LENGTH_MASK 0x0000ffffu /* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC and is fast (a good compiler can turn it into a subtraction and unsigned comparison). */ #define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) /* Table to identify hex digits. The tables in chartables are dependent on the locale, and may mark arbitrary characters as digits. We want to recognize only 0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It costs 256 bytes, but it is a lot faster than doing character value tests (at least in some simple cases I timed), and in some applications one wants PCRE2 to compile efficiently as well as match efficiently. The value in the table is the binary hex digit value, or 0xff for non-hex digits. */ /* This is the "normal" case, for ASCII systems, and EBCDIC systems running in UTF-8 mode. */ #ifndef EBCDIC static const uint8_t xdigitab[] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */ #else /* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ static const uint8_t xdigitab[] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */ #endif /* EBCDIC */ /* Table for handling alphanumeric escaped characters. Positive returns are simple data values; negative values are for special things like \d and so on. Zero means further processing is needed (for things like \x), or the escape is invalid. */ /* This is the "normal" table for ASCII systems or for EBCDIC systems running in UTF-8 mode. It runs from '0' to 'z'. */ #ifndef EBCDIC #define ESCAPES_FIRST CHAR_0 #define ESCAPES_LAST CHAR_z #define UPPER_CASE(c) (c-32) static const short int escapes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CHAR_COLON, CHAR_SEMICOLON, CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, CHAR_COMMERCIAL_AT, -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, -ESC_H, 0, 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P, -ESC_Q, -ESC_R, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X, 0, -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, CHAR_GRAVE_ACCENT, CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0, -ESC_h, 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p, 0, CHAR_CR, -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0, 0, -ESC_z }; #else /* This is the "abnormal" table for EBCDIC systems without UTF-8 support. It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a because it is defined as 'a', which of course picks up the ASCII value. */ #if 'a' == 0x81 /* Check for a real EBCDIC environment */ #define ESCAPES_FIRST CHAR_a #define ESCAPES_LAST CHAR_9 #define UPPER_CASE(c) (c+64) #else /* Testing in an ASCII environment */ #define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */ #define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */ #define UPPER_CASE(c) (c-32) #endif static const short int escapes[] = { /* 80 */ CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0, /* 88 */ -ESC_h, 0, 0, '{', 0, 0, 0, 0, /* 90 */ 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p, /* 98 */ 0, CHAR_CR, 0, '}', 0, 0, 0, 0, /* A0 */ 0, '~', -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0, /* A8 */ 0, -ESC_z, 0, 0, 0, '[', 0, 0, /* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', /* C0 */ '{', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* C8 */ -ESC_H, 0, 0, 0, 0, 0, 0, 0, /* D0 */ '}', 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P, /* D8 */ -ESC_Q, -ESC_R, 0, 0, 0, 0, 0, 0, /* E0 */ '\\', 0, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X, /* E8 */ 0, -ESC_Z, 0, 0, 0, 0, 0, 0, /* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* F8 */ 0, 0 }; /* We also need a table of characters that may follow \c in an EBCDIC environment for characters 0-31. */ static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; #endif /* EBCDIC */ /* Table of special "verbs" like (*PRUNE). This is a short table, so it is searched linearly. Put all the names into a single string, in order to reduce the number of relocations when a shared library is dynamically linked. The string is built from string macros so that it works in UTF-8 mode on EBCDIC platforms. */ typedef struct verbitem { unsigned int len; /* Length of verb name */ uint32_t meta; /* Base META_ code */ int has_arg; /* Argument requirement */ } verbitem; static const char verbnames[] = "\0" /* Empty name is a shorthand for MARK */ STRING_MARK0 STRING_ACCEPT0 STRING_F0 STRING_FAIL0 STRING_COMMIT0 STRING_PRUNE0 STRING_SKIP0 STRING_THEN; static const verbitem verbs[] = { { 0, META_MARK, +1 }, /* > 0 => must have an argument */ { 4, META_MARK, +1 }, { 6, META_ACCEPT, -1 }, /* < 0 => Optional argument, convert to pre-MARK */ { 1, META_FAIL, -1 }, { 4, META_FAIL, -1 }, { 6, META_COMMIT, 0 }, { 5, META_PRUNE, 0 }, /* Optional argument; bump META code if found */ { 4, META_SKIP, 0 }, { 4, META_THEN, 0 } }; static const int verbcount = sizeof(verbs)/sizeof(verbitem); /* Verb opcodes, indexed by their META code offset from META_MARK. */ static const uint32_t verbops[] = { OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE, OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG }; /* Table of "alpha assertions" like (*pla:...), similar to the (*VERB) table. */ typedef struct alasitem { unsigned int len; /* Length of name */ uint32_t meta; /* Base META_ code */ } alasitem; static const char alasnames[] = STRING_pla0 STRING_plb0 STRING_napla0 STRING_naplb0 STRING_nla0 STRING_nlb0 STRING_positive_lookahead0 STRING_positive_lookbehind0 STRING_non_atomic_positive_lookahead0 STRING_non_atomic_positive_lookbehind0 STRING_negative_lookahead0 STRING_negative_lookbehind0 STRING_atomic0 STRING_sr0 STRING_asr0 STRING_script_run0 STRING_atomic_script_run; static const alasitem alasmeta[] = { { 3, META_LOOKAHEAD }, { 3, META_LOOKBEHIND }, { 5, META_LOOKAHEAD_NA }, { 5, META_LOOKBEHIND_NA }, { 3, META_LOOKAHEADNOT }, { 3, META_LOOKBEHINDNOT }, { 18, META_LOOKAHEAD }, { 19, META_LOOKBEHIND }, { 29, META_LOOKAHEAD_NA }, { 30, META_LOOKBEHIND_NA }, { 18, META_LOOKAHEADNOT }, { 19, META_LOOKBEHINDNOT }, { 6, META_ATOMIC }, { 2, META_SCRIPT_RUN }, /* sr = script run */ { 3, META_ATOMIC_SCRIPT_RUN }, /* asr = atomic script run */ { 10, META_SCRIPT_RUN }, /* script run */ { 17, META_ATOMIC_SCRIPT_RUN } /* atomic script run */ }; static const int alascount = sizeof(alasmeta)/sizeof(alasitem); /* Offsets from OP_STAR for case-independent and negative repeat opcodes. */ static uint32_t chartypeoffset[] = { OP_STAR - OP_STAR, OP_STARI - OP_STAR, OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR }; /* Tables of names of POSIX character classes and their lengths. The names are now all in a single string, to reduce the number of relocations when a shared library is dynamically loaded. The list of lengths is terminated by a zero length entry. The first three must be alpha, lower, upper, as this is assumed for handling case independence. The indices for graph, print, and punct are needed, so identify them. */ static const char posix_names[] = STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 STRING_word0 STRING_xdigit; static const uint8_t posix_name_lengths[] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; #define PC_GRAPH 8 #define PC_PRINT 9 #define PC_PUNCT 10 /* Table of class bit maps for each POSIX class. Each class is formed from a base map, with an optional addition or removal of another map. Then, for some classes, there is some additional tweaking: for [:blank:] the vertical space characters are removed, and for [:alpha:] and [:alnum:] the underscore character is removed. The triples in the table consist of the base map offset, second map offset or -1 if no second map, and a non-negative value for map addition or a negative value for map subtraction (if there are two maps). The absolute value of the third field has these meanings: 0 => no tweaking, 1 => remove vertical space characters, 2 => remove underscore. */ static const int posix_class_maps[] = { cbit_word, cbit_digit, -2, /* alpha */ cbit_lower, -1, 0, /* lower */ cbit_upper, -1, 0, /* upper */ cbit_word, -1, 2, /* alnum - word without underscore */ cbit_print, cbit_cntrl, 0, /* ascii */ cbit_space, -1, 1, /* blank - a GNU extension */ cbit_cntrl, -1, 0, /* cntrl */ cbit_digit, -1, 0, /* digit */ cbit_graph, -1, 0, /* graph */ cbit_print, -1, 0, /* print */ cbit_punct, -1, 0, /* punct */ cbit_space, -1, 0, /* space */ cbit_word, -1, 0, /* word - a Perl extension */ cbit_xdigit,-1, 0 /* xdigit */ }; #ifdef SUPPORT_UNICODE /* The POSIX class Unicode property substitutes that are used in UCP mode must be in the order of the POSIX class names, defined above. */ static int posix_substitutes[] = { PT_GC, ucp_L, /* alpha */ PT_PC, ucp_Ll, /* lower */ PT_PC, ucp_Lu, /* upper */ PT_ALNUM, 0, /* alnum */ -1, 0, /* ascii, treat as non-UCP */ -1, 1, /* blank, treat as \h */ PT_PC, ucp_Cc, /* cntrl */ PT_PC, ucp_Nd, /* digit */ PT_PXGRAPH, 0, /* graph */ PT_PXPRINT, 0, /* print */ PT_PXPUNCT, 0, /* punct */ PT_PXSPACE, 0, /* space */ /* Xps is POSIX space, but from 8.34 */ PT_WORD, 0, /* word */ /* Perl and POSIX space are the same */ -1, 0 /* xdigit, treat as non-UCP */ }; #define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t))) #endif /* SUPPORT_UNICODE */ /* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset are allowed. */ #define PUBLIC_LITERAL_COMPILE_OPTIONS \ (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \ PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_MATCH_INVALID_UTF| \ PCRE2_NO_START_OPTIMIZE|PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF) #define PUBLIC_COMPILE_OPTIONS \ (PUBLIC_LITERAL_COMPILE_OPTIONS| \ PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \ PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \ PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \ PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY) #define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \ (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD) #define PUBLIC_COMPILE_EXTRA_OPTIONS \ (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \ PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL| \ PCRE2_EXTRA_ESCAPED_CR_IS_LF|PCRE2_EXTRA_ALT_BSUX| \ PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) /* Compile time error code numbers. They are given names so that they can more easily be tracked. When a new number is added, the tables called eint1 and eint2 in pcre2posix.c may need to be updated, and a new error text must be added to compile_error_texts in pcre2_error.c. Also, the error codes in pcre2.h.in must be updated - their values are exactly 100 greater than these values. */ enum { ERR0 = COMPILE_ERROR_BASE, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70, ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80, ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90, ERR91, ERR92, ERR93, ERR94, ERR95, ERR96, ERR97, ERR98, ERR99 }; /* This is a table of start-of-pattern options such as (*UTF) and settings such as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is generic and always supported. */ enum { PSO_OPT, /* Value is an option bit */ PSO_FLG, /* Value is a flag bit */ PSO_NL, /* Value is a newline type */ PSO_BSR, /* Value is a \R type */ PSO_LIMH, /* Read integer value for heap limit */ PSO_LIMM, /* Read integer value for match limit */ PSO_LIMD }; /* Read integer value for depth limit */ typedef struct pso { const uint8_t *name; uint16_t length; uint16_t type; uint32_t value; } pso; /* NB: STRING_UTFn_RIGHTPAR contains the length as well */ static pso pso_list[] = { { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF }, { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF }, { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP }, { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET }, { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET }, { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS }, { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR }, { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT }, { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE }, { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 }, { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 }, { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 }, { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 }, { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR }, { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF }, { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF }, { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY }, { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL }, { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF }, { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF }, { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE } }; /* This table is used when converting repeating opcodes into possessified versions as a result of an explicit possessive quantifier such as ++. A zero value means there is no possessified version - in those cases the item in question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT because all relevant opcodes are less than that. */ static const uint8_t opcode_possessify[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ 0, /* NOTI */ OP_POSSTAR, 0, /* STAR, MINSTAR */ OP_POSPLUS, 0, /* PLUS, MINPLUS */ OP_POSQUERY, 0, /* QUERY, MINQUERY */ OP_POSUPTO, 0, /* UPTO, MINUPTO */ 0, /* EXACT */ 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */ OP_POSSTARI, 0, /* STARI, MINSTARI */ OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */ OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */ OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */ 0, /* EXACTI */ 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */ OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */ OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */ OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */ OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */ 0, /* NOTEXACT */ 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */ OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */ OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */ OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */ OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */ 0, /* NOTEXACTI */ 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */ OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */ OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */ OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */ OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */ 0, /* TYPEEXACT */ 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */ OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */ OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */ OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */ OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */ 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */ 0, 0, 0, /* CLASS, NCLASS, XCLASS */ 0, 0, /* REF, REFI */ 0, 0, /* DNREF, DNREFI */ 0, 0 /* RECURSE, CALLOUT */ }; #ifdef DEBUG_SHOW_PARSED /************************************************* * Show the parsed pattern for debugging * *************************************************/ /* For debugging the pre-scan, this code, which outputs the parsed data vector, can be enabled. */ static void show_parsed(compile_block *cb) { uint32_t *pptr = cb->parsed_pattern; for (;;) { int max, min; PCRE2_SIZE offset; uint32_t i; uint32_t length; uint32_t meta_arg = META_DATA(*pptr); fprintf(stderr, "+++ %02d %.8x ", (int)(pptr - cb->parsed_pattern), *pptr); if (*pptr < META_END) { if (*pptr > 32 && *pptr < 128) fprintf(stderr, "%c", *pptr); pptr++; } else switch (META_CODE(*pptr++)) { default: fprintf(stderr, "**** OOPS - unknown META value - giving up ****\n"); return; case META_END: fprintf(stderr, "META_END\n"); return; case META_CAPTURE: fprintf(stderr, "META_CAPTURE %d", meta_arg); break; case META_RECURSE: GETOFFSET(offset, pptr); fprintf(stderr, "META_RECURSE %d %zd", meta_arg, offset); break; case META_BACKREF: if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg]; else GETOFFSET(offset, pptr); fprintf(stderr, "META_BACKREF %d %zd", meta_arg, offset); break; case META_ESCAPE: if (meta_arg == ESC_P || meta_arg == ESC_p) { uint32_t ptype = *pptr >> 16; uint32_t pvalue = *pptr++ & 0xffff; fprintf(stderr, "META \\%c %d %d", (meta_arg == ESC_P)? 'P':'p', ptype, pvalue); } else { uint32_t cc; /* There's just one escape we might have here that isn't negated in the escapes table. */ if (meta_arg == ESC_g) cc = CHAR_g; else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++) { if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break; } if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK; fprintf(stderr, "META \\%c", cc); } break; case META_MINMAX: min = *pptr++; max = *pptr++; if (max != REPEAT_UNLIMITED) fprintf(stderr, "META {%d,%d}", min, max); else fprintf(stderr, "META {%d,}", min); break; case META_MINMAX_QUERY: min = *pptr++; max = *pptr++; if (max != REPEAT_UNLIMITED) fprintf(stderr, "META {%d,%d}?", min, max); else fprintf(stderr, "META {%d,}?", min); break; case META_MINMAX_PLUS: min = *pptr++; max = *pptr++; if (max != REPEAT_UNLIMITED) fprintf(stderr, "META {%d,%d}+", min, max); else fprintf(stderr, "META {%d,}+", min); break; case META_BIGVALUE: fprintf(stderr, "META_BIGVALUE %.8x", *pptr++); break; case META_CIRCUMFLEX: fprintf(stderr, "META_CIRCUMFLEX"); break; case META_COND_ASSERT: fprintf(stderr, "META_COND_ASSERT"); break; case META_DOLLAR: fprintf(stderr, "META_DOLLAR"); break; case META_DOT: fprintf(stderr, "META_DOT"); break; case META_ASTERISK: fprintf(stderr, "META *"); break; case META_ASTERISK_QUERY: fprintf(stderr, "META *?"); break; case META_ASTERISK_PLUS: fprintf(stderr, "META *+"); break; case META_PLUS: fprintf(stderr, "META +"); break; case META_PLUS_QUERY: fprintf(stderr, "META +?"); break; case META_PLUS_PLUS: fprintf(stderr, "META ++"); break; case META_QUERY: fprintf(stderr, "META ?"); break; case META_QUERY_QUERY: fprintf(stderr, "META ??"); break; case META_QUERY_PLUS: fprintf(stderr, "META ?+"); break; case META_ATOMIC: fprintf(stderr, "META (?>"); break; case META_NOCAPTURE: fprintf(stderr, "META (?:"); break; case META_LOOKAHEAD: fprintf(stderr, "META (?="); break; case META_LOOKAHEADNOT: fprintf(stderr, "META (?!"); break; case META_LOOKAHEAD_NA: fprintf(stderr, "META (*napla:"); break; case META_SCRIPT_RUN: fprintf(stderr, "META (*sr:"); break; case META_KET: fprintf(stderr, "META )"); break; case META_ALT: fprintf(stderr, "META | %d", meta_arg); break; case META_CLASS: fprintf(stderr, "META ["); break; case META_CLASS_NOT: fprintf(stderr, "META [^"); break; case META_CLASS_END: fprintf(stderr, "META ]"); break; case META_CLASS_EMPTY: fprintf(stderr, "META []"); break; case META_CLASS_EMPTY_NOT: fprintf(stderr, "META [^]"); break; case META_RANGE_LITERAL: fprintf(stderr, "META - (literal)"); break; case META_RANGE_ESCAPED: fprintf(stderr, "META - (escaped)"); break; case META_POSIX: fprintf(stderr, "META_POSIX %d", *pptr++); break; case META_POSIX_NEG: fprintf(stderr, "META_POSIX_NEG %d", *pptr++); break; case META_ACCEPT: fprintf(stderr, "META (*ACCEPT)"); break; case META_FAIL: fprintf(stderr, "META (*FAIL)"); break; case META_COMMIT: fprintf(stderr, "META (*COMMIT)"); break; case META_PRUNE: fprintf(stderr, "META (*PRUNE)"); break; case META_SKIP: fprintf(stderr, "META (*SKIP)"); break; case META_THEN: fprintf(stderr, "META (*THEN)"); break; case META_OPTIONS: fprintf(stderr, "META_OPTIONS 0x%02x", *pptr++); break; case META_LOOKBEHIND: fprintf(stderr, "META (?<= %d offset=", meta_arg); GETOFFSET(offset, pptr); fprintf(stderr, "%zd", offset); break; case META_LOOKBEHIND_NA: fprintf(stderr, "META (*naplb: %d offset=", meta_arg); GETOFFSET(offset, pptr); fprintf(stderr, "%zd", offset); break; case META_LOOKBEHINDNOT: fprintf(stderr, "META (?="); fprintf(stderr, "%d.", *pptr++); fprintf(stderr, "%d)", *pptr++); break; case META_COND_NAME: fprintf(stderr, "META (?() length=%d offset=", *pptr++); GETOFFSET(offset, pptr); fprintf(stderr, "%zd", offset); break; case META_COND_RNAME: fprintf(stderr, "META (?(R&name) length=%d offset=", *pptr++); GETOFFSET(offset, pptr); fprintf(stderr, "%zd", offset); break; /* This is kept as a name, because it might be. */ case META_COND_RNUMBER: fprintf(stderr, "META (?(Rnumber) length=%d offset=", *pptr++); GETOFFSET(offset, pptr); fprintf(stderr, "%zd", offset); break; case META_MARK: fprintf(stderr, "META (*MARK:"); goto SHOWARG; case META_COMMIT_ARG: fprintf(stderr, "META (*COMMIT:"); goto SHOWARG; case META_PRUNE_ARG: fprintf(stderr, "META (*PRUNE:"); goto SHOWARG; case META_SKIP_ARG: fprintf(stderr, "META (*SKIP:"); goto SHOWARG; case META_THEN_ARG: fprintf(stderr, "META (*THEN:"); SHOWARG: length = *pptr++; for (i = 0; i < length; i++) { uint32_t cc = *pptr++; if (cc > 32 && cc < 128) fprintf(stderr, "%c", cc); else fprintf(stderr, "\\x{%x}", cc); } fprintf(stderr, ") length=%u", length); break; } fprintf(stderr, "\n"); } return; } #endif /* DEBUG_SHOW_PARSED */ /************************************************* * Copy compiled code * *************************************************/ /* Compiled JIT code cannot be copied, so the new compiled block has no associated JIT data. */ PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION pcre2_code_copy(const pcre2_code *code) { PCRE2_SIZE* ref_count; pcre2_code *newcode; if (code == NULL) return NULL; newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); if (newcode == NULL) return NULL; memcpy(newcode, code, code->blocksize); newcode->executable_jit = NULL; /* If the code is one that has been deserialized, increment the reference count in the decoded tables. */ if ((code->flags & PCRE2_DEREF_TABLES) != 0) { ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH); (*ref_count)++; } return newcode; } /************************************************* * Copy compiled code and character tables * *************************************************/ /* Compiled JIT code cannot be copied, so the new compiled block has no associated JIT data. This version of code_copy also makes a separate copy of the character tables. */ PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION pcre2_code_copy_with_tables(const pcre2_code *code) { PCRE2_SIZE* ref_count; pcre2_code *newcode; uint8_t *newtables; if (code == NULL) return NULL; newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); if (newcode == NULL) return NULL; memcpy(newcode, code, code->blocksize); newcode->executable_jit = NULL; newtables = code->memctl.malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE), code->memctl.memory_data); if (newtables == NULL) { code->memctl.free((void *)newcode, code->memctl.memory_data); return NULL; } memcpy(newtables, code->tables, TABLES_LENGTH); ref_count = (PCRE2_SIZE *)(newtables + TABLES_LENGTH); *ref_count = 1; newcode->tables = newtables; newcode->flags |= PCRE2_DEREF_TABLES; return newcode; } /************************************************* * Free compiled code * *************************************************/ PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_code_free(pcre2_code *code) { PCRE2_SIZE* ref_count; if (code != NULL) { if (code->executable_jit != NULL) PRIV(jit_free)(code->executable_jit, &code->memctl); if ((code->flags & PCRE2_DEREF_TABLES) != 0) { /* Decoded tables belong to the codes after deserialization, and they must be freed when there are no more references to them. The *ref_count should always be > 0. */ ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH); if (*ref_count > 0) { (*ref_count)--; if (*ref_count == 0) code->memctl.free((void *)code->tables, code->memctl.memory_data); } } code->memctl.free(code, code->memctl.memory_data); } } /************************************************* * Read a number, possibly signed * *************************************************/ /* This function is used to read numbers in the pattern. The initial pointer must be the sign or first digit of the number. When relative values (introduced by + or -) are allowed, they are relative group numbers, and the result must be greater than zero. Arguments: ptrptr points to the character pointer variable ptrend points to the end of the input string allow_sign if < 0, sign not allowed; if >= 0, sign is relative to this max_value the largest number allowed max_error the error to give for an over-large number intptr where to put the result errcodeptr where to put an error code Returns: TRUE - a number was read FALSE - errorcode == 0 => no number was found errorcode != 0 => an error occurred */ static BOOL read_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign, uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr) { int sign = 0; uint32_t n = 0; PCRE2_SPTR ptr = *ptrptr; BOOL yield = FALSE; *errorcodeptr = 0; if (allow_sign >= 0 && ptr < ptrend) { if (*ptr == CHAR_PLUS) { sign = +1; max_value -= allow_sign; ptr++; } else if (*ptr == CHAR_MINUS) { sign = -1; ptr++; } } if (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE; while (ptr < ptrend && IS_DIGIT(*ptr)) { n = n * 10 + *ptr++ - CHAR_0; if (n > max_value) { *errorcodeptr = max_error; goto EXIT; } } if (allow_sign >= 0 && sign != 0) { if (n == 0) { *errorcodeptr = ERR26; /* +0 and -0 are not allowed */ goto EXIT; } if (sign > 0) n += allow_sign; else if ((int)n > allow_sign) { *errorcodeptr = ERR15; /* Non-existent subpattern */ goto EXIT; } else n = allow_sign + 1 - n; } yield = TRUE; EXIT: *intptr = n; *ptrptr = ptr; return yield; } /************************************************* * Read repeat counts * *************************************************/ /* Read an item of the form {n,m} and return the values if non-NULL pointers are supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a larger value is used for "unlimited". We have to use signed arguments for read_number() because it is capable of returning a signed value. Arguments: ptrptr points to pointer to character after'{' ptrend pointer to end of input minp if not NULL, pointer to int for min maxp if not NULL, pointer to int for max (-1 if no max) returned as -1 if no max errorcodeptr points to error code variable Returns: FALSE if not a repeat quantifier, errorcode set zero FALSE on error, with errorcode set non-zero TRUE on success, with pointer updated to point after '}' */ static BOOL read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp, uint32_t *maxp, int *errorcodeptr) { PCRE2_SPTR p; BOOL yield = FALSE; BOOL had_comma = FALSE; int32_t min = 0; int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */ /* Check the syntax */ *errorcodeptr = 0; for (p = *ptrptr;; p++) { uint32_t c; if (p >= ptrend) return FALSE; c = *p; if (IS_DIGIT(c)) continue; if (c == CHAR_RIGHT_CURLY_BRACKET) break; if (c == CHAR_COMMA) { if (had_comma) return FALSE; had_comma = TRUE; } else return FALSE; } /* The only error from read_number() is for a number that is too big. */ p = *ptrptr; if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr)) goto EXIT; if (*p == CHAR_RIGHT_CURLY_BRACKET) { p++; max = min; } else { if (*(++p) != CHAR_RIGHT_CURLY_BRACKET) { if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, errorcodeptr)) goto EXIT; if (max < min) { *errorcodeptr = ERR4; goto EXIT; } } p++; } yield = TRUE; if (minp != NULL) *minp = (uint32_t)min; if (maxp != NULL) *maxp = (uint32_t)max; /* Update the pattern pointer */ EXIT: *ptrptr = p; return yield; } /************************************************* * Handle escapes * *************************************************/ /* This function is called when a \ has been encountered. It either returns a positive value for a simple escape such as \d, or 0 for a data character, which is placed in chptr. A backreference to group n is returned as negative n. On entry, ptr is pointing at the character after \. On exit, it points after the final code unit of the escape sequence. This function is also called from pcre2_substitute() to handle escape sequences in replacement strings. In this case, the cb argument is NULL, and in the case of escapes that have further processing, only sequences that define a data character are recognised. The isclass argument is not relevant; the options argument is the final value of the compiled pattern's options. Arguments: ptrptr points to the input position pointer ptrend points to the end of the input chptr points to a returned data character errorcodeptr points to the errorcode variable (containing zero) options the current options bits isclass TRUE if inside a character class cb compile data block or NULL when called from pcre2_substitute() Returns: zero => a data character positive => a special escape sequence negative => a numerical back reference on error, errorcodeptr is set non-zero */ int PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr, int *errorcodeptr, uint32_t options, uint32_t extra_options, BOOL isclass, compile_block *cb) { BOOL utf = (options & PCRE2_UTF) != 0; PCRE2_SPTR ptr = *ptrptr; uint32_t c, cc; int escape = 0; int i; /* If backslash is at the end of the string, it's an error. */ if (ptr >= ptrend) { *errorcodeptr = ERR1; return 0; } GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ *errorcodeptr = 0; /* Be optimistic */ /* Non-alphanumerics are literals, so we just leave the value in c. An initial value test saves a memory lookup for code points outside the alphanumeric range. */ if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ /* Otherwise, do a table lookup. Non-zero values need little processing here. A positive value is a literal value for something like \n. A negative value is the negation of one of the ESC_ macros that is passed back for handling by the calling function. Some extra checking is needed for \N because only \N{U+dddd} is supported. If the value is zero, further processing is handled below. */ else if ((i = escapes[c - ESCAPES_FIRST]) != 0) { if (i > 0) { c = (uint32_t)i; if (c == CHAR_CR && (extra_options & PCRE2_EXTRA_ESCAPED_CR_IS_LF) != 0) c = CHAR_LF; } else /* Negative table entry */ { escape = -i; /* Else return a special escape */ if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X)) cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ /* Perl supports \N{name} for character names and \N{U+dddd} for numerical Unicode code points, as well as plain \N for "not newline". PCRE does not support \N{name}. However, it does support quantification such as \N{2,3}, so if \N{ is not followed by U+dddd we check for a quantifier. */ if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) { PCRE2_SPTR p = ptr + 1; /* \N{U+ can be handled by the \x{ code. However, this construction is not valid in EBCDIC environments because it specifies a Unicode character, not a codepoint in the local code. For example \N{U+0041} must be "A" in all environments. Also, in Perl, \N{U+ forces Unicode casing semantics for the entire pattern, so allow it only in UTF (i.e. Unicode) mode. */ if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS) { #ifdef EBCDIC *errorcodeptr = ERR93; #else if (utf) { ptr = p + 1; escape = 0; /* Not a fancy escape after all */ goto COME_FROM_NU; } else *errorcodeptr = ERR93; #endif } /* Give an error if what follows is not a quantifier, but don't override an error set by the quantifier reader (e.g. number overflow). */ else { if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) && *errorcodeptr == 0) *errorcodeptr = ERR37; } } } } /* Escapes that need further processing, including those that are unknown, have a zero entry in the lookup table. When called from pcre2_substitute(), only \c, \o, and \x are recognized (\u and \U can never appear as they are used for case forcing). */ else { int s; PCRE2_SPTR oldptr; BOOL overflow; BOOL alt_bsux = ((options & PCRE2_ALT_BSUX) | (extra_options & PCRE2_EXTRA_ALT_BSUX)) != 0; /* Filter calls from pcre2_substitute(). */ if (cb == NULL) { if (c != CHAR_c && c != CHAR_o && c != CHAR_x) { *errorcodeptr = ERR3; return 0; } alt_bsux = FALSE; /* Do not modify \x handling */ } switch (c) { /* A number of Perl escapes are not handled by PCRE. We give an explicit error. */ case CHAR_F: case CHAR_l: case CHAR_L: *errorcodeptr = ERR37; break; /* \u is unrecognized when neither PCRE2_ALT_BSUX nor PCRE2_EXTRA_ALT_BSUX is set. Otherwise, \u must be followed by exactly four hex digits or, if PCRE2_EXTRA_ALT_BSUX is set, by any number of hex digits in braces. Otherwise it is a lowercase u letter. This gives some compatibility with ECMAScript (aka JavaScript). */ case CHAR_u: if (!alt_bsux) *errorcodeptr = ERR37; else { uint32_t xc; if (ptr >= ptrend) break; if (*ptr == CHAR_LEFT_CURLY_BRACKET && (extra_options & PCRE2_EXTRA_ALT_BSUX) != 0) { PCRE2_SPTR hptr = ptr + 1; cc = 0; while (hptr < ptrend && (xc = XDIGIT(*hptr)) != 0xff) { if ((cc & 0xf0000000) != 0) /* Test for 32-bit overflow */ { *errorcodeptr = ERR77; ptr = hptr; /* Show where */ break; /* *hptr != } will cause another break below */ } cc = (cc << 4) | xc; hptr++; } if (hptr == ptr + 1 || /* No hex digits */ hptr >= ptrend || /* Hit end of input */ *hptr != CHAR_RIGHT_CURLY_BRACKET) /* No } terminator */ break; /* Hex escape not recognized */ c = cc; /* Accept the code point */ ptr = hptr + 1; } else /* Must be exactly 4 hex digits */ { if (ptrend - ptr < 4) break; /* Less than 4 chars */ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ cc = (cc << 4) | xc; if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ cc = (cc << 4) | xc; if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */ c = (cc << 4) | xc; ptr += 4; } if (utf) { if (c > 0x10ffffU) *errorcodeptr = ERR77; else if (c >= 0xd800 && c <= 0xdfff && (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0) *errorcodeptr = ERR73; } else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77; } break; /* \U is unrecognized unless PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set, in which case it is an upper case letter. */ case CHAR_U: if (!alt_bsux) *errorcodeptr = ERR37; break; /* In a character class, \g is just a literal "g". Outside a character class, \g must be followed by one of a number of specific things: (1) A number, either plain or braced. If positive, it is an absolute backreference. If negative, it is a relative backreference. This is a Perl 5.10 feature. (2) Perl 5.10 also supports \g{name} as a reference to a named group. This is part of Perl's movement towards a unified syntax for back references. As this is synonymous with \k{name}, we fudge it up by pretending it really was \k{name}. (3) For Oniguruma compatibility we also support \g followed by a name or a number either in angle brackets or in single quotes. However, these are (possibly recursive) subroutine calls, _not_ backreferences. We return the ESC_g code. Summary: Return a negative number for a numerical back reference, ESC_k for a named back reference, and ESC_g for a named or numbered subroutine call. */ case CHAR_g: if (isclass) break; if (ptr >= ptrend) { *errorcodeptr = ERR57; break; } if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE) { escape = ESC_g; break; } /* If there is a brace delimiter, try to read a numerical reference. If there isn't one, assume we have a name and treat it as \k. */ if (*ptr == CHAR_LEFT_CURLY_BRACKET) { PCRE2_SPTR p = ptr + 1; if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, errorcodeptr)) { if (*errorcodeptr == 0) escape = ESC_k; /* No number found */ break; } if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) { *errorcodeptr = ERR57; break; } ptr = p + 1; } /* Read an undelimited number */ else { if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, errorcodeptr)) { if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */ break; } } if (s <= 0) { *errorcodeptr = ERR15; break; } escape = -s; break; /* The handling of escape sequences consisting of a string of digits starting with one that is not zero is not straightforward. Perl has changed over the years. Nowadays \g{} for backreferences and \o{} for octal are recommended to avoid the ambiguities in the old syntax. Outside a character class, the digits are read as a decimal number. If the number is less than 10, or if there are that many previous extracting left brackets, it is a back reference. Otherwise, up to three octal digits are read to form an escaped character code. Thus \123 is likely to be octal 123 (cf \0123, which is octal 012 followed by the literal 3). Inside a character class, \ followed by a digit is always either a literal 8 or 9 or an octal number. */ case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: if (!isclass) { oldptr = ptr; ptr--; /* Back to the digit */ /* As we know we are at a digit, the only possible error from read_number() is a number that is too large to be a group number. In this case we fall through handle this as not a group reference. If we have read a small enough number, check for a back reference. \1 to \9 are always back references. \8x and \9x are too; \1x to \7x are octal escapes if there are not that many previous captures. */ if (read_number(&ptr, ptrend, -1, INT_MAX/10 - 1, 0, &s, errorcodeptr) && (s < 10 || oldptr[-1] >= CHAR_8 || s <= (int)cb->bracount)) { if (s > (int)MAX_GROUP_NUMBER) *errorcodeptr = ERR61; else escape = -s; /* Indicates a back reference */ break; } ptr = oldptr; /* Put the pointer back and fall through */ } /* Handle a digit following \ when the number is not a back reference, or we are within a character class. If the first digit is 8 or 9, Perl used to generate a binary zero and then treat the digit as a following literal. At least by Perl 5.18 this changed so as not to insert the binary zero. */ if (c >= CHAR_8) break; /* Fall through */ /* \0 always starts an octal number, but we may drop through to here with a larger first octal digit. The original code used just to take the least significant 8 bits of octal numbers (I think this is what early Perls used to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, but no more than 3 octal digits. */ case CHAR_0: c -= CHAR_0; while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) c = c * 8 + *ptr++ - CHAR_0; #if PCRE2_CODE_UNIT_WIDTH == 8 if (!utf && c > 0xff) *errorcodeptr = ERR51; #endif break; /* \o is a relatively new Perl feature, supporting a more general way of specifying character codes in octal. The only supported form is \o{ddd}. */ case CHAR_o: if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET) { ptr--; *errorcodeptr = ERR55; } else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) *errorcodeptr = ERR78; else { c = 0; overflow = FALSE; while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) { cc = *ptr++; if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x20000000l) { overflow = TRUE; break; } #endif c = (c << 3) + (cc - CHAR_0); #if PCRE2_CODE_UNIT_WIDTH == 8 if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; } #elif PCRE2_CODE_UNIT_WIDTH == 16 if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } #elif PCRE2_CODE_UNIT_WIDTH == 32 if (utf && c > 0x10ffffU) { overflow = TRUE; break; } #endif } if (overflow) { while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; *errorcodeptr = ERR34; } else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) { if (utf && c >= 0xd800 && c <= 0xdfff && (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0) { ptr--; *errorcodeptr = ERR73; } } else { ptr--; *errorcodeptr = ERR64; } } break; /* When PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set, \x must be followed by two hexadecimal digits. Otherwise it is a lowercase x letter. */ case CHAR_x: if (alt_bsux) { uint32_t xc; if (ptrend - ptr < 2) break; /* Less than 2 characters */ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ c = (cc << 4) | xc; ptr += 2; } /* Handle \x in Perl's style. \x{ddd} is a character code which can be greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex digits. If not, { used to be treated as a data character. However, Perl seems to read hex digits up to the first non-such, and ignore the rest, so that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE now gives an error. */ else { if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) { #ifndef EBCDIC COME_FROM_NU: #endif if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) { *errorcodeptr = ERR78; break; } c = 0; overflow = FALSE; while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff) { ptr++; if (c == 0 && cc == 0) continue; /* Leading zeroes */ #if PCRE2_CODE_UNIT_WIDTH == 32 if (c >= 0x10000000l) { overflow = TRUE; break; } #endif c = (c << 4) | cc; if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR)) { overflow = TRUE; break; } } if (overflow) { while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++; *errorcodeptr = ERR34; } else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) { if (utf && c >= 0xd800 && c <= 0xdfff && (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0) { ptr--; *errorcodeptr = ERR73; } } /* If the sequence of hex digits does not end with '}', give an error. We used just to recognize this construct and fall through to the normal \x handling, but nowadays Perl gives an error, which seems much more sensible, so we do too. */ else { ptr--; *errorcodeptr = ERR67; } } /* End of \x{} processing */ /* Read a up to two hex digits after \x */ else { c = 0; if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ ptr++; c = cc; if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ ptr++; c = (c << 4) | cc; } /* End of \xdd handling */ } /* End of Perl-style \x handling */ break; /* The handling of \c is different in ASCII and EBCDIC environments. In an ASCII (or Unicode) environment, an error is given if the character following \c is not a printable ASCII character. Otherwise, the following character is upper-cased if it is a letter, and after that the 0x40 bit is flipped. The result is the value of the escape. In an EBCDIC environment the handling of \c is compatible with the specification in the perlebcdic document. The following character must be a letter or one of small number of special characters. These provide a means of defining the character values 0-31. For testing the EBCDIC handling of \c in an ASCII environment, recognize the EBCDIC value of 'c' explicitly. */ #if defined EBCDIC && 'a' != 0x81 case 0x83: #else case CHAR_c: #endif if (ptr >= ptrend) { *errorcodeptr = ERR2; break; } c = *ptr; if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); /* Handle \c in an ASCII/Unicode environment. */ #ifndef EBCDIC /* ASCII/UTF-8 coding */ if (c < 32 || c > 126) /* Excludes all non-printable ASCII */ { *errorcodeptr = ERR68; break; } c ^= 0x40; /* Handle \c in an EBCDIC environment. The special case \c? is converted to 255 (0xff) or 95 (0x5f) if other characters suggest we are using the POSIX-BC encoding. (This is the way Perl indicates that it handles \c?.) The other valid sequences correspond to a list of specific characters. */ #else if (c == CHAR_QUESTION_MARK) c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff; else { for (i = 0; i < 32; i++) { if (c == ebcdic_escape_c[i]) break; } if (i < 32) c = i; else *errorcodeptr = ERR68; } #endif /* EBCDIC */ ptr++; break; /* Any other alphanumeric following \ is an error. Perl gives an error only if in warning mode, but PCRE doesn't have a warning mode. */ default: *errorcodeptr = ERR3; *ptrptr = ptr - 1; /* Point to the character at fault */ return 0; } } /* Set the pointer to the next character before returning. */ *ptrptr = ptr; *chptr = c; return escape; } #ifdef SUPPORT_UNICODE /************************************************* * Handle \P and \p * *************************************************/ /* This function is called after \P or \p has been encountered, provided that PCRE2 is compiled with support for UTF and Unicode properties. On entry, the contents of ptrptr are pointing after the P or p. On exit, it is left pointing after the final code unit of the escape sequence. Arguments: ptrptr the pattern position pointer negptr a boolean that is set TRUE for negation else FALSE ptypeptr an unsigned int that is set to the type value pdataptr an unsigned int that is set to the detailed property value errorcodeptr the error code variable cb the compile data Returns: TRUE if the type value was found, or FALSE for an invalid type */ static BOOL get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr, uint16_t *pdataptr, int *errorcodeptr, compile_block *cb) { PCRE2_UCHAR c; PCRE2_SIZE i, bot, top; PCRE2_SPTR ptr = *ptrptr; PCRE2_UCHAR name[32]; if (ptr >= cb->end_pattern) goto ERROR_RETURN; c = *ptr++; *negptr = FALSE; /* \P or \p can be followed by a name in {}, optionally preceded by ^ for negation. */ if (c == CHAR_LEFT_CURLY_BRACKET) { if (ptr >= cb->end_pattern) goto ERROR_RETURN; if (*ptr == CHAR_CIRCUMFLEX_ACCENT) { *negptr = TRUE; ptr++; } for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++) { if (ptr >= cb->end_pattern) goto ERROR_RETURN; c = *ptr++; if (c == CHAR_NUL) goto ERROR_RETURN; if (c == CHAR_RIGHT_CURLY_BRACKET) break; name[i] = c; } if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; name[i] = 0; } /* Otherwise there is just one following character, which must be an ASCII letter. */ else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0) { name[0] = c; name[1] = 0; } else goto ERROR_RETURN; *ptrptr = ptr; /* Search for a recognized property name using binary chop. */ bot = 0; top = PRIV(utt_size); while (bot < top) { int r; i = (bot + top) >> 1; r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); if (r == 0) { *ptypeptr = PRIV(utt)[i].type; *pdataptr = PRIV(utt)[i].value; return TRUE; } if (r > 0) bot = i + 1; else top = i; } *errorcodeptr = ERR47; /* Unrecognized name */ return FALSE; ERROR_RETURN: /* Malformed \P or \p */ *errorcodeptr = ERR46; *ptrptr = ptr; return FALSE; } #endif /************************************************* * Check for POSIX class syntax * *************************************************/ /* This function is called when the sequence "[:" or "[." or "[=" is encountered in a character class. It checks whether this is followed by a sequence of characters terminated by a matching ":]" or ".]" or "=]". If we reach an unescaped ']' without the special preceding character, return FALSE. Originally, this function only recognized a sequence of letters between the terminators, but it seems that Perl recognizes any sequence of characters, though of course unknown POSIX names are subsequently rejected. Perl gives an "Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE didn't consider this to be a POSIX class. Likewise for [:1234:]. The problem in trying to be exactly like Perl is in the handling of escapes. We have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code below handles the special cases \\ and \], but does not try to do any other escape processing. This makes it different from Perl for cases such as [:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize "l\ower". This is a lesser evil than not diagnosing bad classes when Perl does, I think. A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. It seems that the appearance of a nested POSIX class supersedes an apparent external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or a digit. This is handled by returning FALSE if the start of a new group with the same terminator is encountered, since the next closing sequence must close the nested group, not the outer one. In Perl, unescaped square brackets may also appear as part of class names. For example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for [:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not seem right at all. PCRE does not allow closing square brackets in POSIX class names. Arguments: ptr pointer to the character after the initial [ (colon, dot, equals) ptrend pointer to the end of the pattern endptr where to return a pointer to the terminating ':', '.', or '=' Returns: TRUE or FALSE */ static BOOL check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr) { PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */ terminator = *ptr++; /* compiler warns about "non-constant" initializer. */ for (; ptrend - ptr >= 2; ptr++) { if (*ptr == CHAR_BACKSLASH && (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH)) ptr++; else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) || *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) { *endptr = ptr; return TRUE; } } return FALSE; } /************************************************* * Check POSIX class name * *************************************************/ /* This function is called to check the name given in a POSIX-style class entry such as [:alnum:]. Arguments: ptr points to the first letter len the length of the name Returns: a value representing the name, or -1 if unknown */ static int check_posix_name(PCRE2_SPTR ptr, int len) { const char *pn = posix_names; int yield = 0; while (posix_name_lengths[yield] != 0) { if (len == posix_name_lengths[yield] && PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield; pn += posix_name_lengths[yield] + 1; yield++; } return -1; } /************************************************* * Read a subpattern or VERB name * *************************************************/ /* This function is called from parse_regex() below whenever it needs to read the name of a subpattern or a (*VERB) or an (*alpha_assertion). The initial pointer must be to the character before the name. If that character is '*' we are reading a verb or alpha assertion name. The pointer is updated to point after the name, for a VERB or alpha assertion name, or after tha name's terminator for a subpattern name. Returning both the offset and the name pointer is redundant information, but some callers use one and some the other, so it is simplest just to return both. Arguments: ptrptr points to the character pointer variable ptrend points to the end of the input string utf true if the input is UTF-encoded terminator the terminator of a subpattern name must be this offsetptr where to put the offset from the start of the pattern nameptr where to put a pointer to the name in the input namelenptr where to put the length of the name errcodeptr where to put an error code cb pointer to the compile data block Returns: TRUE if a name was read FALSE otherwise, with error code set */ static BOOL read_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL utf, uint32_t terminator, PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr, int *errorcodeptr, compile_block *cb) { PCRE2_SPTR ptr = *ptrptr; BOOL is_group = (*ptr != CHAR_ASTERISK); if (++ptr >= ptrend) /* No characters in name */ { *errorcodeptr = is_group? ERR62: /* Subpattern name expected */ ERR60; /* Verb not recognized or malformed */ goto FAILED; } *nameptr = ptr; *offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern); /* In UTF mode, a group name may contain letters and decimal digits as defined by Unicode properties, and underscores, but must not start with a digit. */ #ifdef SUPPORT_UNICODE if (utf && is_group) { uint32_t c, type; GETCHAR(c, ptr); type = UCD_CHARTYPE(c); if (type == ucp_Nd) { *errorcodeptr = ERR44; goto FAILED; } for(;;) { if (type != ucp_Nd && PRIV(ucp_gentype)[type] != ucp_L && c != CHAR_UNDERSCORE) break; ptr++; FORWARDCHARTEST(ptr, ptrend); if (ptr >= ptrend) break; GETCHAR(c, ptr); type = UCD_CHARTYPE(c); } } else #else (void)utf; /* Avoid compiler warning */ #endif /* SUPPORT_UNICODE */ /* Handle non-group names and group names in non-UTF modes. A group name must not start with a digit. If either of the others start with a digit it just won't be recognized. */ { if (is_group && IS_DIGIT(*ptr)) { *errorcodeptr = ERR44; goto FAILED; } while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) { ptr++; } } /* Check name length */ if (ptr > *nameptr + MAX_NAME_SIZE) { *errorcodeptr = ERR48; goto FAILED; } *namelenptr = (uint32_t)(ptr - *nameptr); /* Subpattern names must not be empty, and their terminator is checked here. (What follows a verb or alpha assertion name is checked separately.) */ if (is_group) { if (ptr == *nameptr) { *errorcodeptr = ERR62; /* Subpattern name expected */ goto FAILED; } if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator) { *errorcodeptr = ERR42; goto FAILED; } ptr++; } *ptrptr = ptr; return TRUE; FAILED: *ptrptr = ptr; return FALSE; } /************************************************* * Manage callouts at start of cycle * *************************************************/ /* At the start of a new item in parse_regex() we are able to record the details of the previous item in a prior callout, and also to set up an automatic callout if enabled. Avoid having two adjacent automatic callouts, which would otherwise happen for items such as \Q that contribute nothing to the parsed pattern. Arguments: ptr current pattern pointer pcalloutptr points to a pointer to previous callout, or NULL auto_callout TRUE if auto_callouts are enabled parsed_pattern the parsed pattern pointer cb compile block Returns: possibly updated parsed_pattern pointer. */ static uint32_t * manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout, uint32_t *parsed_pattern, compile_block *cb) { uint32_t *previous_callout = *pcalloutptr; if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr - cb->start_pattern - (PCRE2_SIZE)previous_callout[1]); if (!auto_callout) previous_callout = NULL; else { if (previous_callout == NULL || previous_callout != parsed_pattern - 4 || previous_callout[3] != 255) { previous_callout = parsed_pattern; /* Set up new automatic callout */ parsed_pattern += 4; previous_callout[0] = META_CALLOUT_NUMBER; previous_callout[2] = 0; previous_callout[3] = 255; } previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); } *pcalloutptr = previous_callout; return parsed_pattern; } /************************************************* * Parse regex and identify named groups * *************************************************/ /* This function is called first of all. It scans the pattern and does two things: (1) It identifies capturing groups and makes a table of named capturing groups so that information about them is fully available to both the compiling scans. (2) It writes a parsed version of the pattern with comments omitted and escapes processed into the parsed_pattern vector. Arguments: ptr points to the start of the pattern options compiling dynamic options (may change during the scan) has_lookbehind points to a boolean, set TRUE if a lookbehind is found cb pointer to the compile data block Returns: zero on success or a non-zero error code, with the error offset placed in the cb field */ /* A structure and some flags for dealing with nested groups. */ typedef struct nest_save { uint16_t nest_depth; uint16_t reset_group; uint16_t max_group; uint16_t flags; uint32_t options; } nest_save; #define NSF_RESET 0x0001u #define NSF_CONDASSERT 0x0002u #define NSF_ATOMICSR 0x0004u /* Options that are changeable within the pattern must be tracked during parsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing, but all must be tracked so that META_OPTIONS items set the correct values for the main compiling phase. */ #define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \ PCRE2_UNGREEDY) /* States used for analyzing ranges in character classes. The two OK values must be last. */ enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL }; /* Only in 32-bit mode can there be literals > META_END. A macro encapsulates the storing of literal values in the main parsed pattern, where they can always be quantified. */ #if PCRE2_CODE_UNIT_WIDTH == 32 #define PARSED_LITERAL(c, p) \ { \ if (c >= META_END) *p++ = META_BIGVALUE; \ *p++ = c; \ okquantifier = TRUE; \ } #else #define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE; #endif /* Here's the actual function. */ static int parse_regex(PCRE2_SPTR ptr, uint32_t options, BOOL *has_lookbehind, compile_block *cb) { uint32_t c; uint32_t delimiter; uint32_t namelen; uint32_t class_range_state; uint32_t *verblengthptr = NULL; /* Value avoids compiler warning */ uint32_t *verbstartptr = NULL; uint32_t *previous_callout = NULL; uint32_t *parsed_pattern = cb->parsed_pattern; uint32_t *parsed_pattern_end = cb->parsed_pattern_end; uint32_t meta_quantifier = 0; uint32_t add_after_mark = 0; uint32_t extra_options = cb->cx->extra_options; uint16_t nest_depth = 0; int after_manual_callout = 0; int expect_cond_assert = 0; int errorcode = 0; int escape; int i; BOOL inescq = FALSE; BOOL inverbname = FALSE; BOOL utf = (options & PCRE2_UTF) != 0; BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0; BOOL isdupname; BOOL negate_class; BOOL okquantifier = FALSE; PCRE2_SPTR thisptr; PCRE2_SPTR name; PCRE2_SPTR ptrend = cb->end_pattern; PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */ named_group *ng; nest_save *top_nest, *end_nests; /* Insert leading items for word and line matching (features provided for the benefit of pcre2grep). */ if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) { *parsed_pattern++ = META_CIRCUMFLEX; *parsed_pattern++ = META_NOCAPTURE; } else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) { *parsed_pattern++ = META_ESCAPE + ESC_b; *parsed_pattern++ = META_NOCAPTURE; } /* If the pattern is actually a literal string, process it separately to avoid cluttering up the main loop. */ if ((options & PCRE2_LITERAL) != 0) { while (ptr < ptrend) { if (parsed_pattern >= parsed_pattern_end) { errorcode = ERR63; /* Internal error (parsed pattern overflow) */ goto FAILED; } thisptr = ptr; GETCHARINCTEST(c, ptr); if (auto_callout) parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, parsed_pattern, cb); PARSED_LITERAL(c, parsed_pattern); } goto PARSED_END; } /* Process a real regex which may contain meta-characters. */ top_nest = NULL; end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size); /* The size of the nest_save structure might not be a factor of the size of the workspace. Therefore we must round down end_nests so as to correctly avoid creating a nest_save that spans the end of the workspace. */ end_nests = (nest_save *)((char *)end_nests - ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save))); /* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */ if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED; /* Now scan the pattern */ while (ptr < ptrend) { int prev_expect_cond_assert; uint32_t min_repeat, max_repeat; uint32_t set, unset, *optset; uint32_t terminator; uint32_t prev_meta_quantifier; BOOL prev_okquantifier; PCRE2_SPTR tempptr; PCRE2_SIZE offset; if (parsed_pattern >= parsed_pattern_end) { errorcode = ERR63; /* Internal error (parsed pattern overflow) */ goto FAILED; } if (nest_depth > cb->cx->parens_nest_limit) { errorcode = ERR19; goto FAILED; /* Parentheses too deeply nested */ } /* Get next input character, save its position for callout handling. */ thisptr = ptr; GETCHARINCTEST(c, ptr); /* Copy quoted literals until \E, allowing for the possibility of automatic callouts, except when processing a (*VERB) "name". */ if (inescq) { if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) { inescq = FALSE; ptr++; /* Skip E */ } else { if (expect_cond_assert > 0) /* A literal is not allowed if we are */ { /* expecting a conditional assertion, */ ptr--; /* but an empty \Q\E sequence is OK. */ errorcode = ERR28; goto FAILED; } if (inverbname) { /* Don't use PARSED_LITERAL() because it */ #if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE; #endif *parsed_pattern++ = c; } else { if (after_manual_callout-- <= 0) parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, parsed_pattern, cb); PARSED_LITERAL(c, parsed_pattern); } meta_quantifier = 0; } continue; /* Next character */ } /* If we are processing the "name" part of a (*VERB:NAME) item, all characters up to the closing parenthesis are literals except when PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \Q and \E and escaped characters are allowed (no character types such as \d). If PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do this by not entering the special (*VERB:NAME) processing - they are then picked up below. Note that c is a character, not a code unit, so we must not use MAX_255 to test its size because MAX_255 tests code units and is assumed TRUE in 8-bit mode. */ if (inverbname && ( /* EITHER: not both options set */ ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) != (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) || #ifdef SUPPORT_UNICODE /* OR: character > 255 AND not Unicode Pattern White Space */ (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) || #endif /* OR: not a # comment or isspace() white space */ (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0 #ifdef SUPPORT_UNICODE /* and not CHAR_NEL when Unicode is supported */ && c != CHAR_NEL #endif ))) { PCRE2_SIZE verbnamelength; switch(c) { default: /* Don't use PARSED_LITERAL() because it */ #if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE; #endif *parsed_pattern++ = c; break; case CHAR_RIGHT_PARENTHESIS: inverbname = FALSE; /* This is the length in characters */ verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1); /* But the limit on the length is in code units */ if (ptr - verbnamestart - 1 > (int)MAX_MARK) { ptr--; errorcode = ERR76; goto FAILED; } *verblengthptr = (uint32_t)verbnamelength; /* If this name was on a verb such as (*ACCEPT) which does not continue, a (*MARK) was generated for the name. We now add the original verb as the next item. */ if (add_after_mark != 0) { *parsed_pattern++ = add_after_mark; add_after_mark = 0; } break; case CHAR_BACKSLASH: if ((options & PCRE2_ALT_VERBNAMES) != 0) { escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, cb->cx->extra_options, FALSE, cb); if (errorcode != 0) goto FAILED; } else escape = 0; /* Treat all as literal */ switch(escape) { case 0: /* Don't use PARSED_LITERAL() because it */ #if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE; #endif *parsed_pattern++ = c; break; case ESC_Q: inescq = TRUE; break; case ESC_E: /* Ignore */ break; default: errorcode = ERR40; /* Invalid in verb name */ goto FAILED; } } continue; /* Next character in pattern */ } /* Not a verb name character. At this point we must process everything that must not change the quantification state. This is mainly comments, but we handle \Q and \E here as well, so that an item such as A\Q\E+ is treated as A+, as in Perl. An isolated \E is ignored. */ if (c == CHAR_BACKSLASH && ptr < ptrend) { if (*ptr == CHAR_Q || *ptr == CHAR_E) { inescq = *ptr == CHAR_Q; ptr++; continue; } } /* Skip over whitespace and # comments in extended mode. Note that c is a character, not a code unit, so we must not use MAX_255 to test its size because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The whitespace characters are those designated as "Pattern White Space" by Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a subset of space characters that match \h and \v. */ if ((options & PCRE2_EXTENDED) != 0) { if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue; #ifdef SUPPORT_UNICODE if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue; #endif if (c == CHAR_NUMBER_SIGN) { while (ptr < ptrend) { if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ { /* IS_NEWLINE sets cb->nllen. */ ptr += cb->nllen; break; } ptr++; #ifdef SUPPORT_UNICODE if (utf) FORWARDCHARTEST(ptr, ptrend); #endif } continue; /* Next character in pattern */ } } /* Skip over bracketed comments */ if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 && ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN) { while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS); if (ptr >= ptrend) { errorcode = ERR18; /* A special error for missing ) in a comment */ goto FAILED; /* to make it easier to debug. */ } ptr++; continue; /* Next character in pattern */ } /* If the next item is not a quantifier, fill in length of any previous callout and create an auto callout if required. */ if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK && (c != CHAR_LEFT_CURLY_BRACKET || (tempptr = ptr, !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode)))) { if (after_manual_callout-- <= 0) parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, parsed_pattern, cb); } /* If expect_cond_assert is 2, we have just passed (?( and are expecting an assertion, possibly preceded by a callout. If the value is 1, we have just had the callout and expect an assertion. There must be at least 3 more characters in all cases. When expect_cond_assert is 2, we know that the current character is an opening parenthesis, as otherwise we wouldn't be here. However, when it is 1, we need to check, and it's easiest just to check always. Note that expect_cond_assert may be negative, since all callouts just decrement it. */ if (expect_cond_assert > 0) { BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 && (ptr[0] == CHAR_QUESTION_MARK || ptr[0] == CHAR_ASTERISK); if (ok) { if (ptr[0] == CHAR_ASTERISK) /* New alpha assertion format, possibly */ { ok = MAX_255(ptr[1]) && (cb->ctypes[ptr[1]] & ctype_lcletter) != 0; } else switch(ptr[1]) /* Traditional symbolic format */ { case CHAR_C: ok = expect_cond_assert == 2; break; case CHAR_EQUALS_SIGN: case CHAR_EXCLAMATION_MARK: break; case CHAR_LESS_THAN_SIGN: ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK; break; default: ok = FALSE; } } if (!ok) { ptr--; /* Adjust error offset */ errorcode = ERR28; goto FAILED; } } /* Remember whether we are expecting a conditional assertion, and set the default for this item. */ prev_expect_cond_assert = expect_cond_assert; expect_cond_assert = 0; /* Remember quantification status for the previous significant item, then set default for this item. */ prev_okquantifier = okquantifier; prev_meta_quantifier = meta_quantifier; okquantifier = FALSE; meta_quantifier = 0; /* If the previous significant item was a quantifier, adjust the parsed code if there is a following modifier. The base meta value is always followed by the PLUS and QUERY values, in that order. We do this here rather than after reading a quantifier so that intervening comments and /x whitespace can be ignored without having to replicate code. */ if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS)) { parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] = prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)? 0x00020000u : 0x00010000u); continue; /* Next character in pattern */ } /* Process the next item in the main part of a pattern. */ switch(c) { default: /* Non-special character */ PARSED_LITERAL(c, parsed_pattern); break; /* ---- Escape sequence ---- */ case CHAR_BACKSLASH: tempptr = ptr; escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, cb->cx->extra_options, FALSE, cb); if (errorcode != 0) { ESCAPE_FAILED: if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) goto FAILED; ptr = tempptr; if (ptr >= ptrend) c = CHAR_BACKSLASH; else { GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ } escape = 0; /* Treat as literal character */ } /* The escape was a data escape or literal character. */ if (escape == 0) { PARSED_LITERAL(c, parsed_pattern); } /* The escape was a back (or forward) reference. We keep the offset in order to give a more useful diagnostic for a bad forward reference. For references to groups numbered less than 10 we can't use more than two items in parsed_pattern because they may be just two characters in the input (and in a 64-bit world an offset may need two elements). So for them, the offset of the first occurrent is held in a special vector. */ else if (escape < 0) { offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1); escape = -escape; *parsed_pattern++ = META_BACKREF | (uint32_t)escape; if (escape < 10) { if (cb->small_ref_offset[escape] == PCRE2_UNSET) cb->small_ref_offset[escape] = offset; } else { PUTOFFSET(offset, parsed_pattern); } okquantifier = TRUE; } /* The escape was a character class such as \d etc. or other special escape indicator such as \A or \X. Most of them generate just a single parsed item, but \P and \p are followed by a 16-bit type and a 16-bit value. They are supported only when Unicode is available. The type and value are packed into a single 32-bit value so that the whole sequences uses only two elements in the parsed_vector. This is because the same coding is used if \d (for example) is turned into \p{Nd} when PCRE2_UCP is set. There are also some cases where the escape sequence is followed by a name: \k{name}, \k, and \k'name' are backreferences by name, and \g and \g'name' are subroutine calls by name; \g{name} is a synonym for \k{name}. Note that \g and \g'number' are handled by check_escape() and returned as a negative value (handled above). A name is coded as an offset into the pattern and a length. */ else switch (escape) { case ESC_C: #ifdef NEVER_BACKSLASH_C errorcode = ERR85; goto ESCAPE_FAILED; #else if ((options & PCRE2_NEVER_BACKSLASH_C) != 0) { errorcode = ERR83; goto ESCAPE_FAILED; } #endif okquantifier = TRUE; *parsed_pattern++ = META_ESCAPE + escape; break; case ESC_X: #ifndef SUPPORT_UNICODE errorcode = ERR45; /* Supported only with Unicode support */ goto ESCAPE_FAILED; #endif case ESC_H: case ESC_h: case ESC_N: case ESC_R: case ESC_V: case ESC_v: okquantifier = TRUE; *parsed_pattern++ = META_ESCAPE + escape; break; default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */ *parsed_pattern++ = META_ESCAPE + escape; break; /* Escapes that change in UCP mode. Note that PCRE2_UCP will never be set without Unicode support because it is checked when pcre2_compile() is called. */ case ESC_d: case ESC_D: case ESC_s: case ESC_S: case ESC_w: case ESC_W: okquantifier = TRUE; if ((options & PCRE2_UCP) == 0) { *parsed_pattern++ = META_ESCAPE + escape; } else { *parsed_pattern++ = META_ESCAPE + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? ESC_p : ESC_P); switch(escape) { case ESC_d: case ESC_D: *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; break; case ESC_s: case ESC_S: *parsed_pattern++ = PT_SPACE << 16; break; case ESC_w: case ESC_W: *parsed_pattern++ = PT_WORD << 16; break; } } break; /* Unicode property matching */ case ESC_P: case ESC_p: #ifdef SUPPORT_UNICODE { BOOL negated; uint16_t ptype = 0, pdata = 0; if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) goto ESCAPE_FAILED; if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; *parsed_pattern++ = META_ESCAPE + escape; *parsed_pattern++ = (ptype << 16) | pdata; okquantifier = TRUE; } #else errorcode = ERR45; goto ESCAPE_FAILED; #endif break; /* End \P and \p */ /* When \g is used with quotes or angle brackets as delimiters, it is a numerical or named subroutine call, and control comes here. When used with brace delimiters it is a numberical back reference and does not come here because check_escape() returns it directly as a reference. \k is always a named back reference. */ case ESC_g: case ESC_k: if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET && *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE)) { errorcode = (escape == ESC_g)? ERR57 : ERR69; goto ESCAPE_FAILED; } terminator = (*ptr == CHAR_LESS_THAN_SIGN)? CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; /* For a non-braced \g, check for a numerical recursion. */ if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET) { PCRE2_SPTR p = ptr + 1; if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, &errorcode)) { if (p >= ptrend || *p != terminator) { errorcode = ERR57; goto ESCAPE_FAILED; } ptr = p; goto SET_RECURSION; } if (errorcode != 0) goto ESCAPE_FAILED; } /* Not a numerical recursion */ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen, &errorcode, cb)) goto ESCAPE_FAILED; /* \k and \g when used with braces are back references, whereas \g used with quotes or angle brackets is a recursion */ *parsed_pattern++ = (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)? META_BACKREF_BYNAME : META_RECURSE_BYNAME; *parsed_pattern++ = namelen; PUTOFFSET(offset, parsed_pattern); okquantifier = TRUE; break; /* End special escape processing */ } break; /* End escape sequence processing */ /* ---- Single-character special items ---- */ case CHAR_CIRCUMFLEX_ACCENT: *parsed_pattern++ = META_CIRCUMFLEX; break; case CHAR_DOLLAR_SIGN: *parsed_pattern++ = META_DOLLAR; break; case CHAR_DOT: *parsed_pattern++ = META_DOT; okquantifier = TRUE; break; /* ---- Single-character quantifiers ---- */ case CHAR_ASTERISK: meta_quantifier = META_ASTERISK; goto CHECK_QUANTIFIER; case CHAR_PLUS: meta_quantifier = META_PLUS; goto CHECK_QUANTIFIER; case CHAR_QUESTION_MARK: meta_quantifier = META_QUERY; goto CHECK_QUANTIFIER; /* ---- Potential {n,m} quantifier ---- */ case CHAR_LEFT_CURLY_BRACKET: if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat, &errorcode)) { if (errorcode != 0) goto FAILED; /* Error in quantifier. */ PARSED_LITERAL(c, parsed_pattern); /* Not a quantifier */ break; /* No more quantifier processing */ } meta_quantifier = META_MINMAX; /* Fall through */ /* ---- Quantifier post-processing ---- */ /* Check that a quantifier is allowed after the previous item. */ CHECK_QUANTIFIER: if (!prev_okquantifier) { errorcode = ERR9; goto FAILED_BACK; } /* Most (*VERB)s are not allowed to be quantified, but an ungreedy quantifier can be useful for (*ACCEPT) - meaning "succeed on backtrack", a sort of negated (*COMMIT). We therefore allow (*ACCEPT) to be quantified by wrapping it in non-capturing brackets, but we have to allow for a preceding (*MARK) for when (*ACCEPT) has an argument. */ if (parsed_pattern[-1] == META_ACCEPT) { uint32_t *p; for (p = parsed_pattern - 1; p >= verbstartptr; p--) p[1] = p[0]; *verbstartptr = META_NOCAPTURE; parsed_pattern[1] = META_KET; parsed_pattern += 2; } /* Now we can put the quantifier into the parsed pattern vector. At this stage, we have only the basic quantifier. The check for a following + or ? modifier happens at the top of the loop, after any intervening comments have been removed. */ *parsed_pattern++ = meta_quantifier; if (c == CHAR_LEFT_CURLY_BRACKET) { *parsed_pattern++ = min_repeat; *parsed_pattern++ = max_repeat; } break; /* ---- Character class ---- */ case CHAR_LEFT_SQUARE_BRACKET: okquantifier = TRUE; /* In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is used for "start of word" and "end of word". As these are otherwise illegal sequences, we don't break anything by recognizing them. They are replaced by \b(?=\w) and \b(?<=\w) respectively. Sequences like [a[:<:]] are erroneous and are handled by the normal code below. */ if (ptrend - ptr >= 6 && (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 || PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0)) { *parsed_pattern++ = META_ESCAPE + ESC_b; if (ptr[2] == CHAR_LESS_THAN_SIGN) { *parsed_pattern++ = META_LOOKAHEAD; } else { *parsed_pattern++ = META_LOOKBEHIND; *has_lookbehind = TRUE; /* The offset is used only for the "non-fixed length" error; this won't occur here, so just store zero. */ PUTOFFSET((PCRE2_SIZE)0, parsed_pattern); } if ((options & PCRE2_UCP) == 0) *parsed_pattern++ = META_ESCAPE + ESC_w; else { *parsed_pattern++ = META_ESCAPE + ESC_p; *parsed_pattern++ = PT_WORD << 16; } *parsed_pattern++ = META_KET; ptr += 6; break; } /* PCRE supports POSIX class stuff inside a class. Perl gives an error if they are encountered at the top level, so we'll do that too. */ if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT || *ptr == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, ptrend, &tempptr)) { errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13; goto FAILED; } /* Process a regular character class. If the first character is '^', set the negation flag. If the first few characters (either before or after ^) are \Q\E or \E or space or tab in extended-more mode, we skip them too. This makes for compatibility with Perl. */ negate_class = FALSE; while (ptr < ptrend) { GETCHARINCTEST(c, ptr); if (c == CHAR_BACKSLASH) { if (ptr < ptrend && *ptr == CHAR_E) ptr++; else if (ptrend - ptr >= 3 && PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0) ptr += 3; else break; } else if ((options & PCRE2_EXTENDED_MORE) != 0 && (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */ continue; else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) negate_class = TRUE; else break; } /* Now the real contents of the class; c has the first "real" character. Empty classes are permitted only if the option is set. */ if (c == CHAR_RIGHT_SQUARE_BRACKET && (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) { *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY; break; /* End of class processing */ } /* Process a non-empty class. */ *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS; class_range_state = RANGE_NO; /* In an EBCDIC environment, Perl treats alphabetic ranges specially because there are holes in the encoding, and simply using the range A-Z (for example) would include the characters in the holes. This applies only to ranges where both values are literal; [\xC1-\xE9] is different to [A-Z] in this respect. In order to accommodate this, we keep track of whether character values are literal or not, and a state variable for handling ranges. */ /* Loop for the contents of the class */ for (;;) { BOOL char_is_literal = TRUE; /* Inside \Q...\E everything is literal except \E */ if (inescq) { if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) { inescq = FALSE; /* Reset literal state */ ptr++; /* Skip the 'E' */ goto CLASS_CONTINUE; } goto CLASS_LITERAL; } /* Skip over space and tab (only) in extended-more mode. */ if ((options & PCRE2_EXTENDED_MORE) != 0 && (c == CHAR_SPACE || c == CHAR_HT)) goto CLASS_CONTINUE; /* Handle POSIX class names. Perl allows a negation extension of the form [:^name:]. A square bracket that doesn't match the syntax is treated as a literal. We also recognize the POSIX constructions [.ch.] and [=ch=] ("collating elements") and fault them, as Perl 5.6 and 5.8 do. */ if (c == CHAR_LEFT_SQUARE_BRACKET && ptrend - ptr >= 3 && (*ptr == CHAR_COLON || *ptr == CHAR_DOT || *ptr == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, ptrend, &tempptr)) { BOOL posix_negate = FALSE; int posix_class; /* Perl treats a hyphen before a POSIX class as a literal, not the start of a range. However, it gives a warning in its warning mode. PCRE does not have a warning mode, so we give an error, because this is likely an error on the user's part. */ if (class_range_state == RANGE_STARTED) { errorcode = ERR50; goto FAILED; } if (*ptr != CHAR_COLON) { errorcode = ERR13; goto FAILED_BACK; } if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT) { posix_negate = TRUE; ptr++; } posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); if (posix_class < 0) { errorcode = ERR30; goto FAILED; } ptr = tempptr + 2; /* Perl treats a hyphen after a POSIX class as a literal, not the start of a range. However, it gives a warning in its warning mode unless the hyphen is the last character in the class. PCRE does not have a warning mode, so we give an error, because this is likely an error on the user's part. */ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) { errorcode = ERR50; goto FAILED; } /* Set "a hyphen is not the start of a range" for the -] case, and also in case the POSIX class is followed by \E or \Q\E (possibly repeated - fuzzers do that kind of thing) and *then* a hyphen. This causes that hyphen to be treated as a literal. I don't think it's worth setting up special apparatus to do otherwise. */ class_range_state = RANGE_NO; /* When PCRE2_UCP is set, some of the POSIX classes are converted to use Unicode properties \p or \P or, in one case, \h or \H. The substitutes table has two values per class, containing the type and value of a \p or \P item. The special cases are specified with a negative type: a non-zero value causes \h or \H to be used, and a zero value falls through to behave like a non-UCP POSIX class. */ #ifdef SUPPORT_UNICODE if ((options & PCRE2_UCP) != 0) { int ptype = posix_substitutes[2*posix_class]; int pvalue = posix_substitutes[2*posix_class + 1]; if (ptype >= 0) { *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p); *parsed_pattern++ = (ptype << 16) | pvalue; goto CLASS_CONTINUE; } if (pvalue != 0) { *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h); goto CLASS_CONTINUE; } /* Fall through */ } #endif /* SUPPORT_UNICODE */ /* Non-UCP POSIX class */ *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX; *parsed_pattern++ = posix_class; } /* Handle potential start of range */ else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED) { *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)? META_RANGE_LITERAL : META_RANGE_ESCAPED; class_range_state = RANGE_STARTED; } /* Handle a literal character */ else if (c != CHAR_BACKSLASH) { CLASS_LITERAL: if (class_range_state == RANGE_STARTED) { if (c == parsed_pattern[-2]) /* Optimize one-char range */ parsed_pattern--; else if (parsed_pattern[-2] > c) /* Check range is in order */ { errorcode = ERR8; goto FAILED_BACK; } else { if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL) parsed_pattern[-1] = META_RANGE_ESCAPED; PARSED_LITERAL(c, parsed_pattern); } class_range_state = RANGE_NO; } else /* Potential start of range */ { class_range_state = char_is_literal? RANGE_OK_LITERAL : RANGE_OK_ESCAPED; PARSED_LITERAL(c, parsed_pattern); } } /* Handle escapes in a class */ else { tempptr = ptr; escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, cb->cx->extra_options, TRUE, cb); if (errorcode != 0) { if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) goto FAILED; ptr = tempptr; if (ptr >= ptrend) c = CHAR_BACKSLASH; else { GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ } escape = 0; /* Treat as literal character */ } switch(escape) { case 0: /* Escaped character code point is in c */ char_is_literal = FALSE; goto CLASS_LITERAL; case ESC_b: c = CHAR_BS; /* \b is backspace in a class */ char_is_literal = FALSE; goto CLASS_LITERAL; case ESC_Q: inescq = TRUE; /* Enter literal mode */ goto CLASS_CONTINUE; case ESC_E: /* Ignore orphan \E */ goto CLASS_CONTINUE; case ESC_B: /* Always an error in a class */ case ESC_R: case ESC_X: errorcode = ERR7; ptr--; goto FAILED; } /* The second part of a range can be a single-character escape sequence (detected above), but not any of the other escapes. Perl treats a hyphen as a literal in such circumstances. However, in Perl's warning mode, a warning is given, so PCRE now faults it, as it is almost certainly a mistake on the user's part. */ if (class_range_state == RANGE_STARTED) { errorcode = ERR50; goto FAILED; /* Not CLASS_ESCAPE_FAILED; always an error */ } /* Of the remaining escapes, only those that define characters are allowed in a class. None may start a range. */ class_range_state = RANGE_NO; switch(escape) { case ESC_N: errorcode = ERR71; goto FAILED; case ESC_H: case ESC_h: case ESC_V: case ESC_v: *parsed_pattern++ = META_ESCAPE + escape; break; /* These escapes are converted to Unicode property tests when PCRE2_UCP is set. */ case ESC_d: case ESC_D: case ESC_s: case ESC_S: case ESC_w: case ESC_W: if ((options & PCRE2_UCP) == 0) { *parsed_pattern++ = META_ESCAPE + escape; } else { *parsed_pattern++ = META_ESCAPE + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? ESC_p : ESC_P); switch(escape) { case ESC_d: case ESC_D: *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; break; case ESC_s: case ESC_S: *parsed_pattern++ = PT_SPACE << 16; break; case ESC_w: case ESC_W: *parsed_pattern++ = PT_WORD << 16; break; } } break; /* Explicit Unicode property matching */ case ESC_P: case ESC_p: #ifdef SUPPORT_UNICODE { BOOL negated; uint16_t ptype = 0, pdata = 0; if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) goto FAILED; if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; *parsed_pattern++ = META_ESCAPE + escape; *parsed_pattern++ = (ptype << 16) | pdata; } #else errorcode = ERR45; goto FAILED; #endif break; /* End \P and \p */ default: /* All others are not allowed in a class */ errorcode = ERR7; ptr--; goto FAILED; } /* Perl gives a warning unless a following hyphen is the last character in the class. PCRE throws an error. */ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) { errorcode = ERR50; goto FAILED; } } /* Proceed to next thing in the class. */ CLASS_CONTINUE: if (ptr >= ptrend) { errorcode = ERR6; /* Missing terminating ']' */ goto FAILED; } GETCHARINCTEST(c, ptr); if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; } /* End of class-processing loop */ /* -] at the end of a class is a literal '-' */ if (class_range_state == RANGE_STARTED) { parsed_pattern[-1] = CHAR_MINUS; class_range_state = RANGE_NO; } *parsed_pattern++ = META_CLASS_END; break; /* End of character class */ /* ---- Opening parenthesis ---- */ case CHAR_LEFT_PARENTHESIS: if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; /* If ( is not followed by ? it is either a capture or a special verb or an alpha assertion or a positive non-atomic lookahead. */ if (*ptr != CHAR_QUESTION_MARK) { const char *vn; /* Handle capturing brackets (or non-capturing if auto-capture is turned off). */ if (*ptr != CHAR_ASTERISK) { nest_depth++; if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) { if (cb->bracount >= MAX_GROUP_NUMBER) { errorcode = ERR97; goto FAILED; } cb->bracount++; *parsed_pattern++ = META_CAPTURE | cb->bracount; } else *parsed_pattern++ = META_NOCAPTURE; } /* Do nothing for (* followed by end of pattern or ) so it gives a "bad quantifier" error rather than "(*MARK) must have an argument". */ else if (ptrend - ptr <= 1 || (c = ptr[1]) == CHAR_RIGHT_PARENTHESIS) break; /* Handle "alpha assertions" such as (*pla:...). Most of these are synonyms for the historical symbolic assertions, but the script run and non-atomic lookaround ones are new. They are distinguished by starting with a lower case letter. Checking both ends of the alphabet makes this work in all character codes. */ else if (CHMAX_255(c) && (cb->ctypes[c] & ctype_lcletter) != 0) { uint32_t meta; vn = alasnames; if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; if (ptr >= ptrend || *ptr != CHAR_COLON) { errorcode = ERR95; /* Malformed */ goto FAILED; } /* Scan the table of alpha assertion names */ for (i = 0; i < alascount; i++) { if (namelen == alasmeta[i].len && PRIV(strncmp_c8)(name, vn, namelen) == 0) break; vn += alasmeta[i].len + 1; } if (i >= alascount) { errorcode = ERR95; /* Alpha assertion not recognized */ goto FAILED; } /* Check for expecting an assertion condition. If so, only atomic lookaround assertions are valid. */ meta = alasmeta[i].meta; if (prev_expect_cond_assert > 0 && (meta < META_LOOKAHEAD || meta > META_LOOKBEHINDNOT)) { errorcode = (meta == META_LOOKAHEAD_NA || meta == META_LOOKBEHIND_NA)? ERR98 : ERR28; /* (Atomic) assertion expected */ goto FAILED; } /* The lookaround alphabetic synonyms can mostly be handled by jumping to the code that handles the traditional symbolic forms. */ switch(meta) { default: errorcode = ERR89; /* Unknown code; should never occur because */ goto FAILED; /* the meta values come from a table above. */ case META_ATOMIC: goto ATOMIC_GROUP; case META_LOOKAHEAD: goto POSITIVE_LOOK_AHEAD; case META_LOOKAHEAD_NA: goto POSITIVE_NONATOMIC_LOOK_AHEAD; case META_LOOKAHEADNOT: goto NEGATIVE_LOOK_AHEAD; case META_LOOKBEHIND: case META_LOOKBEHINDNOT: case META_LOOKBEHIND_NA: *parsed_pattern++ = meta; ptr--; goto POST_LOOKBEHIND; /* The script run facilities are handled here. Unicode support is required (give an error if not, as this is a security issue). Always record a META_SCRIPT_RUN item. Then, for the atomic version, insert META_ATOMIC and remember that we need two META_KETs at the end. */ case META_SCRIPT_RUN: case META_ATOMIC_SCRIPT_RUN: #ifdef SUPPORT_UNICODE *parsed_pattern++ = META_SCRIPT_RUN; nest_depth++; ptr++; if (meta == META_ATOMIC_SCRIPT_RUN) { *parsed_pattern++ = META_ATOMIC; if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); else if (++top_nest >= end_nests) { errorcode = ERR84; goto FAILED; } top_nest->nest_depth = nest_depth; top_nest->flags = NSF_ATOMICSR; top_nest->options = options & PARSE_TRACKED_OPTIONS; } break; #else /* SUPPORT_UNICODE */ errorcode = ERR96; goto FAILED; #endif } } /* ---- Handle (*VERB) and (*VERB:NAME) ---- */ else { vn = verbnames; if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; if (ptr >= ptrend || (*ptr != CHAR_COLON && *ptr != CHAR_RIGHT_PARENTHESIS)) { errorcode = ERR60; /* Malformed */ goto FAILED; } /* Scan the table of verb names */ for (i = 0; i < verbcount; i++) { if (namelen == verbs[i].len && PRIV(strncmp_c8)(name, vn, namelen) == 0) break; vn += verbs[i].len + 1; } if (i >= verbcount) { errorcode = ERR60; /* Verb not recognized */ goto FAILED; } /* An empty argument is treated as no argument. */ if (*ptr == CHAR_COLON && ptr + 1 < ptrend && ptr[1] == CHAR_RIGHT_PARENTHESIS) ptr++; /* Advance to the closing parens */ /* Check for mandatory non-empty argument; this is (*MARK) */ if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON) { errorcode = ERR66; goto FAILED; } /* Remember where this verb, possibly with a preceding (*MARK), starts, for handling quantified (*ACCEPT). */ verbstartptr = parsed_pattern; okquantifier = (verbs[i].meta == META_ACCEPT); /* It appears that Perl allows any characters whatsoever, other than a closing parenthesis, to appear in arguments ("names"), so we no longer insist on letters, digits, and underscores. Perl does not, however, do any interpretation within arguments, and has no means of including a closing parenthesis. PCRE supports escape processing but only when it is requested by an option. We set inverbname TRUE here, and let the main loop take care of this so that escape and \x processing is done by the main code above. */ if (*ptr++ == CHAR_COLON) /* Skip past : or ) */ { /* Some optional arguments can be treated as a preceding (*MARK) */ if (verbs[i].has_arg < 0) { add_after_mark = verbs[i].meta; *parsed_pattern++ = META_MARK; } /* The remaining verbs with arguments (except *MARK) need a different opcode. */ else { *parsed_pattern++ = verbs[i].meta + ((verbs[i].meta != META_MARK)? 0x00010000u:0); } /* Set up for reading the name in the main loop. */ verblengthptr = parsed_pattern++; verbnamestart = ptr; inverbname = TRUE; } else /* No verb "name" argument */ { *parsed_pattern++ = verbs[i].meta; } } /* End of (*VERB) handling */ break; /* Done with this parenthesis */ } /* End of groups that don't start with (? */ /* ---- Items starting (? ---- */ /* The type of item is determined by what follows (?. Handle (?| and option changes under "default" because both need a new block on the nest stack. Comments starting with (?# are handled above. Note that there is some ambiguity about the sequence (?- because if a digit follows it's a relative recursion or subroutine call whereas otherwise it's an option unsetting. */ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; switch(*ptr) { default: if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1])) goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */ /* We now have either (?| or a (possibly empty) option setting, optionally followed by a non-capturing group. */ nest_depth++; if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); else if (++top_nest >= end_nests) { errorcode = ERR84; goto FAILED; } top_nest->nest_depth = nest_depth; top_nest->flags = 0; top_nest->options = options & PARSE_TRACKED_OPTIONS; /* Start of non-capturing group that resets the capture count for each branch. */ if (*ptr == CHAR_VERTICAL_LINE) { top_nest->reset_group = (uint16_t)cb->bracount; top_nest->max_group = (uint16_t)cb->bracount; top_nest->flags |= NSF_RESET; cb->external_flags |= PCRE2_DUPCAPUSED; *parsed_pattern++ = META_NOCAPTURE; ptr++; } /* Scan for options imnsxJU to be set or unset. */ else { BOOL hyphenok = TRUE; uint32_t oldoptions = options; top_nest->reset_group = 0; top_nest->max_group = 0; set = unset = 0; optset = &set; /* ^ at the start unsets imnsx and disables the subsequent use of - */ if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT) { options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE); hyphenok = FALSE; ptr++; } while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) { switch (*ptr++) { case CHAR_MINUS: if (!hyphenok) { errorcode = ERR94; ptr--; /* Correct the offset */ goto FAILED; } optset = &unset; hyphenok = FALSE; break; case CHAR_J: /* Record that it changed in the external options */ *optset |= PCRE2_DUPNAMES; cb->external_flags |= PCRE2_JCHANGED; break; case CHAR_i: *optset |= PCRE2_CASELESS; break; case CHAR_m: *optset |= PCRE2_MULTILINE; break; case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break; case CHAR_s: *optset |= PCRE2_DOTALL; break; case CHAR_U: *optset |= PCRE2_UNGREEDY; break; /* If x appears twice it sets the extended extended option. */ case CHAR_x: *optset |= PCRE2_EXTENDED; if (ptr < ptrend && *ptr == CHAR_x) { *optset |= PCRE2_EXTENDED_MORE; ptr++; } break; default: errorcode = ERR11; ptr--; /* Correct the offset */ goto FAILED; } } /* If we are setting extended without extended-more, ensure that any existing extended-more gets unset. Also, unsetting extended must also unset extended-more. */ if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED || (unset & PCRE2_EXTENDED) != 0) unset |= PCRE2_EXTENDED_MORE; options = (options | set) & (~unset); /* If the options ended with ')' this is not the start of a nested group with option changes, so the options change at this level. In this case, if the previous level set up a nest block, discard the one we have just created. Otherwise adjust it for the previous level. If the options ended with ':' we are starting a non-capturing group, possibly with an options setting. */ if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; if (*ptr++ == CHAR_RIGHT_PARENTHESIS) { nest_depth--; /* This is not a nested group after all. */ if (top_nest > (nest_save *)(cb->start_workspace) && (top_nest-1)->nest_depth == nest_depth) top_nest--; else top_nest->nest_depth = nest_depth; } else *parsed_pattern++ = META_NOCAPTURE; /* If nothing changed, no need to record. */ if (options != oldoptions) { *parsed_pattern++ = META_OPTIONS; *parsed_pattern++ = options; } } /* End options processing */ break; /* End default case after (? */ /* ---- Python syntax support ---- */ case CHAR_P: if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; /* (?P is the same as (?, which defines a named group. */ if (*ptr == CHAR_LESS_THAN_SIGN) { terminator = CHAR_GREATER_THAN_SIGN; goto DEFINE_NAME; } /* (?P>name) is the same as (?&name), which is a recursion or subroutine call. */ if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME; /* (?P=name) is the same as \k, a back reference by name. Anything else after (?P is an error. */ if (*ptr != CHAR_EQUALS_SIGN) { errorcode = ERR41; goto FAILED; } if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; *parsed_pattern++ = META_BACKREF_BYNAME; *parsed_pattern++ = namelen; PUTOFFSET(offset, parsed_pattern); okquantifier = TRUE; break; /* End of (?P processing */ /* ---- Recursion/subroutine calls by number ---- */ case CHAR_R: i = 0; /* (?R) == (?R0) */ ptr++; if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) { errorcode = ERR58; goto FAILED; } goto SET_RECURSION; /* An item starting (?- followed by a digit comes here via the "default" case because (?- followed by a non-digit is an options setting. */ case CHAR_PLUS: if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1])) { errorcode = ERR29; /* Missing number */ goto FAILED; } /* Fall through */ case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: RECURSION_BYNUMBER: if (!read_number(&ptr, ptrend, (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */ MAX_GROUP_NUMBER, ERR61, &i, &errorcode)) goto FAILED; if (i < 0) /* NB (?0) is permitted */ { errorcode = ERR15; /* Unknown group */ goto FAILED_BACK; } if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) goto UNCLOSED_PARENTHESIS; SET_RECURSION: *parsed_pattern++ = META_RECURSE | (uint32_t)i; offset = (PCRE2_SIZE)(ptr - cb->start_pattern); ptr++; PUTOFFSET(offset, parsed_pattern); okquantifier = TRUE; break; /* End of recursive call by number handling */ /* ---- Recursion/subroutine calls by name ---- */ case CHAR_AMPERSAND: RECURSE_BY_NAME: if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; *parsed_pattern++ = META_RECURSE_BYNAME; *parsed_pattern++ = namelen; PUTOFFSET(offset, parsed_pattern); okquantifier = TRUE; break; /* ---- Callout with numerical or string argument ---- */ case CHAR_C: if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; /* If the previous item was a condition starting (?(? an assertion, optionally preceded by a callout, is expected. This is checked later on, during actual compilation. However we need to identify this kind of assertion in this pass because it must not be qualified. The value of expect_cond_assert is set to 2 after (?(? is processed. We decrement it for a callout - still leaving a positive value that identifies the assertion. Multiple callouts or any other items will make it zero or less, which doesn't matter because they will cause an error later. */ expect_cond_assert = prev_expect_cond_assert - 1; /* If previous_callout is not NULL, it means this follows a previous callout. If it was a manual callout, do nothing; this means its "length of next pattern item" field will remain zero. If it was an automatic callout, abolish it. */ if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 && previous_callout == parsed_pattern - 4 && parsed_pattern[-1] == 255) parsed_pattern = previous_callout; /* Save for updating next pattern item length, and skip one item before completing. */ previous_callout = parsed_pattern; after_manual_callout = 1; /* Handle a string argument; specific delimiter is required. */ if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) { PCRE2_SIZE calloutlength; PCRE2_SPTR startptr = ptr; delimiter = 0; for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) { if (*ptr == PRIV(callout_start_delims)[i]) { delimiter = PRIV(callout_end_delims)[i]; break; } } if (delimiter == 0) { errorcode = ERR82; goto FAILED; } *parsed_pattern = META_CALLOUT_STRING; parsed_pattern += 3; /* Skip pattern info */ for (;;) { if (++ptr >= ptrend) { errorcode = ERR81; ptr = startptr; /* To give a more useful message */ goto FAILED; } if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter)) break; } calloutlength = (PCRE2_SIZE)(ptr - startptr); if (calloutlength > UINT32_MAX) { errorcode = ERR72; goto FAILED; } *parsed_pattern++ = (uint32_t)calloutlength; offset = (PCRE2_SIZE)(startptr - cb->start_pattern); PUTOFFSET(offset, parsed_pattern); } /* Handle a callout with an optional numerical argument, which must be less than or equal to 255. A missing argument gives 0. */ else { int n = 0; *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */ parsed_pattern += 3; /* Skip pattern info */ while (ptr < ptrend && IS_DIGIT(*ptr)) { n = n * 10 + *ptr++ - CHAR_0; if (n > 255) { errorcode = ERR38; goto FAILED; } } *parsed_pattern++ = n; } /* Both formats must have a closing parenthesis */ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) { errorcode = ERR39; goto FAILED; } ptr++; /* Remember the offset to the next item in the pattern, and set a default length. This should get updated after the next item is read. */ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); previous_callout[2] = 0; break; /* End callout */ /* ---- Conditional group ---- */ /* A condition can be an assertion, a number (referring to a numbered group's having been set), a name (referring to a named group), or 'R', referring to overall recursion. R and R&name are also permitted for recursion state tests. Numbers may be preceded by + or - to specify a relative group number. There are several syntaxes for testing a named group: (?(name)) is used by Python; Perl 5.10 onwards uses (?() or (?('name')). There are two unfortunate ambiguities. 'R' can be the recursive thing or the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be the Perl DEFINE feature or the Python named test. We look for a name first; if not found, we try the other case. For compatibility with auto-callouts, we allow a callout to be specified before a condition that is an assertion. */ case CHAR_LEFT_PARENTHESIS: if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; nest_depth++; /* If the next character is ? or * there must be an assertion next (optionally preceded by a callout). We do not check this here, but instead we set expect_cond_assert to 2. If this is still greater than zero (callouts decrement it) when the next assertion is read, it will be marked as a condition that must not be repeated. A value greater than zero also causes checking that an assertion (possibly with callout) follows. */ if (*ptr == CHAR_QUESTION_MARK || *ptr == CHAR_ASTERISK) { *parsed_pattern++ = META_COND_ASSERT; ptr--; /* Pull pointer back to the opening parenthesis. */ expect_cond_assert = 2; break; /* End of conditional */ } /* Handle (?([+-]number)... */ if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, &errorcode)) { if (i <= 0) { errorcode = ERR15; goto FAILED; } *parsed_pattern++ = META_COND_NUMBER; offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); PUTOFFSET(offset, parsed_pattern); *parsed_pattern++ = i; } else if (errorcode != 0) goto FAILED; /* Number too big */ /* No number found. Handle the special case (?(VERSION[>]=n.m)... */ else if (ptrend - ptr >= 10 && PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 && ptr[7] != CHAR_RIGHT_PARENTHESIS) { uint32_t ge = 0; int major = 0; int minor = 0; ptr += 7; if (*ptr == CHAR_GREATER_THAN_SIGN) { ge = 1; ptr++; } /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT references its argument twice. */ if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) goto BAD_VERSION_CONDITION; if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode)) goto FAILED; if (ptr >= ptrend) goto BAD_VERSION_CONDITION; if (*ptr == CHAR_DOT) { if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION; minor = (*ptr++ - CHAR_0) * 10; if (ptr >= ptrend) goto BAD_VERSION_CONDITION; if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0; if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) goto BAD_VERSION_CONDITION; } *parsed_pattern++ = META_COND_VERSION; *parsed_pattern++ = ge; *parsed_pattern++ = major; *parsed_pattern++ = minor; } /* All the remaining cases now require us to read a name. We cannot at this stage distinguish ambiguous cases such as (?(R12) which might be a recursion test by number or a name, because the named groups have not yet all been identified. Those cases are treated as names, but given a different META code. */ else { BOOL was_r_ampersand = FALSE; if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND) { terminator = CHAR_RIGHT_PARENTHESIS; was_r_ampersand = TRUE; ptr++; } else if (*ptr == CHAR_LESS_THAN_SIGN) terminator = CHAR_GREATER_THAN_SIGN; else if (*ptr == CHAR_APOSTROPHE) terminator = CHAR_APOSTROPHE; else { terminator = CHAR_RIGHT_PARENTHESIS; ptr--; /* Point to char before name */ } if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; /* Handle (?(R&name) */ if (was_r_ampersand) { *parsed_pattern = META_COND_RNAME; ptr--; /* Back to closing parens */ } /* Handle (?(name). If the name is "DEFINE" we identify it with a special code. Likewise if the name consists of R followed only by digits. Otherwise, handle it like a quoted name. */ else if (terminator == CHAR_RIGHT_PARENTHESIS) { if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) *parsed_pattern = META_COND_DEFINE; else { for (i = 1; i < (int)namelen; i++) if (!IS_DIGIT(name[i])) break; *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)? META_COND_RNUMBER : META_COND_NAME; } ptr--; /* Back to closing parens */ } /* Handle (?('name') or (?() */ else *parsed_pattern = META_COND_NAME; /* All these cases except DEFINE end with the name length and offset; DEFINE just has an offset (for the "too many branches" error). */ if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen; PUTOFFSET(offset, parsed_pattern); } /* End cases that read a name */ /* Check the closing parenthesis of the condition */ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) { errorcode = ERR24; goto FAILED; } ptr++; break; /* End of condition processing */ /* ---- Atomic group ---- */ case CHAR_GREATER_THAN_SIGN: ATOMIC_GROUP: /* Come from (*atomic: */ *parsed_pattern++ = META_ATOMIC; nest_depth++; ptr++; break; /* ---- Lookahead assertions ---- */ case CHAR_EQUALS_SIGN: POSITIVE_LOOK_AHEAD: /* Come from (*pla: */ *parsed_pattern++ = META_LOOKAHEAD; ptr++; goto POST_ASSERTION; case CHAR_ASTERISK: POSITIVE_NONATOMIC_LOOK_AHEAD: /* Come from (?* */ *parsed_pattern++ = META_LOOKAHEAD_NA; ptr++; goto POST_ASSERTION; case CHAR_EXCLAMATION_MARK: NEGATIVE_LOOK_AHEAD: /* Come from (*nla: */ *parsed_pattern++ = META_LOOKAHEADNOT; ptr++; goto POST_ASSERTION; /* ---- Lookbehind assertions ---- */ /* (?< followed by = or ! or * is a lookbehind assertion. Otherwise (?< is the start of the name of a capturing group. */ case CHAR_LESS_THAN_SIGN: if (ptrend - ptr <= 1 || (ptr[1] != CHAR_EQUALS_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK && ptr[1] != CHAR_ASTERISK)) { terminator = CHAR_GREATER_THAN_SIGN; goto DEFINE_NAME; } *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)? META_LOOKBEHIND : (ptr[1] == CHAR_EXCLAMATION_MARK)? META_LOOKBEHINDNOT : META_LOOKBEHIND_NA; POST_LOOKBEHIND: /* Come from (*plb: (*naplb: and (*nlb: */ *has_lookbehind = TRUE; offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); PUTOFFSET(offset, parsed_pattern); ptr += 2; /* Fall through */ /* If the previous item was a condition starting (?(? an assertion, optionally preceded by a callout, is expected. This is checked later on, during actual compilation. However we need to identify this kind of assertion in this pass because it must not be qualified. The value of expect_cond_assert is set to 2 after (?(? is processed. We decrement it for a callout - still leaving a positive value that identifies the assertion. Multiple callouts or any other items will make it zero or less, which doesn't matter because they will cause an error later. */ POST_ASSERTION: nest_depth++; if (prev_expect_cond_assert > 0) { if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); else if (++top_nest >= end_nests) { errorcode = ERR84; goto FAILED; } top_nest->nest_depth = nest_depth; top_nest->flags = NSF_CONDASSERT; top_nest->options = options & PARSE_TRACKED_OPTIONS; } break; /* ---- Define a named group ---- */ /* A named group may be defined as (?'name') or (?). In the latter case we jump to DEFINE_NAME from the disambiguation of (?< above with the terminator set to '>'. */ case CHAR_APOSTROPHE: terminator = CHAR_APOSTROPHE; /* Terminator */ DEFINE_NAME: if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen, &errorcode, cb)) goto FAILED; /* We have a name for this capturing group. It is also assigned a number, which is its primary means of identification. */ if (cb->bracount >= MAX_GROUP_NUMBER) { errorcode = ERR97; goto FAILED; } cb->bracount++; *parsed_pattern++ = META_CAPTURE | cb->bracount; nest_depth++; /* Check not too many names */ if (cb->names_found >= MAX_NAME_COUNT) { errorcode = ERR49; goto FAILED; } /* Adjust the entry size to accommodate the longest name found. */ if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); /* Scan the list to check for duplicates. For duplicate names, if the number is the same, break the loop, which causes the name to be discarded; otherwise, if DUPNAMES is not set, give an error. If it is set, allow the name with a different number, but continue scanning in case this is a duplicate with the same number. For non-duplicate names, give an error if the number is duplicated. */ isdupname = FALSE; ng = cb->named_groups; for (i = 0; i < cb->names_found; i++, ng++) { if (namelen == ng->length && PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0) { if (ng->number == cb->bracount) break; if ((options & PCRE2_DUPNAMES) == 0) { errorcode = ERR43; goto FAILED; } isdupname = ng->isdup = TRUE; /* Mark as a duplicate */ cb->dupnames = TRUE; /* Duplicate names exist */ } else if (ng->number == cb->bracount) { errorcode = ERR65; goto FAILED; } } if (i < cb->names_found) break; /* Ignore duplicate with same number */ /* Increase the list size if necessary */ if (cb->names_found >= cb->named_group_list_size) { uint32_t newsize = cb->named_group_list_size * 2; named_group *newspace = cb->cx->memctl.malloc(newsize * sizeof(named_group), cb->cx->memctl.memory_data); if (newspace == NULL) { errorcode = ERR21; goto FAILED; } memcpy(newspace, cb->named_groups, cb->named_group_list_size * sizeof(named_group)); if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE) cb->cx->memctl.free((void *)cb->named_groups, cb->cx->memctl.memory_data); cb->named_groups = newspace; cb->named_group_list_size = newsize; } /* Add this name to the list */ cb->named_groups[cb->names_found].name = name; cb->named_groups[cb->names_found].length = (uint16_t)namelen; cb->named_groups[cb->names_found].number = cb->bracount; cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname; cb->names_found++; break; } /* End of (? switch */ break; /* End of ( handling */ /* ---- Branch terminators ---- */ /* Alternation: reset the capture count if we are in a (?| group. */ case CHAR_VERTICAL_LINE: if (top_nest != NULL && top_nest->nest_depth == nest_depth && (top_nest->flags & NSF_RESET) != 0) { if (cb->bracount > top_nest->max_group) top_nest->max_group = (uint16_t)cb->bracount; cb->bracount = top_nest->reset_group; } *parsed_pattern++ = META_ALT; break; /* End of group; reset the capture count to the maximum if we are in a (?| group and/or reset the options that are tracked during parsing. Disallow quantifier for a condition that is an assertion. */ case CHAR_RIGHT_PARENTHESIS: okquantifier = TRUE; if (top_nest != NULL && top_nest->nest_depth == nest_depth) { options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options; if ((top_nest->flags & NSF_RESET) != 0 && top_nest->max_group > cb->bracount) cb->bracount = top_nest->max_group; if ((top_nest->flags & NSF_CONDASSERT) != 0) okquantifier = FALSE; if ((top_nest->flags & NSF_ATOMICSR) != 0) { *parsed_pattern++ = META_KET; } if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; else top_nest--; } if (nest_depth == 0) /* Unmatched closing parenthesis */ { errorcode = ERR22; goto FAILED_BACK; } nest_depth--; *parsed_pattern++ = META_KET; break; } /* End of switch on pattern character */ } /* End of main character scan loop */ /* End of pattern reached. Check for missing ) at the end of a verb name. */ if (inverbname && ptr >= ptrend) { errorcode = ERR60; goto FAILED; } /* Manage callout for the final item */ PARSED_END: parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout, parsed_pattern, cb); /* Insert trailing items for word and line matching (features provided for the benefit of pcre2grep). */ if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) { *parsed_pattern++ = META_KET; *parsed_pattern++ = META_DOLLAR; } else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) { *parsed_pattern++ = META_KET; *parsed_pattern++ = META_ESCAPE + ESC_b; } /* Terminate the parsed pattern, then return success if all groups are closed. Otherwise we have unclosed parentheses. */ if (parsed_pattern >= parsed_pattern_end) { errorcode = ERR63; /* Internal error (parsed pattern overflow) */ goto FAILED; } *parsed_pattern = META_END; if (nest_depth == 0) return 0; UNCLOSED_PARENTHESIS: errorcode = ERR14; /* Come here for all failures. */ FAILED: cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern); return errorcode; /* Some errors need to indicate the previous character. */ FAILED_BACK: ptr--; goto FAILED; /* This failure happens several times. */ BAD_VERSION_CONDITION: errorcode = ERR79; goto FAILED; } /************************************************* * Find first significant opcode * *************************************************/ /* This is called by several functions that scan a compiled expression looking for a fixed first character, or an anchoring opcode etc. It skips over things that do not influence this. For some calls, it makes sense to skip negative forward and all backward assertions, and also the \b assertion; for others it does not. Arguments: code pointer to the start of the group skipassert TRUE if certain assertions are to be skipped Returns: pointer to the first significant opcode */ static const PCRE2_UCHAR* first_significant_code(PCRE2_SPTR code, BOOL skipassert) { for (;;) { switch ((int)*code) { case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERTBACK_NA: if (!skipassert) return code; do code += GET(code, 1); while (*code == OP_ALT); code += PRIV(OP_lengths)[*code]; break; case OP_WORD_BOUNDARY: case OP_NOT_WORD_BOUNDARY: if (!skipassert) return code; /* Fall through */ case OP_CALLOUT: case OP_CREF: case OP_DNCREF: case OP_RREF: case OP_DNRREF: case OP_FALSE: case OP_TRUE: code += PRIV(OP_lengths)[*code]; break; case OP_CALLOUT_STR: code += GET(code, 1 + 2*LINK_SIZE); break; case OP_SKIPZERO: code += 2 + GET(code, 2) + LINK_SIZE; break; case OP_COND: case OP_SCOND: if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */ code[GET(code, 1)] != OP_KET) /* More than one branch */ return code; code += GET(code, 1) + 1 + LINK_SIZE; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: code += code[1] + PRIV(OP_lengths)[*code]; break; default: return code; } } /* Control never reaches here */ } #ifdef SUPPORT_UNICODE /************************************************* * Get othercase range * *************************************************/ /* This function is passed the start and end of a class range in UCP mode. It searches up the characters, looking for ranges of characters in the "other" case. Each call returns the next one, updating the start address. A character with multiple other cases is returned on its own with a special return value. Arguments: cptr points to starting character value; updated d end value ocptr where to put start of othercase range odptr where to put end of othercase range Yield: -1 when no more 0 when a range is returned >0 the CASESET offset for char with multiple other cases in this case, ocptr contains the original */ static int get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, uint32_t *odptr) { uint32_t c, othercase, next; unsigned int co; /* Find the first character that has an other case. If it has multiple other cases, return its case offset value. */ for (c = *cptr; c <= d; c++) { if ((co = UCD_CASESET(c)) != 0) { *ocptr = c++; /* Character that has the set */ *cptr = c; /* Rest of input range */ return (int)co; } if ((othercase = UCD_OTHERCASE(c)) != c) break; } if (c > d) return -1; /* Reached end of range */ /* Found a character that has a single other case. Search for the end of the range, which is either the end of the input range, or a character that has zero or more than one other cases. */ *ocptr = othercase; next = othercase + 1; for (++c; c <= d; c++) { if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; next++; } *odptr = next - 1; /* End of othercase range */ *cptr = c; /* Rest of input range */ return 0; } #endif /* SUPPORT_UNICODE */ /************************************************* * Add a character or range to a class (internal) * *************************************************/ /* This function packages up the logic of adding a character or range of characters to a class. The character values in the arguments will be within the valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is called only from within the "add to class" group of functions, some of which are recursive and mutually recursive. The external entry point is add_to_class(). Arguments: classbits the bit map for characters < 256 uchardptr points to the pointer for extra data options the options word cb compile data start start of range character end end of range character Returns: the number of < 256 characters added the pointer to extra data is updated */ static unsigned int add_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, compile_block *cb, uint32_t start, uint32_t end) { uint32_t c; uint32_t classbits_end = (end <= 0xff ? end : 0xff); unsigned int n8 = 0; /* If caseless matching is required, scan the range and process alternate cases. In Unicode, there are 8-bit characters that have alternate cases that are greater than 255 and vice-versa. Sometimes we can just extend the original range. */ if ((options & PCRE2_CASELESS) != 0) { #ifdef SUPPORT_UNICODE if ((options & (PCRE2_UTF|PCRE2_UCP)) != 0) { int rc; uint32_t oc, od; options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ c = start; while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) { /* Handle a single character that has more than one other case. */ if (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb, PRIV(ucd_caseless_sets) + rc, oc); /* Do nothing if the other case range is within the original range. */ else if (oc >= cb->class_range_start && od <= cb->class_range_end) continue; /* Extend the original range if there is overlap, noting that if oc < c, we can't have od > end because a subrange is always shorter than the basic range. Otherwise, use a recursive call to add the additional range. */ else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ else if (od > end && oc <= end + 1) { end = od; /* Extend upwards */ if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff); } else n8 += add_to_class_internal(classbits, uchardptr, options, cb, oc, od); } } else #endif /* SUPPORT_UNICODE */ /* Not UTF mode */ for (c = start; c <= classbits_end; c++) { SETBIT(classbits, cb->fcc[c]); n8++; } } /* Now handle the originally supplied range. Adjust the final value according to the bit length - this means that the same lists of (e.g.) horizontal spaces can be used in all cases. */ if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) end = MAX_NON_UTF_CHAR; if (start > cb->class_range_start && end < cb->class_range_end) return n8; /* Use the bitmap for characters < 256. Otherwise use extra data.*/ for (c = start; c <= classbits_end; c++) { /* Regardless of start, c will always be <= 255. */ SETBIT(classbits, c); n8++; } #ifdef SUPPORT_WIDE_CHARS if (start <= 0xff) start = 0xff + 1; if (end >= start) { PCRE2_UCHAR *uchardata = *uchardptr; #ifdef SUPPORT_UNICODE if ((options & PCRE2_UTF) != 0) { if (start < end) { *uchardata++ = XCL_RANGE; uchardata += PRIV(ord2utf)(start, uchardata); uchardata += PRIV(ord2utf)(end, uchardata); } else if (start == end) { *uchardata++ = XCL_SINGLE; uchardata += PRIV(ord2utf)(start, uchardata); } } else #endif /* SUPPORT_UNICODE */ /* Without UTF support, character values are constrained by the bit length, and can only be > 256 for 16-bit and 32-bit libraries. */ #if PCRE2_CODE_UNIT_WIDTH == 8 {} #else if (start < end) { *uchardata++ = XCL_RANGE; *uchardata++ = start; *uchardata++ = end; } else if (start == end) { *uchardata++ = XCL_SINGLE; *uchardata++ = start; } #endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ *uchardptr = uchardata; /* Updata extra data pointer */ } #else /* SUPPORT_WIDE_CHARS */ (void)uchardptr; /* Avoid compiler warning */ #endif /* SUPPORT_WIDE_CHARS */ return n8; /* Number of 8-bit characters */ } #ifdef SUPPORT_UNICODE /************************************************* * Add a list of characters to a class (internal) * *************************************************/ /* This function is used for adding a list of case-equivalent characters to a class when in UTF mode. This function is called only from within add_to_class_internal(), with which it is mutually recursive. Arguments: classbits the bit map for characters < 256 uchardptr points to the pointer for extra data options the options word cb contains pointers to tables etc. p points to row of 32-bit values, terminated by NOTACHAR except character to omit; this is used when adding lists of case-equivalent characters to avoid including the one we already know about Returns: the number of < 256 characters added the pointer to extra data is updated */ static unsigned int add_list_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except) { unsigned int n8 = 0; while (p[0] < NOTACHAR) { unsigned int n = 0; if (p[0] != except) { while(p[n+1] == p[0] + n + 1) n++; n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); } p += n + 1; } return n8; } #endif /************************************************* * External entry point for add range to class * *************************************************/ /* This function sets the overall range so that the internal functions can try to avoid duplication when handling case-independence. Arguments: classbits the bit map for characters < 256 uchardptr points to the pointer for extra data options the options word cb compile data start start of range character end end of range character Returns: the number of < 256 characters added the pointer to extra data is updated */ static unsigned int add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, compile_block *cb, uint32_t start, uint32_t end) { cb->class_range_start = start; cb->class_range_end = end; return add_to_class_internal(classbits, uchardptr, options, cb, start, end); } /************************************************* * External entry point for add list to class * *************************************************/ /* This function is used for adding a list of horizontal or vertical whitespace characters to a class. The list must be in order so that ranges of characters can be detected and handled appropriately. This function sets the overall range so that the internal functions can try to avoid duplication when handling case-independence. Arguments: classbits the bit map for characters < 256 uchardptr points to the pointer for extra data options the options word cb contains pointers to tables etc. p points to row of 32-bit values, terminated by NOTACHAR except character to omit; this is used when adding lists of case-equivalent characters to avoid including the one we already know about Returns: the number of < 256 characters added the pointer to extra data is updated */ static unsigned int add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except) { unsigned int n8 = 0; while (p[0] < NOTACHAR) { unsigned int n = 0; if (p[0] != except) { while(p[n+1] == p[0] + n + 1) n++; cb->class_range_start = p[0]; cb->class_range_end = p[n]; n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); } p += n + 1; } return n8; } /************************************************* * Add characters not in a list to a class * *************************************************/ /* This function is used for adding the complement of a list of horizontal or vertical whitespace to a class. The list must be in order. Arguments: classbits the bit map for characters < 256 uchardptr points to the pointer for extra data options the options word cb contains pointers to tables etc. p points to row of 32-bit values, terminated by NOTACHAR Returns: the number of < 256 characters added the pointer to extra data is updated */ static unsigned int add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, compile_block *cb, const uint32_t *p) { BOOL utf = (options & PCRE2_UTF) != 0; unsigned int n8 = 0; if (p[0] > 0) n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1); while (p[0] < NOTACHAR) { while (p[1] == p[0] + 1) p++; n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1, (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); p++; } return n8; } /************************************************* * Find details of duplicate group names * *************************************************/ /* This is called from compile_branch() when it needs to know the index and count of duplicates in the names table when processing named backreferences, either directly, or as conditions. Arguments: name points to the name length the length of the name indexptr where to put the index countptr where to put the count of duplicates errorcodeptr where to put an error code cb the compile block Returns: TRUE if OK, FALSE if not, error code set */ static BOOL find_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr, int *countptr, int *errorcodeptr, compile_block *cb) { uint32_t i, groupnumber; int count; PCRE2_UCHAR *slot = cb->name_table; /* Find the first entry in the table */ for (i = 0; i < cb->names_found; i++) { if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 && slot[IMM2_SIZE+length] == 0) break; slot += cb->name_entry_size; } /* This should not occur, because this function is called only when we know we have duplicate names. Give an internal error. */ if (i >= cb->names_found) { *errorcodeptr = ERR53; cb->erroroffset = name - cb->start_pattern; return FALSE; } /* Record the index and then see how many duplicates there are, updating the backref map and maximum back reference as we do. */ *indexptr = i; count = 0; for (;;) { count++; groupnumber = GET2(slot,0); cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; if (++i >= cb->names_found) break; slot += cb->name_entry_size; if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 || (slot+IMM2_SIZE)[length] != 0) break; } *countptr = count; return TRUE; } /************************************************* * Compile one branch * *************************************************/ /* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If the options are changed during the branch, the pointer is used to change the external options bits. This function is used during the pre-compile phase when we are trying to find out the amount of memory needed, as well as during the real compile phase. The value of lengthptr distinguishes the two phases. Arguments: optionsptr pointer to the option bits codeptr points to the pointer to the current code point pptrptr points to the current parsed pattern pointer errorcodeptr points to error code variable firstcuptr place to put the first required code unit firstcuflagsptr place to put the first code unit flags, or a negative number reqcuptr place to put the last required code unit reqcuflagsptr place to put the last required code unit flags, or a negative number bcptr points to current branch chain cb contains pointers to tables etc. lengthptr NULL during the real compile phase points to length accumulator during pre-compile phase Returns: 0 There's been an error, *errorcodeptr is non-zero +1 Success, this branch must match at least one character -1 Success, this branch may match an empty string */ static int compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, int *errorcodeptr, uint32_t *firstcuptr, int32_t *firstcuflagsptr, uint32_t *reqcuptr, int32_t *reqcuflagsptr, branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr) { int bravalue = 0; int okreturn = -1; int group_return = 0; uint32_t repeat_min = 0, repeat_max = 0; /* To please picky compilers */ uint32_t greedy_default, greedy_non_default; uint32_t repeat_type, op_type; uint32_t options = *optionsptr; /* May change dynamically */ uint32_t firstcu, reqcu; uint32_t zeroreqcu, zerofirstcu; uint32_t escape; uint32_t *pptr = *pptrptr; uint32_t meta, meta_arg; int32_t firstcuflags, reqcuflags; int32_t zeroreqcuflags, zerofirstcuflags; int32_t req_caseopt, reqvary, tempreqvary; PCRE2_SIZE offset = 0; PCRE2_SIZE length_prevgroup = 0; PCRE2_UCHAR *code = *codeptr; PCRE2_UCHAR *last_code = code; PCRE2_UCHAR *orig_code = code; PCRE2_UCHAR *tempcode; PCRE2_UCHAR *previous = NULL; PCRE2_UCHAR op_previous; BOOL groupsetfirstcu = FALSE; BOOL had_accept = FALSE; BOOL matched_char = FALSE; BOOL previous_matched_char = FALSE; BOOL reset_caseful = FALSE; const uint8_t *cbits = cb->cbits; uint8_t classbits[32]; /* We can fish out the UTF setting once and for all into a BOOL, but we must not do this for other options (e.g. PCRE2_EXTENDED) because they may change dynamically as we process the pattern. */ #ifdef SUPPORT_UNICODE BOOL utf = (options & PCRE2_UTF) != 0; BOOL ucp = (options & PCRE2_UCP) != 0; #else /* No Unicode support */ BOOL utf = FALSE; #endif /* Helper variables for OP_XCLASS opcode (for characters > 255). We define class_uchardata always so that it can be passed to add_to_class() always, though it will not be used in non-UTF 8-bit cases. This avoids having to supply alternative calls for the different cases. */ PCRE2_UCHAR *class_uchardata; #ifdef SUPPORT_WIDE_CHARS BOOL xclass; PCRE2_UCHAR *class_uchardata_base; #endif /* Set up the default and non-default settings for greediness */ greedy_default = ((options & PCRE2_UNGREEDY) != 0); greedy_non_default = greedy_default ^ 1; /* Initialize no first unit, no required unit. REQ_UNSET means "no char matching encountered yet". It gets changed to REQ_NONE if we hit something that matches a non-fixed first unit; reqcu just remains unset if we never find one. When we hit a repeat whose minimum is zero, we may have to adjust these values to take the zero repeat into account. This is implemented by setting them to zerofirstcu and zeroreqcu when such a repeat is encountered. The individual item types that can be repeated set these backoff variables appropriately. */ firstcu = reqcu = zerofirstcu = zeroreqcu = 0; firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; /* The variable req_caseopt contains either the REQ_CASELESS value or zero, according to the current setting of the caseless flag. The REQ_CASELESS value leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables to record the case status of the value. This is used only for ASCII characters. */ req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; /* Switch on next META item until the end of the branch */ for (;; pptr++) { #ifdef SUPPORT_WIDE_CHARS BOOL xclass_has_prop; #endif BOOL negate_class; BOOL should_flip_negation; BOOL match_all_or_no_wide_chars; BOOL possessive_quantifier; BOOL note_group_empty; int class_has_8bitchar; int i; uint32_t mclength; uint32_t skipunits; uint32_t subreqcu, subfirstcu; uint32_t groupnumber; uint32_t verbarglen, verbculen; int32_t subreqcuflags, subfirstcuflags; /* Must be signed */ open_capitem *oc; PCRE2_UCHAR mcbuffer[8]; /* Get next META item in the pattern and its potential argument. */ meta = META_CODE(*pptr); meta_arg = META_DATA(*pptr); /* If we are in the pre-compile phase, accumulate the length used for the previous cycle of this loop, unless the next item is a quantifier. */ if (lengthptr != NULL) { if (code > cb->start_workspace + cb->workspace_size - WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ { *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? ERR52 : ERR86; return 0; } /* There is at least one situation where code goes backwards: this is the case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier is processed, the whole class is eliminated. However, it is created first, so we have to allow memory for it. Therefore, don't ever reduce the length at this point. */ if (code < last_code) code = last_code; /* If the next thing is not a quantifier, we add the length of the previous item into the total, and reset the code pointer to the start of the workspace. Otherwise leave the previous item available to be quantified. */ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) { if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code)) { *errorcodeptr = ERR20; /* Integer overflow */ return 0; } *lengthptr += (PCRE2_SIZE)(code - orig_code); if (*lengthptr > MAX_PATTERN_SIZE) { *errorcodeptr = ERR20; /* Pattern is too large */ return 0; } code = orig_code; } /* Remember where this code item starts so we can catch the "backwards" case above next time round. */ last_code = code; } /* Process the next parsed pattern item. If it is not a quantifier, remember where it starts so that it can be quantified when a quantifier follows. Checking for the legality of quantifiers happens in parse_regex(), except for a quantifier after an assertion that is a condition. */ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) { previous = code; if (matched_char && !had_accept) okreturn = 1; } previous_matched_char = matched_char; matched_char = FALSE; note_group_empty = FALSE; skipunits = 0; /* Default value for most subgroups */ switch(meta) { /* ===================================================================*/ /* The branch terminates at pattern end or | or ) */ case META_END: case META_ALT: case META_KET: *firstcuptr = firstcu; *firstcuflagsptr = firstcuflags; *reqcuptr = reqcu; *reqcuflagsptr = reqcuflags; *codeptr = code; *pptrptr = pptr; return okreturn; /* ===================================================================*/ /* Handle single-character metacharacters. In multiline mode, ^ disables the setting of any following char as a first character. */ case META_CIRCUMFLEX: if ((options & PCRE2_MULTILINE) != 0) { if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE; *code++ = OP_CIRCM; } else *code++ = OP_CIRC; break; case META_DOLLAR: *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; break; /* There can never be a first char if '.' is first, whatever happens about repeats. The value of reqcu doesn't change either. */ case META_DOT: matched_char = TRUE; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; break; /* ===================================================================*/ /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set. Otherwise, an initial ']' is taken as a data character. When empty classes are allowed, [] must always fail, so generate OP_FAIL, whereas [^] must match any character, so generate OP_ALLANY. */ case META_CLASS_EMPTY: case META_CLASS_EMPTY_NOT: matched_char = TRUE; *code++ = (meta == META_CLASS_EMPTY_NOT)? OP_ALLANY : OP_FAIL; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; break; /* ===================================================================*/ /* Non-empty character class. If the included characters are all < 256, we build a 32-byte bitmap of the permitted characters, except in the special case where there is only one such character. For negated classes, we build the map as usual, then invert it at the end. However, we use a different opcode so that data characters > 255 can be handled correctly. If the class contains characters outside the 0-255 range, a different opcode is compiled. It may optionally have a bit map for characters < 256, but those above are are explicitly listed afterwards. A flag code unit tells whether the bitmap is present, and whether this is a negated class or not. */ case META_CLASS_NOT: case META_CLASS: matched_char = TRUE; negate_class = meta == META_CLASS_NOT; /* We can optimize the case of a single character in a class by generating OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's negative. In the negative case there can be no first char if this item is first, whatever repeat count may follow. In the case of reqcu, save the previous value for reinstating. */ /* NOTE: at present this optimization is not effective if the only character in a class in 32-bit, non-UCP mode has its top bit set. */ if (pptr[1] < META_END && pptr[2] == META_CLASS_END) { #ifdef SUPPORT_UNICODE uint32_t d; #endif uint32_t c = pptr[1]; pptr += 2; /* Move on to class end */ if (meta == META_CLASS) /* A positive one-char class can be */ { /* handled as a normal literal character. */ meta = c; /* Set up the character */ goto NORMAL_CHAR_SET; } /* Handle a negative one-character class */ zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; /* For caseless UTF or UCP mode, check whether this character has more than one other case. If so, generate a special OP_NOTPROP item instead of OP_NOTI. */ #ifdef SUPPORT_UNICODE if ((utf||ucp) && (options & PCRE2_CASELESS) != 0 && (d = UCD_CASESET(c)) != 0) { *code++ = OP_NOTPROP; *code++ = PT_CLIST; *code++ = d; break; /* We are finished with this class */ } #endif /* Char has only one other case, or UCP not available */ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; code += PUTCHAR(c, code); break; /* We are finished with this class */ } /* End of 1-char optimization */ /* Handle character classes that contain more than just one literal character. If there are exactly two characters in a positive class, see if they are case partners. This can be optimized to generate a caseless single character match (which also sets first/required code units if relevant). */ if (meta == META_CLASS && pptr[1] < META_END && pptr[2] < META_END && pptr[3] == META_CLASS_END) { uint32_t c = pptr[1]; #ifdef SUPPORT_UNICODE if (UCD_CASESET(c) == 0) #endif { uint32_t d; #ifdef SUPPORT_UNICODE if ((utf || ucp) && c > 127) d = UCD_OTHERCASE(c); else #endif { #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) d = c; else #endif d = TABLE_GET(c, cb->fcc, c); } if (c != d && pptr[2] == d) { pptr += 3; /* Move on to class end */ meta = c; if ((options & PCRE2_CASELESS) == 0) { reset_caseful = TRUE; options |= PCRE2_CASELESS; req_caseopt = REQ_CASELESS; } goto CLASS_CASELESS_CHAR; } } } /* If a non-extended class contains a negative special such as \S, we need to flip the negation flag at the end, so that support for characters > 255 works correctly (they are all included in the class). An extended class may need to insert specific matching or non-matching code for wide characters. */ should_flip_negation = match_all_or_no_wide_chars = FALSE; /* Extended class (xclass) will be used when characters > 255 might match. */ #ifdef SUPPORT_WIDE_CHARS xclass = FALSE; class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ class_uchardata_base = class_uchardata; /* Save the start */ #endif /* For optimization purposes, we track some properties of the class: class_has_8bitchar will be non-zero if the class contains at least one character with a code point less than 256; xclass_has_prop will be TRUE if Unicode property checks are present in the class. */ class_has_8bitchar = 0; #ifdef SUPPORT_WIDE_CHARS xclass_has_prop = FALSE; #endif /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map in a temporary bit of memory, in case the class contains fewer than two 8-bit characters because in that case the compiled code doesn't use the bit map. */ memset(classbits, 0, 32 * sizeof(uint8_t)); /* Process items until META_CLASS_END is reached. */ while ((meta = *(++pptr)) != META_CLASS_END) { /* Handle POSIX classes such as [:alpha:] etc. */ if (meta == META_POSIX || meta == META_POSIX_NEG) { BOOL local_negate = (meta == META_POSIX_NEG); int posix_class = *(++pptr); int taboffset, tabopt; uint8_t pbits[32]; should_flip_negation = local_negate; /* Note negative special */ /* If matching is caseless, upper and lower are converted to alpha. This relies on the fact that the class table starts with alpha, lower, upper as the first 3 entries. */ if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) posix_class = 0; /* When PCRE2_UCP is set, some of the POSIX classes are converted to different escape sequences that use Unicode properties \p or \P. Others that are not available via \p or \P have to generate XCL_PROP/XCL_NOTPROP directly, which is done here. */ #ifdef SUPPORT_UNICODE if ((options & PCRE2_UCP) != 0) switch(posix_class) { case PC_GRAPH: case PC_PRINT: case PC_PUNCT: *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP; *class_uchardata++ = (PCRE2_UCHAR) ((posix_class == PC_GRAPH)? PT_PXGRAPH : (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT); *class_uchardata++ = 0; xclass_has_prop = TRUE; goto CONTINUE_CLASS; /* For the other POSIX classes (ascii, xdigit) we are going to fall through to the non-UCP case and build a bit map for characters with code points less than 256. However, if we are in a negated POSIX class, characters with code points greater than 255 must either all match or all not match, depending on whether the whole class is not or is negated. For example, for [[:^ascii:]... they must all match, whereas for [^[:^xdigit:]... they must not. In the special case where there are no xclass items, this is automatically handled by the use of OP_CLASS or OP_NCLASS, but an explicit range is needed for OP_XCLASS. Setting a flag here causes the range to be generated later when it is known that OP_XCLASS is required. In the 8-bit library this is relevant only in utf mode, since no wide characters can exist otherwise. */ default: #if PCRE2_CODE_UNIT_WIDTH == 8 if (utf) #endif match_all_or_no_wide_chars |= local_negate; break; } #endif /* SUPPORT_UNICODE */ /* In the non-UCP case, or when UCP makes no difference, we build the bit map for the POSIX class in a chunk of local store because we may be adding and subtracting from it, and we don't want to subtract bits that may be in the main map already. At the end we or the result into the bit map that is being built. */ posix_class *= 3; /* Copy in the first table (always present) */ memcpy(pbits, cbits + posix_class_maps[posix_class], 32 * sizeof(uint8_t)); /* If there is a second table, add or remove it as required. */ taboffset = posix_class_maps[posix_class + 1]; tabopt = posix_class_maps[posix_class + 2]; if (taboffset >= 0) { if (tabopt >= 0) for (i = 0; i < 32; i++) pbits[i] |= cbits[(int)i + taboffset]; else for (i = 0; i < 32; i++) pbits[i] &= ~cbits[(int)i + taboffset]; } /* Now see if we need to remove any special characters. An option value of 1 removes vertical space and 2 removes underscore. */ if (tabopt < 0) tabopt = -tabopt; if (tabopt == 1) pbits[1] &= ~0x3c; else if (tabopt == 2) pbits[11] &= 0x7f; /* Add the POSIX table or its complement into the main table that is being built and we are done. */ if (local_negate) for (i = 0; i < 32; i++) classbits[i] |= ~pbits[i]; else for (i = 0; i < 32; i++) classbits[i] |= pbits[i]; /* Every class contains at least one < 256 character. */ class_has_8bitchar = 1; goto CONTINUE_CLASS; /* End of POSIX handling */ } /* Other than POSIX classes, the only items we should encounter are \d-type escapes and literal characters (possibly as ranges). */ if (meta == META_BIGVALUE) { meta = *(++pptr); goto CLASS_LITERAL; } /* Any other non-literal must be an escape */ if (meta >= META_END) { if (META_CODE(meta) != META_ESCAPE) { #ifdef DEBUG_SHOW_PARSED fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x " "in character class\n", meta); #endif *errorcodeptr = ERR89; /* Internal error - unrecognized. */ return 0; } escape = META_DATA(meta); /* Every class contains at least one < 256 character. */ class_has_8bitchar++; switch(escape) { case ESC_d: for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit]; break; case ESC_D: should_flip_negation = TRUE; for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_digit]; break; case ESC_w: for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word]; break; case ESC_W: should_flip_negation = TRUE; for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_word]; break; /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was previously set by something earlier in the character class. Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so we could just adjust the appropriate bit. From PCRE 8.34 we no longer treat \s and \S specially. */ case ESC_s: for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space]; break; case ESC_S: should_flip_negation = TRUE; for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_space]; break; /* When adding the horizontal or vertical space lists to a class, or their complements, disable PCRE2_CASELESS, because it justs wastes time, and in the "not-x" UTF cases can create unwanted duplicates in the XCLASS list (provoked by characters that have more than one other case and by both cases being in the same "not-x" sublist). */ case ESC_h: (void)add_list_to_class(classbits, &class_uchardata, options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR); break; case ESC_H: (void)add_not_list_to_class(classbits, &class_uchardata, options & ~PCRE2_CASELESS, cb, PRIV(hspace_list)); break; case ESC_v: (void)add_list_to_class(classbits, &class_uchardata, options & ~PCRE2_CASELESS, cb, PRIV(vspace_list), NOTACHAR); break; case ESC_V: (void)add_not_list_to_class(classbits, &class_uchardata, options & ~PCRE2_CASELESS, cb, PRIV(vspace_list)); break; /* If Unicode is not supported, \P and \p are not allowed and are faulted at parse time, so will never appear here. */ #ifdef SUPPORT_UNICODE case ESC_p: case ESC_P: { uint32_t ptype = *(++pptr) >> 16; uint32_t pdata = *pptr & 0xffff; *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP; *class_uchardata++ = ptype; *class_uchardata++ = pdata; xclass_has_prop = TRUE; class_has_8bitchar--; /* Undo! */ } break; #endif } goto CONTINUE_CLASS; } /* End handling \d-type escapes */ /* A literal character may be followed by a range meta. At parse time there are checks for out-of-order characters, for ranges where the two characters are equal, and for hyphens that cannot indicate a range. At this point, therefore, no checking is needed. */ else { uint32_t c, d; CLASS_LITERAL: c = d = meta; /* Remember if \r or \n were explicitly used */ if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; /* Process a character range */ if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED) { #ifdef EBCDIC BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL); #endif pptr += 2; d = *pptr; if (d == META_BIGVALUE) d = *(++pptr); /* Remember an explicit \r or \n, and add the range to the class. */ if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; /* In an EBCDIC environment, Perl treats alphabetic ranges specially because there are holes in the encoding, and simply using the range A-Z (for example) would include the characters in the holes. This applies only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */ #ifdef EBCDIC if (range_is_literal && (cb->ctypes[c] & ctype_letter) != 0 && (cb->ctypes[d] & ctype_letter) != 0 && (c <= CHAR_z) == (d <= CHAR_z)) { uint32_t uc = (d <= CHAR_z)? 0 : 64; uint32_t C = c - uc; uint32_t D = d - uc; if (C <= CHAR_i) { class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, C + uc, ((D < CHAR_i)? D : CHAR_i) + uc); C = CHAR_j; } if (C <= D && C <= CHAR_r) { class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, C + uc, ((D < CHAR_r)? D : CHAR_r) + uc); C = CHAR_s; } if (C <= D) { class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, C + uc, D + uc); } } else #endif /* Not an EBCDIC special range */ class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, c, d); goto CONTINUE_CLASS; /* Go get the next char in the class */ } /* End of range handling */ /* Handle a single character. */ class_has_8bitchar += add_to_class(classbits, &class_uchardata, options, cb, meta, meta); } /* Continue to the next item in the class. */ CONTINUE_CLASS: #ifdef SUPPORT_WIDE_CHARS /* If any wide characters or Unicode properties have been encountered, set xclass = TRUE. Then, in the pre-compile phase, accumulate the length of the extra data and reset the pointer. This is so that very large classes that contain a zillion wide characters or Unicode property tests do not overwrite the workspace (which is on the stack). */ if (class_uchardata > class_uchardata_base) { xclass = TRUE; if (lengthptr != NULL) { *lengthptr += class_uchardata - class_uchardata_base; class_uchardata = class_uchardata_base; } } #endif continue; /* Needed to avoid error when not supporting wide chars */ } /* End of main class-processing loop */ /* If this class is the first thing in the branch, there can be no first char setting, whatever the repeat count. Any reqcu setting must remain unchanged after any kind of repeat. */ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; /* If there are characters with values > 255, or Unicode property settings (\p or \P), we have to compile an extended class, with its own opcode, unless there were no property settings and there was a negated special such as \S in the class, and PCRE2_UCP is not set, because in that case all characters > 255 are in or not in the class, so any that were explicitly given as well can be ignored. In the UCP case, if certain negated POSIX classes ([:^ascii:] or [^:xdigit:]) were present in a class, we either have to match or not match all wide characters (depending on whether the whole class is or is not negated). This requirement is indicated by match_all_or_no_wide_chars being true. We do this by including an explicit range, which works in both cases. This applies only in UTF and 16-bit and 32-bit non-UTF modes, since there cannot be any wide characters in 8-bit non-UTF mode. When there *are* properties in a positive UTF-8 or any 16-bit or 32_bit class where \S etc is present without PCRE2_UCP, causing an extended class to be compiled, we make sure that all characters > 255 are included by forcing match_all_or_no_wide_chars to be true. If, when generating an xclass, there are no characters < 256, we can omit the bitmap in the actual compiled code. */ #ifdef SUPPORT_WIDE_CHARS /* Defined for 16/32 bits, or 8-bit with Unicode */ if (xclass && ( #ifdef SUPPORT_UNICODE (options & PCRE2_UCP) != 0 || #endif xclass_has_prop || !should_flip_negation)) { if (match_all_or_no_wide_chars || ( #if PCRE2_CODE_UNIT_WIDTH == 8 utf && #endif should_flip_negation && !negate_class && (options & PCRE2_UCP) == 0)) { *class_uchardata++ = XCL_RANGE; if (utf) /* Will always be utf in the 8-bit library */ { class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata); } else /* Can only happen for the 16-bit & 32-bit libraries */ { #if PCRE2_CODE_UNIT_WIDTH == 16 *class_uchardata++ = 0x100; *class_uchardata++ = 0xffffu; #elif PCRE2_CODE_UNIT_WIDTH == 32 *class_uchardata++ = 0x100; *class_uchardata++ = 0xffffffffu; #endif } } *class_uchardata++ = XCL_END; /* Marks the end of extra data */ *code++ = OP_XCLASS; code += LINK_SIZE; *code = negate_class? XCL_NOT:0; if (xclass_has_prop) *code |= XCL_HASPROP; /* If the map is required, move up the extra data to make room for it; otherwise just move the code pointer to the end of the extra data. */ if (class_has_8bitchar > 0) { *code++ |= XCL_MAP; (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code, CU2BYTES(class_uchardata - code)); if (negate_class && !xclass_has_prop) { /* Using 255 ^ instead of ~ avoids clang sanitize warning. */ for (i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i]; } memcpy(code, classbits, 32); code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); } else code = class_uchardata; /* Now fill in the complete length of the item */ PUT(previous, 1, (int)(code - previous)); break; /* End of class handling */ } #endif /* SUPPORT_WIDE_CHARS */ /* If there are no characters > 255, or they are all to be included or excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the whole class was negated and whether there were negative specials such as \S (non-UCP) in the class. Then copy the 32-byte map into the code vector, negating it if necessary. */ *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; if (lengthptr == NULL) /* Save time in the pre-compile phase */ { if (negate_class) { /* Using 255 ^ instead of ~ avoids clang sanitize warning. */ for (i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i]; } memcpy(code, classbits, 32); } code += 32 / sizeof(PCRE2_UCHAR); break; /* End of class processing */ /* ===================================================================*/ /* Deal with (*VERB)s. */ /* Check for open captures before ACCEPT and close those that are within the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an assertion. In the first pass, just accumulate the length required; otherwise hitting (*ACCEPT) inside many nested parentheses can cause workspace overflow. Do not set firstcu after *ACCEPT. */ case META_ACCEPT: cb->had_accept = had_accept = TRUE; for (oc = cb->open_caps; oc != NULL && oc->assert_depth >= cb->assert_depth; oc = oc->next) { if (lengthptr != NULL) { *lengthptr += CU2BYTES(1) + IMM2_SIZE; } else { *code++ = OP_CLOSE; PUT2INC(code, 0, oc->number); } } *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; break; case META_PRUNE: case META_SKIP: cb->had_pruneorskip = TRUE; /* Fall through */ case META_COMMIT: case META_FAIL: *code++ = verbops[(meta - META_MARK) >> 16]; break; case META_THEN: cb->external_flags |= PCRE2_HASTHEN; *code++ = OP_THEN; break; /* Handle verbs with arguments. Arguments can be very long, especially in 16- and 32-bit modes, and can overflow the workspace in the first pass. However, the argument length is constrained to be small enough to fit in one code unit. This check happens in parse_regex(). In the first pass, instead of putting the argument into memory, we just update the length counter and set up an empty argument. */ case META_THEN_ARG: cb->external_flags |= PCRE2_HASTHEN; goto VERB_ARG; case META_PRUNE_ARG: case META_SKIP_ARG: cb->had_pruneorskip = TRUE; /* Fall through */ case META_MARK: case META_COMMIT_ARG: VERB_ARG: *code++ = verbops[(meta - META_MARK) >> 16]; /* The length is in characters. */ verbarglen = *(++pptr); verbculen = 0; tempcode = code++; for (i = 0; i < (int)verbarglen; i++) { meta = *(++pptr); #ifdef SUPPORT_UNICODE if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else #endif { mclength = 1; mcbuffer[0] = meta; } if (lengthptr != NULL) *lengthptr += mclength; else { memcpy(code, mcbuffer, CU2BYTES(mclength)); code += mclength; verbculen += mclength; } } *tempcode = verbculen; /* Fill in the code unit length */ *code++ = 0; /* Terminating zero */ break; /* ===================================================================*/ /* Handle options change. The new setting must be passed back for use in subsequent branches. Reset the greedy defaults and the case value for firstcu and reqcu. */ case META_OPTIONS: *optionsptr = options = *(++pptr); greedy_default = ((options & PCRE2_UNGREEDY) != 0); greedy_non_default = greedy_default ^ 1; req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0; break; /* ===================================================================*/ /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous because it could be a numerical check on recursion, or a name check on a group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that we can handle it either way. We first try for a name; if not found, process the number. */ case META_COND_RNUMBER: /* (?(Rdigits) */ case META_COND_NAME: /* (?(name) or (?'name') or ?() */ case META_COND_RNAME: /* (?(R&name) - test for recursion */ bravalue = OP_COND; { int count, index; PCRE2_SPTR name; named_group *ng = cb->named_groups; uint32_t length = *(++pptr); GETPLUSOFFSET(offset, pptr); name = cb->start_pattern + offset; /* In the first pass, the names generated in the pre-pass are available, but the main name table has not yet been created. Scan the list of names generated in the pre-pass in order to get a number and whether or not this name is duplicated. If it is not duplicated, we can handle it as a numerical group. */ for (i = 0; i < cb->names_found; i++, ng++) { if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0) { if (!ng->isdup) { code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; PUT2(code, 2+LINK_SIZE, ng->number); if (ng->number > cb->top_backref) cb->top_backref = ng->number; skipunits = 1+IMM2_SIZE; goto GROUP_PROCESS_NOTE_EMPTY; } break; /* Found a duplicated name */ } } /* If the name was not found we have a bad reference, unless we are dealing with R, which is treated as a recursion test by number. */ if (i >= cb->names_found) { groupnumber = 0; if (meta == META_COND_RNUMBER) { for (i = 1; i < (int)length; i++) { groupnumber = groupnumber * 10 + name[i] - CHAR_0; if (groupnumber > MAX_GROUP_NUMBER) { *errorcodeptr = ERR61; cb->erroroffset = offset + i; return 0; } } } if (meta != META_COND_RNUMBER || groupnumber > cb->bracount) { *errorcodeptr = ERR15; cb->erroroffset = offset; return 0; } /* (?Rdigits) treated as a recursion reference by number. A value of zero (which is the result of both (?R) and (?R0)) means "any", and is translated into RREF_ANY (which is 0xffff). */ if (groupnumber == 0) groupnumber = RREF_ANY; code[1+LINK_SIZE] = OP_RREF; PUT2(code, 2+LINK_SIZE, groupnumber); skipunits = 1+IMM2_SIZE; goto GROUP_PROCESS_NOTE_EMPTY; } /* A duplicated name was found. Note that if an R name is found (META_COND_RNUMBER), it is a reference test, not a recursion test. */ code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; /* We have a duplicated name. In the compile pass we have to search the main table in order to get the index and count values. */ count = 0; /* Values for first pass (avoids compiler warning) */ index = 0; if (lengthptr == NULL && !find_dupname_details(name, length, &index, &count, errorcodeptr, cb)) return 0; /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and insert appropriate data values. */ code[1+LINK_SIZE]++; skipunits = 1+2*IMM2_SIZE; PUT2(code, 2+LINK_SIZE, index); PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count); } goto GROUP_PROCESS_NOTE_EMPTY; /* The DEFINE condition is always false. Its internal groups may never be called, so matched_char must remain false, hence the jump to GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */ case META_COND_DEFINE: bravalue = OP_COND; GETPLUSOFFSET(offset, pptr); code[1+LINK_SIZE] = OP_DEFINE; skipunits = 1; goto GROUP_PROCESS; /* Conditional test of a group's being set. */ case META_COND_NUMBER: bravalue = OP_COND; GETPLUSOFFSET(offset, pptr); groupnumber = *(++pptr); if (groupnumber > cb->bracount) { *errorcodeptr = ERR15; cb->erroroffset = offset; return 0; } if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; offset -= 2; /* Point at initial ( for too many branches error */ code[1+LINK_SIZE] = OP_CREF; skipunits = 1+IMM2_SIZE; PUT2(code, 2+LINK_SIZE, groupnumber); goto GROUP_PROCESS_NOTE_EMPTY; /* Test for the PCRE2 version. */ case META_COND_VERSION: bravalue = OP_COND; if (pptr[1] > 0) code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) || (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))? OP_TRUE : OP_FALSE; else code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])? OP_TRUE : OP_FALSE; skipunits = 1; pptr += 3; goto GROUP_PROCESS_NOTE_EMPTY; /* The condition is an assertion, possibly preceded by a callout. */ case META_COND_ASSERT: bravalue = OP_COND; goto GROUP_PROCESS_NOTE_EMPTY; /* ===================================================================*/ /* Handle all kinds of nested bracketed groups. The non-capturing, non-conditional cases are here; others come to GROUP_PROCESS via goto. */ case META_LOOKAHEAD: bravalue = OP_ASSERT; cb->assert_depth += 1; goto GROUP_PROCESS; case META_LOOKAHEAD_NA: bravalue = OP_ASSERT_NA; cb->assert_depth += 1; goto GROUP_PROCESS; /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird thing to do, but Perl allows all assertions to be quantified, and when they contain capturing parentheses there may be a potential use for this feature. Not that that applies to a quantified (?!) but we allow it for uniformity. */ case META_LOOKAHEADNOT: if (pptr[1] == META_KET && (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY)) { *code++ = OP_FAIL; pptr++; } else { bravalue = OP_ASSERT_NOT; cb->assert_depth += 1; goto GROUP_PROCESS; } break; case META_LOOKBEHIND: bravalue = OP_ASSERTBACK; cb->assert_depth += 1; goto GROUP_PROCESS; case META_LOOKBEHINDNOT: bravalue = OP_ASSERTBACK_NOT; cb->assert_depth += 1; goto GROUP_PROCESS; case META_LOOKBEHIND_NA: bravalue = OP_ASSERTBACK_NA; cb->assert_depth += 1; goto GROUP_PROCESS; case META_ATOMIC: bravalue = OP_ONCE; goto GROUP_PROCESS_NOTE_EMPTY; case META_SCRIPT_RUN: bravalue = OP_SCRIPT_RUN; goto GROUP_PROCESS_NOTE_EMPTY; case META_NOCAPTURE: bravalue = OP_BRA; /* Fall through */ /* Process nested bracketed regex. The nesting depth is maintained for the benefit of the stackguard function. The test for too deep nesting is now done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS; others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take note of whether or not they may match an empty string. */ GROUP_PROCESS_NOTE_EMPTY: note_group_empty = TRUE; GROUP_PROCESS: cb->parens_depth += 1; *code = bravalue; pptr++; tempcode = code; tempreqvary = cb->req_varyopt; /* Save value before group */ length_prevgroup = 0; /* Initialize for pre-compile phase */ if ((group_return = compile_regex( options, /* The option state */ &tempcode, /* Where to put code (updated) */ &pptr, /* Input pointer (updated) */ errorcodeptr, /* Where to put an error message */ skipunits, /* Skip over bracket number */ &subfirstcu, /* For possible first char */ &subfirstcuflags, &subreqcu, /* For possible last char */ &subreqcuflags, bcptr, /* Current branch chain */ cb, /* Compile data block */ (lengthptr == NULL)? NULL : /* Actual compile phase */ &length_prevgroup /* Pre-compile phase */ )) == 0) return 0; /* Error */ cb->parens_depth -= 1; /* If that was a non-conditional significant group (not an assertion, not a DEFINE) that matches at least one character, then the current item matches a character. Conditionals are handled below. */ if (note_group_empty && bravalue != OP_COND && group_return > 0) matched_char = TRUE; /* If we've just compiled an assertion, pop the assert depth. */ if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NA) cb->assert_depth -= 1; /* At the end of compiling, code is still pointing to the start of the group, while tempcode has been updated to point past the end of the group. The parsed pattern pointer (pptr) is on the closing META_KET. If this is a conditional bracket, check that there are no more than two branches in the group, or just one if it's a DEFINE group. We do this in the real compile phase, not in the pre-pass, where the whole group may not be available. */ if (bravalue == OP_COND && lengthptr == NULL) { PCRE2_UCHAR *tc = code; int condcount = 0; do { condcount++; tc += GET(tc,1); } while (*tc != OP_KET); /* A DEFINE group is never obeyed inline (the "condition" is always false). It must have only one branch. Having checked this, change the opcode to OP_FALSE. */ if (code[LINK_SIZE+1] == OP_DEFINE) { if (condcount > 1) { cb->erroroffset = offset; *errorcodeptr = ERR54; return 0; } code[LINK_SIZE+1] = OP_FALSE; bravalue = OP_DEFINE; /* A flag to suppress char handling below */ } /* A "normal" conditional group. If there is just one branch, we must not make use of its firstcu or reqcu, because this is equivalent to an empty second branch. Also, it may match an empty string. If there are two branches, this item must match a character if the group must. */ else { if (condcount > 2) { cb->erroroffset = offset; *errorcodeptr = ERR27; return 0; } if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; else if (group_return > 0) matched_char = TRUE; } } /* In the pre-compile phase, update the length by the length of the group, less the brackets at either end. Then reduce the compiled code to just a set of non-capturing brackets so that it doesn't use much memory if it is duplicated by a quantifier.*/ if (lengthptr != NULL) { if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) { *errorcodeptr = ERR20; return 0; } *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; code++; /* This already contains bravalue */ PUTINC(code, 0, 1 + LINK_SIZE); *code++ = OP_KET; PUTINC(code, 0, 1 + LINK_SIZE); break; /* No need to waste time with special character handling */ } /* Otherwise update the main code pointer to the end of the group. */ code = tempcode; /* For a DEFINE group, required and first character settings are not relevant. */ if (bravalue == OP_DEFINE) break; /* Handle updating of the required and first code units for other types of group. Update for normal brackets of all kinds, and conditions with two branches (see code above). If the bracket is followed by a quantifier with zero repeat, we have to back off. Hence the definition of zeroreqcu and zerofirstcu outside the main loop so that they can be accessed for the back off. */ zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; groupsetfirstcu = FALSE; if (bravalue >= OP_ONCE) /* Not an assertion */ { /* If we have not yet set a firstcu in this branch, take it from the subpattern, remembering that it was set here so that a repeat of more than one can replicate it as reqcu if necessary. If the subpattern has no firstcu, set "none" for the whole branch. In both cases, a zero repeat forces firstcu to "none". */ if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET) { if (subfirstcuflags >= 0) { firstcu = subfirstcu; firstcuflags = subfirstcuflags; groupsetfirstcu = TRUE; } else firstcuflags = REQ_NONE; zerofirstcuflags = REQ_NONE; } /* If firstcu was previously set, convert the subpattern's firstcu into reqcu if there wasn't one, using the vary flag that was in existence beforehand. */ else if (subfirstcuflags >= 0 && subreqcuflags < 0) { subreqcu = subfirstcu; subreqcuflags = subfirstcuflags | tempreqvary; } /* If the subpattern set a required code unit (or set a first code unit that isn't really the first code unit - see above), set it. */ if (subreqcuflags >= 0) { reqcu = subreqcu; reqcuflags = subreqcuflags; } } /* For a forward assertion, we take the reqcu, if set, provided that the group has also set a firstcu. This can be helpful if the pattern that follows the assertion doesn't set a different char. For example, it's useful for /(?=abcde).+/. We can't set firstcu for an assertion, however because it leads to incorrect effect for patterns such as /(?=a)a.+/ when the "real" "a" would then become a reqcu instead of a firstcu. This is overcome by a scan at the end if there's no firstcu, looking for an asserted first char. A similar effect for patterns like /(?=.*X)X$/ means we must only take the reqcu when the group also set a firstcu. Otherwise, in that example, 'X' ends up set for both. */ else if ((bravalue == OP_ASSERT || bravalue == OP_ASSERT_NA) && subreqcuflags >= 0 && subfirstcuflags >= 0) { reqcu = subreqcu; reqcuflags = subreqcuflags; } break; /* End of nested group handling */ /* ===================================================================*/ /* Handle named backreferences and recursions. */ case META_BACKREF_BYNAME: case META_RECURSE_BYNAME: { int count, index; PCRE2_SPTR name; BOOL is_dupname = FALSE; named_group *ng = cb->named_groups; uint32_t length = *(++pptr); GETPLUSOFFSET(offset, pptr); name = cb->start_pattern + offset; /* In the first pass, the names generated in the pre-pass are available, but the main name table has not yet been created. Scan the list of names generated in the pre-pass in order to get a number and whether or not this name is duplicated. */ groupnumber = 0; for (i = 0; i < cb->names_found; i++, ng++) { if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0) { is_dupname = ng->isdup; groupnumber = ng->number; /* For a recursion, that's all that is needed. We can now go to the code that handles numerical recursion, applying it to the first group with the given name. */ if (meta == META_RECURSE_BYNAME) { meta_arg = groupnumber; goto HANDLE_NUMERICAL_RECURSION; } /* For a back reference, update the back reference map and the maximum back reference. */ cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; } } /* If the name was not found we have a bad reference. */ if (groupnumber == 0) { *errorcodeptr = ERR15; cb->erroroffset = offset; return 0; } /* If a back reference name is not duplicated, we can handle it as a numerical reference. */ if (!is_dupname) { meta_arg = groupnumber; goto HANDLE_SINGLE_REFERENCE; } /* If a back reference name is duplicated, we generate a different opcode to a numerical back reference. In the second pass we must search for the index and count in the final name table. */ count = 0; /* Values for first pass (avoids compiler warning) */ index = 0; if (lengthptr == NULL && !find_dupname_details(name, length, &index, &count, errorcodeptr, cb)) return 0; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; PUT2INC(code, 0, index); PUT2INC(code, 0, count); } break; /* ===================================================================*/ /* Handle a numerical callout. */ case META_CALLOUT_NUMBER: code[0] = OP_CALLOUT; PUT(code, 1, pptr[1]); /* Offset to next pattern item */ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ code[1 + 2*LINK_SIZE] = pptr[3]; pptr += 3; code += PRIV(OP_lengths)[OP_CALLOUT]; break; /* ===================================================================*/ /* Handle a callout with a string argument. In the pre-pass we just compute the length without generating anything. The length in pptr[3] includes both delimiters; in the actual compile only the first one is copied, but a terminating zero is added. Any doubled delimiters within the string make this an overestimate, but it is not worth bothering about. */ case META_CALLOUT_STRING: if (lengthptr != NULL) { *lengthptr += pptr[3] + (1 + 4*LINK_SIZE); pptr += 3; SKIPOFFSET(pptr); } /* In the real compile we can copy the string. The starting delimiter is included so that the client can discover it if they want. We also pass the start offset to help a script language give better error messages. */ else { PCRE2_SPTR pp; uint32_t delimiter; uint32_t length = pptr[3]; PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE); code[0] = OP_CALLOUT_STR; PUT(code, 1, pptr[1]); /* Offset to next pattern item */ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ pptr += 3; GETPLUSOFFSET(offset, pptr); /* Offset to string in pattern */ pp = cb->start_pattern + offset; delimiter = *callout_string++ = *pp++; if (delimiter == CHAR_LEFT_CURLY_BRACKET) delimiter = CHAR_RIGHT_CURLY_BRACKET; PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1)); /* One after delimiter */ /* The syntax of the pattern was checked in the parsing scan. The length includes both delimiters, but we have passed the opening one just above, so we reduce length before testing it. The test is for > 1 because we do not want to copy the final delimiter. This also ensures that pp[1] is accessible. */ while (--length > 1) { if (*pp == delimiter && pp[1] == delimiter) { *callout_string++ = delimiter; pp += 2; length--; } else *callout_string++ = *pp++; } *callout_string++ = CHAR_NUL; /* Set the length of the entire item, the advance to its end. */ PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code)); code = callout_string; } break; /* ===================================================================*/ /* Handle repetition. The different types are all sorted out in the parsing pass. */ case META_MINMAX_PLUS: case META_MINMAX_QUERY: case META_MINMAX: repeat_min = *(++pptr); repeat_max = *(++pptr); goto REPEAT; case META_ASTERISK: case META_ASTERISK_PLUS: case META_ASTERISK_QUERY: repeat_min = 0; repeat_max = REPEAT_UNLIMITED; goto REPEAT; case META_PLUS: case META_PLUS_PLUS: case META_PLUS_QUERY: repeat_min = 1; repeat_max = REPEAT_UNLIMITED; goto REPEAT; case META_QUERY: case META_QUERY_PLUS: case META_QUERY_QUERY: repeat_min = 0; repeat_max = 1; REPEAT: if (previous_matched_char && repeat_min > 0) matched_char = TRUE; /* Remember whether this is a variable length repeat, and default to single-char opcodes. */ reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; op_type = 0; /* Adjust first and required code units for a zero repeat. */ if (repeat_min == 0) { firstcu = zerofirstcu; firstcuflags = zerofirstcuflags; reqcu = zeroreqcu; reqcuflags = zeroreqcuflags; } /* Note the greediness and possessiveness. */ switch (meta) { case META_MINMAX_PLUS: case META_ASTERISK_PLUS: case META_PLUS_PLUS: case META_QUERY_PLUS: repeat_type = 0; /* Force greedy */ possessive_quantifier = TRUE; break; case META_MINMAX_QUERY: case META_ASTERISK_QUERY: case META_PLUS_QUERY: case META_QUERY_QUERY: repeat_type = greedy_non_default; possessive_quantifier = FALSE; break; default: repeat_type = greedy_default; possessive_quantifier = FALSE; break; } /* Save start of previous item, in case we have to move it up in order to insert something before it, and remember what it was. */ tempcode = previous; op_previous = *previous; /* Now handle repetition for the different types of item. If the repeat minimum and the repeat maximum are both 1, we can ignore the quantifier for non-parenthesized items, as they have only one alternative. For anything in parentheses, we must not ignore if {1} is possessive. */ switch (op_previous) { /* If previous was a character or negated character match, abolish the item and generate a repeat item instead. If a char item has a minimum of more than one, ensure that it is set in reqcu - it might not be if a sequence such as x{3} is the first thing in a branch because the x will have gone into firstcu instead. */ case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; op_type = chartypeoffset[op_previous - OP_CHAR]; /* Deal with UTF characters that take up more than one code unit. */ #ifdef MAYBE_UTF_MULTI if (utf && NOT_FIRSTCU(code[-1])) { PCRE2_UCHAR *lastchar = code - 1; BACKCHAR(lastchar); mclength = (uint32_t)(code - lastchar); /* Length of UTF character */ memcpy(mcbuffer, lastchar, CU2BYTES(mclength)); /* Save the char */ } else #endif /* MAYBE_UTF_MULTI */ /* Handle the case of a single code unit - either with no UTF support, or with UTF disabled, or for a single-code-unit UTF character. */ { mcbuffer[0] = code[-1]; mclength = 1; if (op_previous <= OP_CHARI && repeat_min > 1) { reqcu = mcbuffer[0]; reqcuflags = req_caseopt | cb->req_varyopt; } } goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ /* If previous was a character class or a back reference, we put the repeat stuff after it, but just skip the item if the repeat was {0,0}. */ #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: #endif case OP_CLASS: case OP_NCLASS: case OP_REF: case OP_REFI: case OP_DNREF: case OP_DNREFI: if (repeat_max == 0) { code = previous; goto END_REPEAT; } if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED) *code++ = OP_CRSTAR + repeat_type; else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED) *code++ = OP_CRPLUS + repeat_type; else if (repeat_min == 0 && repeat_max == 1) *code++ = OP_CRQUERY + repeat_type; else { *code++ = OP_CRRANGE + repeat_type; PUT2INC(code, 0, repeat_min); if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0; /* 2-byte encoding for max */ PUT2INC(code, 0, repeat_max); } break; /* If previous is OP_FAIL, it was generated by an empty class [] (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be generated, that is by (*FAIL) or (?!), disallow a quantifier at parse time. We can just ignore this repeat. */ case OP_FAIL: goto END_REPEAT; /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets because pcre2_match() could not handle backtracking into recursively called groups. Now that this backtracking is available, we no longer need to do this. However, we still need to replicate recursions as we do for groups so as to have independent backtracking points. We can replicate for the minimum number of repeats directly. For optional repeats we now wrap the recursion in OP_BRA brackets and make use of the bracket repetition. */ case OP_RECURSE: if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier) goto END_REPEAT; /* Generate unwrapped repeats for a non-zero minimum, except when the minimum is 1 and the maximum unlimited, because that can be handled with OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the minimum, we just need to generate the appropriate additional copies. Otherwise we need to generate one more, to simulate the situation when the minimum is zero. */ if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED)) { int replicate = repeat_min; if (repeat_min == repeat_max) replicate--; /* In the pre-compile phase, we don't actually do the replication. We just adjust the length as if we had. Do some paranoid checks for potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit integer type when available, otherwise double. */ if (lengthptr != NULL) { PCRE2_SIZE delta = replicate*(1 + LINK_SIZE); if ((INT64_OR_DOUBLE)replicate* (INT64_OR_DOUBLE)(1 + LINK_SIZE) > (INT64_OR_DOUBLE)INT_MAX || OFLOW_MAX - *lengthptr < delta) { *errorcodeptr = ERR20; return 0; } *lengthptr += delta; } else for (i = 0; i < replicate; i++) { memcpy(code, previous, CU2BYTES(1 + LINK_SIZE)); previous = code; code += 1 + LINK_SIZE; } /* If the number of repeats is fixed, we are done. Otherwise, adjust the counts and fall through. */ if (repeat_min == repeat_max) break; if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; repeat_min = 0; } /* Wrap the recursion call in OP_BRA brackets. */ (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); op_previous = *previous = OP_BRA; PUT(previous, 1, 2 + 2*LINK_SIZE); previous[2 + 2*LINK_SIZE] = OP_KET; PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); code += 2 + 2 * LINK_SIZE; length_prevgroup = 3 + 3*LINK_SIZE; group_return = -1; /* Set "may match empty string" */ /* Now treat as a repeated OP_BRA. */ /* Fall through */ /* If previous was a bracket group, we may have to replicate it in certain cases. Note that at this point we can encounter only the "basic" bracket opcodes such as BRA and CBRA, as this is the place where they get converted into the more special varieties such as BRAPOS and SBRA. Originally, PCRE did not allow repetition of assertions, but now it does, for Perl compatibility. */ case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERT_NA: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ASSERTBACK_NA: case OP_ONCE: case OP_SCRIPT_RUN: case OP_BRA: case OP_CBRA: case OP_COND: { int len = (int)(code - previous); PCRE2_UCHAR *bralink = NULL; PCRE2_UCHAR *brazeroptr = NULL; if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier) goto END_REPEAT; /* Repeating a DEFINE group (or any group where the condition is always FALSE and there is only one branch) is pointless, but Perl allows the syntax, so we just ignore the repeat. */ if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && previous[GET(previous, 1)] != OP_ALT) goto END_REPEAT; /* Perl allows all assertions to be quantified, and when they contain capturing parentheses and/or are optional there are potential uses for this feature. PCRE2 used to force the maximum quantifier to 1 on the invalid grounds that further repetition was never useful. This was always a bit pointless, since an assertion could be wrapped with a repeated group to achieve the effect. General repetition is now permitted, but if the maximum is unlimited it is set to one more than the minimum. */ if (op_previous < OP_ONCE) /* Assertion */ { if (repeat_max == REPEAT_UNLIMITED) repeat_max = repeat_min + 1; } /* The case of a zero minimum is special because of the need to stick OP_BRAZERO in front of it, and because the group appears once in the data, whereas in other cases it appears the minimum number of times. For this reason, it is simplest to treat this case separately, as otherwise the code gets far too messy. There are several special subcases when the minimum is zero. */ if (repeat_min == 0) { /* If the maximum is also zero, we used to just omit the group from the output altogether, like this: ** if (repeat_max == 0) ** { ** code = previous; ** goto END_REPEAT; ** } However, that fails when a group or a subgroup within it is referenced as a subroutine from elsewhere in the pattern, so now we stick in OP_SKIPZERO in front of it so that it is skipped on execution. As we don't have a list of which groups are referenced, we cannot do this selectively. If the maximum is 1 or unlimited, we just have to stick in the BRAZERO and do no more at this point. */ if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED) { (void)memmove(previous + 1, previous, CU2BYTES(len)); code++; if (repeat_max == 0) { *previous++ = OP_SKIPZERO; goto END_REPEAT; } brazeroptr = previous; /* Save for possessive optimizing */ *previous++ = OP_BRAZERO + repeat_type; } /* If the maximum is greater than 1 and limited, we have to replicate in a nested fashion, sticking OP_BRAZERO before each set of brackets. The first one has to be handled carefully because it's the original copy, which has to be moved up. The remainder can be handled by code that is common with the non-zero minimum case below. We have to adjust the value or repeat_max, since one less copy is required. */ else { int linkoffset; (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); code += 2 + LINK_SIZE; *previous++ = OP_BRAZERO + repeat_type; *previous++ = OP_BRA; /* We chain together the bracket link offset fields that have to be filled in later when the ends of the brackets are reached. */ linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink); bralink = previous; PUTINC(previous, 0, linkoffset); } if (repeat_max != REPEAT_UNLIMITED) repeat_max--; } /* If the minimum is greater than zero, replicate the group as many times as necessary, and adjust the maximum to the number of subsequent copies that we need. */ else { if (repeat_min > 1) { /* In the pre-compile phase, we don't actually do the replication. We just adjust the length as if we had. Do some paranoid checks for potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit integer type when available, otherwise double. */ if (lengthptr != NULL) { PCRE2_SIZE delta = (repeat_min - 1)*length_prevgroup; if ((INT64_OR_DOUBLE)(repeat_min - 1)* (INT64_OR_DOUBLE)length_prevgroup > (INT64_OR_DOUBLE)INT_MAX || OFLOW_MAX - *lengthptr < delta) { *errorcodeptr = ERR20; return 0; } *lengthptr += delta; } /* This is compiling for real. If there is a set first code unit for the group, and we have not yet set a "required code unit", set it. */ else { if (groupsetfirstcu && reqcuflags < 0) { reqcu = firstcu; reqcuflags = firstcuflags; } for (i = 1; (uint32_t)i < repeat_min; i++) { memcpy(code, previous, CU2BYTES(len)); code += len; } } } if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; } /* This code is common to both the zero and non-zero minimum cases. If the maximum is limited, it replicates the group in a nested fashion, remembering the bracket starts on a stack. In the case of a zero minimum, the first one was set up above. In all cases the repeat_max now specifies the number of additional copies needed. Again, we must remember to replicate entries on the forward reference list. */ if (repeat_max != REPEAT_UNLIMITED) { /* In the pre-compile phase, we don't actually do the replication. We just adjust the length as if we had. For each repetition we must add 1 to the length for BRAZERO and for all but the last repetition we must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is a 64-bit integer type when available, otherwise double. */ if (lengthptr != NULL && repeat_max > 0) { PCRE2_SIZE delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - 2 - 2*LINK_SIZE; /* Last one doesn't nest */ if ((INT64_OR_DOUBLE)repeat_max * (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) > (INT64_OR_DOUBLE)INT_MAX || OFLOW_MAX - *lengthptr < delta) { *errorcodeptr = ERR20; return 0; } *lengthptr += delta; } /* This is compiling for real */ else for (i = repeat_max - 1; i >= 0; i--) { *code++ = OP_BRAZERO + repeat_type; /* All but the final copy start a new nesting, maintaining the chain of brackets outstanding. */ if (i != 0) { int linkoffset; *code++ = OP_BRA; linkoffset = (bralink == NULL)? 0 : (int)(code - bralink); bralink = code; PUTINC(code, 0, linkoffset); } memcpy(code, previous, CU2BYTES(len)); code += len; } /* Now chain through the pending brackets, and fill in their length fields (which are holding the chain links pro tem). */ while (bralink != NULL) { int oldlinkoffset; int linkoffset = (int)(code - bralink + 1); PCRE2_UCHAR *bra = code - linkoffset; oldlinkoffset = GET(bra, 1); bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; *code++ = OP_KET; PUTINC(code, 0, linkoffset); PUT(bra, 1, linkoffset); } } /* If the maximum is unlimited, set a repeater in the final copy. For SCRIPT_RUN and ONCE brackets, that's all we need to do. However, possessively repeated ONCE brackets can be converted into non-capturing brackets, as the behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to deal with possessive ONCEs specially. Otherwise, when we are doing the actual compile phase, check to see whether this group is one that could match an empty string. If so, convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so that runtime checking can be done. [This check is also applied to ONCE and SCRIPT_RUN groups at runtime, but in a different way.] Then, if the quantifier was possessive and the bracket is not a conditional, we convert the BRA code to the POS form, and the KET code to KETRPOS. (It turns out to be convenient at runtime to detect this kind of subpattern at both the start and at the end.) The use of special opcodes makes it possible to reduce greatly the stack usage in pcre2_match(). If the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. Then, if the minimum number of matches is 1 or 0, cancel the possessive flag so that the default action below, of wrapping everything inside atomic brackets, does not happen. When the minimum is greater than 1, there will be earlier copies of the group, and so we still have to wrap the whole thing. */ else { PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); /* Convert possessive ONCE brackets to non-capturing */ if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA; /* For non-possessive ONCE and for SCRIPT_RUN brackets, all we need to do is to set the KET. */ if (*bracode == OP_ONCE || *bracode == OP_SCRIPT_RUN) *ketcode = OP_KETRMAX + repeat_type; /* Handle non-SCRIPT_RUN and non-ONCE brackets and possessive ONCEs (which have been converted to non-capturing above). */ else { /* In the compile phase, adjust the opcode if the group can match an empty string. For a conditional group with only one branch, the value of group_return will not show "could be empty", so we must check that separately. */ if (lengthptr == NULL) { if (group_return < 0) *bracode += OP_SBRA - OP_BRA; if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) *bracode = OP_SCOND; } /* Handle possessive quantifiers. */ if (possessive_quantifier) { /* For COND brackets, we wrap the whole thing in a possessively repeated non-capturing bracket, because we have not invented POS versions of the COND opcodes. */ if (*bracode == OP_COND || *bracode == OP_SCOND) { int nlen = (int)(code - bracode); (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); code += 1 + LINK_SIZE; nlen += 1 + LINK_SIZE; *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; *code++ = OP_KETRPOS; PUTINC(code, 0, nlen); PUT(bracode, 1, nlen); } /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ else { *bracode += 1; /* Switch to xxxPOS opcodes */ *ketcode = OP_KETRPOS; } /* If the minimum is zero, mark it as possessive, then unset the possessive flag when the minimum is 0 or 1. */ if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; if (repeat_min < 2) possessive_quantifier = FALSE; } /* Non-possessive quantifier */ else *ketcode = OP_KETRMAX + repeat_type; } } } break; /* If previous was a character type match (\d or similar), abolish it and create a suitable repeat item. The code is shared with single-character repeats by setting op_type to add a suitable offset into repeat_type. Note the the Unicode property types will be present only when SUPPORT_UNICODE is defined, but we don't wrap the little bits of code here because it just makes it horribly messy. */ default: if (op_previous >= OP_EODN) /* Not a character type - internal error */ { *errorcodeptr = ERR10; return 0; } else { int prop_type, prop_value; PCRE2_UCHAR *oldcode; if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ mclength = 0; /* Not a character */ if (op_previous == OP_PROP || op_previous == OP_NOTPROP) { prop_type = previous[1]; prop_value = previous[2]; } else { /* Come here from just above with a character in mcbuffer/mclength. */ OUTPUT_SINGLE_REPEAT: prop_type = prop_value = -1; } /* At this point, if prop_type == prop_value == -1 we either have a character in mcbuffer when mclength is greater than zero, or we have mclength zero, in which case there is a non-property character type in op_previous. If prop_type/value are not negative, we have a property character type in op_previous. */ oldcode = code; /* Save where we were */ code = previous; /* Usually overwrite previous item */ /* If the maximum is zero then the minimum must also be zero; Perl allows this case, so we do too - by simply omitting the item altogether. */ if (repeat_max == 0) goto END_REPEAT; /* Combine the op_type with the repeat_type */ repeat_type += op_type; /* A minimum of zero is handled either as the special case * or ?, or as an UPTO, with the maximum given. */ if (repeat_min == 0) { if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type; else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; else { *code++ = OP_UPTO + repeat_type; PUT2INC(code, 0, repeat_max); } } /* A repeat minimum of 1 is optimized into some special cases. If the maximum is unlimited, we use OP_PLUS. Otherwise, the original item is left in place and, if the maximum is greater than 1, we use OP_UPTO with one less than the maximum. */ else if (repeat_min == 1) { if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_PLUS + repeat_type; else { code = oldcode; /* Leave previous item in place */ if (repeat_max == 1) goto END_REPEAT; *code++ = OP_UPTO + repeat_type; PUT2INC(code, 0, repeat_max - 1); } } /* The case {n,n} is just an EXACT, while the general case {n,m} is handled as an EXACT followed by an UPTO or STAR or QUERY. */ else { *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ PUT2INC(code, 0, repeat_min); /* Unless repeat_max equals repeat_min, fill in the data for EXACT, and then generate the second opcode. For a repeated Unicode property match, there are two extra values that define the required property, and mclength is set zero to indicate this. */ if (repeat_max != repeat_min) { if (mclength > 0) { memcpy(code, mcbuffer, CU2BYTES(mclength)); code += mclength; } else { *code++ = op_previous; if (prop_type >= 0) { *code++ = prop_type; *code++ = prop_value; } } /* Now set up the following opcode */ if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type; else { repeat_max -= repeat_min; if (repeat_max == 1) { *code++ = OP_QUERY + repeat_type; } else { *code++ = OP_UPTO + repeat_type; PUT2INC(code, 0, repeat_max); } } } } /* Fill in the character or character type for the final opcode. */ if (mclength > 0) { memcpy(code, mcbuffer, CU2BYTES(mclength)); code += mclength; } else { *code++ = op_previous; if (prop_type >= 0) { *code++ = prop_type; *code++ = prop_value; } } } break; } /* End of switch on different op_previous values */ /* If the character following a repeat is '+', possessive_quantifier is TRUE. For some opcodes, there are special alternative opcodes for this case. For anything else, we wrap the entire repeated item inside OP_ONCE brackets. Logically, the '+' notation is just syntactic sugar, taken from Sun's Java package, but the special opcodes can optimize it. Some (but not all) possessively repeated subpatterns have already been completely handled in the code just above. For them, possessive_quantifier is always FALSE at this stage. Note that the repeated item starts at tempcode, not at previous, which might be the first part of a string whose (former) last char we repeated. */ if (possessive_quantifier) { int len; /* Possessifying an EXACT quantifier has no effect, so we can ignore it. However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6}, {5,}, or {5,10}). We skip over an EXACT item; if the length of what remains is greater than zero, there's a further opcode that can be handled. If not, do nothing, leaving the EXACT alone. */ switch(*tempcode) { case OP_TYPEEXACT: tempcode += PRIV(OP_lengths)[*tempcode] + ((tempcode[1 + IMM2_SIZE] == OP_PROP || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); break; /* CHAR opcodes are used for exacts whose count is 1. */ case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_EXACT: case OP_EXACTI: case OP_NOTEXACT: case OP_NOTEXACTI: tempcode += PRIV(OP_lengths)[*tempcode]; #ifdef SUPPORT_UNICODE if (utf && HAS_EXTRALEN(tempcode[-1])) tempcode += GET_EXTRALEN(tempcode[-1]); #endif break; /* For the class opcodes, the repeat operator appears at the end; adjust tempcode to point to it. */ case OP_CLASS: case OP_NCLASS: tempcode += 1 + 32/sizeof(PCRE2_UCHAR); break; #ifdef SUPPORT_WIDE_CHARS case OP_XCLASS: tempcode += GET(tempcode, 1); break; #endif } /* If tempcode is equal to code (which points to the end of the repeated item), it means we have skipped an EXACT item but there is no following QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In all other cases, tempcode will be pointing to the repeat opcode, and will be less than code, so the value of len will be greater than 0. */ len = (int)(code - tempcode); if (len > 0) { unsigned int repcode = *tempcode; /* There is a table for possessifying opcodes, all of which are less than OP_CALLOUT. A zero entry means there is no possessified version. */ if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) *tempcode = opcode_possessify[repcode]; /* For opcode without a special possessified version, wrap the item in ONCE brackets. */ else { (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); code += 1 + LINK_SIZE; len += 1 + LINK_SIZE; tempcode[0] = OP_ONCE; *code++ = OP_KET; PUTINC(code, 0, len); PUT(tempcode, 1, len); } } } /* We set the "follows varying string" flag for subsequently encountered reqcus if it isn't already set and we have just passed a varying length item. */ END_REPEAT: cb->req_varyopt |= reqvary; break; /* ===================================================================*/ /* Handle a 32-bit data character with a value greater than META_END. */ case META_BIGVALUE: pptr++; goto NORMAL_CHAR; /* ===============================================================*/ /* Handle a back reference by number, which is the meta argument. The pattern offsets for back references to group numbers less than 10 are held in a special vector, to avoid using more than two parsed pattern elements in 64-bit environments. We only need the offset to the first occurrence, because if that doesn't fail, subsequent ones will also be OK. */ case META_BACKREF: if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg]; else GETPLUSOFFSET(offset, pptr); if (meta_arg > cb->bracount) { cb->erroroffset = offset; *errorcodeptr = ERR15; /* Non-existent subpattern */ return 0; } /* Come here from named backref handling when the reference is to a single group (that is, not to a duplicated name). The back reference data will have already been updated. We must disable firstcu if not set, to cope with cases like (?=(\w+))\1: which would otherwise set ':' later. */ HANDLE_SINGLE_REFERENCE: if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE; *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; PUT2INC(code, 0, meta_arg); /* Update the map of back references, and keep the highest one. We could do this in parse_regex() for numerical back references, but not for named back references, because we don't know the numbers to which named back references refer. So we do it all in this function. */ cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1; if (meta_arg > cb->top_backref) cb->top_backref = meta_arg; break; /* ===============================================================*/ /* Handle recursion by inserting the number of the called group (which is the meta argument) after OP_RECURSE. At the end of compiling the pattern is scanned and these numbers are replaced by offsets within the pattern. It is done like this to avoid problems with forward references and adjusting offsets when groups are duplicated and moved (as discovered in previous implementations). Note that a recursion does not have a set first character. */ case META_RECURSE: GETPLUSOFFSET(offset, pptr); if (meta_arg > cb->bracount) { cb->erroroffset = offset; *errorcodeptr = ERR15; /* Non-existent subpattern */ return 0; } HANDLE_NUMERICAL_RECURSION: *code = OP_RECURSE; PUT(code, 1, meta_arg); code += 1 + LINK_SIZE; groupsetfirstcu = FALSE; cb->had_recurse = TRUE; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; break; /* ===============================================================*/ /* Handle capturing parentheses; the number is the meta argument. */ case META_CAPTURE: bravalue = OP_CBRA; skipunits = IMM2_SIZE; PUT2(code, 1+LINK_SIZE, meta_arg); cb->lastcapture = meta_arg; goto GROUP_PROCESS_NOTE_EMPTY; /* ===============================================================*/ /* Handle escape sequence items. For ones like \d, the ESC_values are arranged to be the same as the corresponding OP_values in the default case when PCRE2_UCP is not set (which is the only case in which they will appear here). Note: \Q and \E are never seen here, as they were dealt with in parse_pattern(). Neither are numerical back references or recursions, which were turned into META_BACKREF or META_RECURSE items, respectively. \k and \g, when followed by names, are turned into META_BACKREF_BYNAME or META_RECURSE_BYNAME. */ case META_ESCAPE: /* We can test for escape sequences that consume a character because their values lie between ESC_b and ESC_Z; this may have to change if any new ones are ever created. For these sequences, we disable the setting of a first character if it hasn't already been set. */ if (meta_arg > ESC_b && meta_arg < ESC_Z) { matched_char = TRUE; if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; } /* Set values to reset to if this is followed by a zero repeat. */ zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; /* If Unicode is not supported, \P and \p are not allowed and are faulted at parse time, so will never appear here. */ #ifdef SUPPORT_UNICODE if (meta_arg == ESC_P || meta_arg == ESC_p) { uint32_t ptype = *(++pptr) >> 16; uint32_t pdata = *pptr & 0xffff; /* The special case of \p{Any} is compiled to OP_ALLANY so as to benefit from the auto-anchoring code. */ if (meta_arg == ESC_p && ptype == PT_ANY) { *code++ = OP_ALLANY; } else { *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP; *code++ = ptype; *code++ = pdata; } break; /* End META_ESCAPE */ } #endif /* \K is forbidden in lookarounds since 10.38 because that's what Perl has done. However, there's an option, in case anyone was relying on it. */ if (cb->assert_depth > 0 && meta_arg == ESC_K && (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) == 0) { *errorcodeptr = ERR99; return 0; } /* For the rest (including \X when Unicode is supported - if not it's faulted at parse time), the OP value is the escape value when PCRE2_UCP is not set; if it is set, these escapes do not show up here because they are converted into Unicode property tests in parse_regex(). Note that \b and \B do a one-character lookbehind, and \A also behaves as if it does. */ if (meta_arg == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */ if ((meta_arg == ESC_b || meta_arg == ESC_B || meta_arg == ESC_A) && cb->max_lookbehind == 0) cb->max_lookbehind = 1; /* In non-UTF mode, and for both 32-bit modes, we turn \C into OP_ALLANY instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds. */ #if PCRE2_CODE_UNIT_WIDTH == 32 *code++ = (meta_arg == ESC_C)? OP_ALLANY : meta_arg; #else *code++ = (!utf && meta_arg == ESC_C)? OP_ALLANY : meta_arg; #endif break; /* End META_ESCAPE */ /* ===================================================================*/ /* Handle an unrecognized meta value. A parsed pattern value less than META_END is a literal. Otherwise we have a problem. */ default: if (meta >= META_END) { #ifdef DEBUG_SHOW_PARSED fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x\n", *pptr); #endif *errorcodeptr = ERR89; /* Internal error - unrecognized. */ return 0; } /* Handle a literal character. We come here by goto in the case of a 32-bit, non-UTF character whose value is greater than META_END. */ NORMAL_CHAR: meta = *pptr; /* Get the full 32 bits */ NORMAL_CHAR_SET: /* Character is already in meta */ matched_char = TRUE; /* For caseless UTF or UCP mode, check whether this character has more than one other case. If so, generate a special OP_PROP item instead of OP_CHARI. */ #ifdef SUPPORT_UNICODE if ((utf||ucp) && (options & PCRE2_CASELESS) != 0) { uint32_t caseset = UCD_CASESET(meta); if (caseset != 0) { *code++ = OP_PROP; *code++ = PT_CLIST; *code++ = caseset; if (firstcuflags == REQ_UNSET) firstcuflags = zerofirstcuflags = REQ_NONE; break; /* End handling this meta item */ } } #endif /* Caseful matches, or caseless and not one of the multicase characters. We come here by goto in the case of a positive class that contains only case-partners of a character with just two cases; matched_char has already been set TRUE and options fudged if necessary. */ CLASS_CASELESS_CHAR: /* Get the character's code units into mcbuffer, with the length in mclength. When not in UTF mode, the length is always 1. */ #ifdef SUPPORT_UNICODE if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else #endif { mclength = 1; mcbuffer[0] = meta; } /* Generate the appropriate code */ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; memcpy(code, mcbuffer, CU2BYTES(mclength)); code += mclength; /* Remember if \r or \n were seen */ if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; /* Set the first and required code units appropriately. If no previous first code unit, set it from this character, but revert to none on a zero repeat. Otherwise, leave the firstcu value alone, and don't change it on a zero repeat. */ if (firstcuflags == REQ_UNSET) { zerofirstcuflags = REQ_NONE; zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; /* If the character is more than one code unit long, we can set a single firstcu only if it is not to be matched caselessly. Multiple possible starting code units may be picked up later in the studying code. */ if (mclength == 1 || req_caseopt == 0) { firstcu = mcbuffer[0]; firstcuflags = req_caseopt; if (mclength != 1) { reqcu = code[-1]; reqcuflags = cb->req_varyopt; } } else firstcuflags = reqcuflags = REQ_NONE; } /* firstcu was previously set; we can set reqcu only if the length is 1 or the matching is caseful. */ else { zerofirstcu = firstcu; zerofirstcuflags = firstcuflags; zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; if (mclength == 1 || req_caseopt == 0) { reqcu = code[-1]; reqcuflags = req_caseopt | cb->req_varyopt; } } /* If caselessness was temporarily instated, reset it. */ if (reset_caseful) { options &= ~PCRE2_CASELESS; req_caseopt = 0; reset_caseful = FALSE; } break; /* End literal character handling */ } /* End of big switch */ } /* End of big loop */ /* Control never reaches here. */ } /************************************************* * Compile regex: a sequence of alternatives * *************************************************/ /* On entry, pptr is pointing past the bracket meta, but on return it points to the closing bracket or META_END. The code variable is pointing at the code unit into which the BRA operator has been stored. This function is used during the pre-compile phase when we are trying to find out the amount of memory needed, as well as during the real compile phase. The value of lengthptr distinguishes the two phases. Arguments: options option bits, including any changes for this subpattern codeptr -> the address of the current code pointer pptrptr -> the address of the current parsed pattern pointer errorcodeptr -> pointer to error code variable skipunits skip this many code units at start (for brackets and OP_COND) firstcuptr place to put the first required code unit firstcuflagsptr place to put the first code unit flags, or a negative number reqcuptr place to put the last required code unit reqcuflagsptr place to put the last required code unit flags, or a negative number bcptr pointer to the chain of currently open branches cb points to the data block with tables pointers etc. lengthptr NULL during the real compile phase points to length accumulator during pre-compile phase Returns: 0 There has been an error +1 Success, this group must match at least one character -1 Success, this group may match an empty string */ static int compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, int *errorcodeptr, uint32_t skipunits, uint32_t *firstcuptr, int32_t *firstcuflagsptr, uint32_t *reqcuptr,int32_t *reqcuflagsptr, branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr) { PCRE2_UCHAR *code = *codeptr; PCRE2_UCHAR *last_branch = code; PCRE2_UCHAR *start_bracket = code; BOOL lookbehind; open_capitem capitem; int capnumber = 0; int okreturn = 1; uint32_t *pptr = *pptrptr; uint32_t firstcu, reqcu; uint32_t lookbehindlength; int32_t firstcuflags, reqcuflags; uint32_t branchfirstcu, branchreqcu; int32_t branchfirstcuflags, branchreqcuflags; PCRE2_SIZE length; branch_chain bc; /* If set, call the external function that checks for stack availability. */ if (cb->cx->stack_guard != NULL && cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) { *errorcodeptr= ERR33; return 0; } /* Miscellaneous initialization */ bc.outer = bcptr; bc.current_branch = code; firstcu = reqcu = 0; firstcuflags = reqcuflags = REQ_UNSET; /* Accumulate the length for use in the pre-compile phase. Start with the length of the BRA and KET and any extra code units that are required at the beginning. We accumulate in a local variable to save frequent testing of lengthptr for NULL. We cannot do this by looking at the value of 'code' at the start and end of each alternative, because compiled items are discarded during the pre-compile phase so that the workspace is not exceeded. */ length = 2 + 2*LINK_SIZE + skipunits; /* Remember if this is a lookbehind assertion, and if it is, save its length and skip over the pattern offset. */ lookbehind = *code == OP_ASSERTBACK || *code == OP_ASSERTBACK_NOT || *code == OP_ASSERTBACK_NA; if (lookbehind) { lookbehindlength = META_DATA(pptr[-1]); pptr += SIZEOFFSET; } else lookbehindlength = 0; /* If this is a capturing subpattern, add to the chain of open capturing items so that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA need be tested here; changing this opcode to one of its variants, e.g. OP_SCBRAPOS, happens later, after the group has been compiled. */ if (*code == OP_CBRA) { capnumber = GET2(code, 1 + LINK_SIZE); capitem.number = capnumber; capitem.next = cb->open_caps; capitem.assert_depth = cb->assert_depth; cb->open_caps = &capitem; } /* Offset is set zero to mark that this bracket is still open */ PUT(code, 1, 0); code += 1 + LINK_SIZE + skipunits; /* Loop for each alternative branch */ for (;;) { int branch_return; /* Insert OP_REVERSE if this is as lookbehind assertion. */ if (lookbehind && lookbehindlength > 0) { *code++ = OP_REVERSE; PUTINC(code, 0, lookbehindlength); length += 1 + LINK_SIZE; } /* Now compile the branch; in the pre-compile phase its length gets added into the length. */ if ((branch_return = compile_branch(&options, &code, &pptr, errorcodeptr, &branchfirstcu, &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, cb, (lengthptr == NULL)? NULL : &length)) == 0) return 0; /* If a branch can match an empty string, so can the whole group. */ if (branch_return < 0) okreturn = -1; /* In the real compile phase, there is some post-processing to be done. */ if (lengthptr == NULL) { /* If this is the first branch, the firstcu and reqcu values for the branch become the values for the regex. */ if (*last_branch != OP_ALT) { firstcu = branchfirstcu; firstcuflags = branchfirstcuflags; reqcu = branchreqcu; reqcuflags = branchreqcuflags; } /* If this is not the first branch, the first char and reqcu have to match the values from all the previous branches, except that if the previous value for reqcu didn't have REQ_VARY set, it can still match, and we set REQ_VARY for the group from this branch's value. */ else { /* If we previously had a firstcu, but it doesn't match the new branch, we have to abandon the firstcu for the regex, but if there was previously no reqcu, it takes on the value of the old firstcu. */ if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) { if (firstcuflags >= 0) { if (reqcuflags < 0) { reqcu = firstcu; reqcuflags = firstcuflags; } } firstcuflags = REQ_NONE; } /* If we (now or from before) have no firstcu, a firstcu from the branch becomes a reqcu if there isn't a branch reqcu. */ if (firstcuflags < 0 && branchfirstcuflags >= 0 && branchreqcuflags < 0) { branchreqcu = branchfirstcu; branchreqcuflags = branchfirstcuflags; } /* Now ensure that the reqcus match */ if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || reqcu != branchreqcu) reqcuflags = REQ_NONE; else { reqcu = branchreqcu; reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY if present */ } } } /* Handle reaching the end of the expression, either ')' or end of pattern. In the real compile phase, go back through the alternative branches and reverse the chain of offsets, with the field in the BRA item now becoming an offset to the first alternative. If there are no alternatives, it points to the end of the group. The length in the terminating ket is always the length of the whole bracketed item. Return leaving the pointer at the terminating char. */ if (META_CODE(*pptr) != META_ALT) { if (lengthptr == NULL) { PCRE2_SIZE branch_length = code - last_branch; do { PCRE2_SIZE prev_length = GET(last_branch, 1); PUT(last_branch, 1, branch_length); branch_length = prev_length; last_branch -= branch_length; } while (branch_length > 0); } /* Fill in the ket */ *code = OP_KET; PUT(code, 1, (int)(code - start_bracket)); code += 1 + LINK_SIZE; /* If it was a capturing subpattern, remove the block from the chain. */ if (capnumber > 0) cb->open_caps = cb->open_caps->next; /* Set values to pass back */ *codeptr = code; *pptrptr = pptr; *firstcuptr = firstcu; *firstcuflagsptr = firstcuflags; *reqcuptr = reqcu; *reqcuflagsptr = reqcuflags; if (lengthptr != NULL) { if (OFLOW_MAX - *lengthptr < length) { *errorcodeptr = ERR20; return 0; } *lengthptr += length; } return okreturn; } /* Another branch follows. In the pre-compile phase, we can move the code pointer back to where it was for the start of the first branch. (That is, pretend that each branch is the only one.) In the real compile phase, insert an ALT node. Its length field points back to the previous branch while the bracket remains open. At the end the chain is reversed. It's done like this so that the start of the bracket has a zero offset until it is closed, making it possible to detect recursion. */ if (lengthptr != NULL) { code = *codeptr + 1 + LINK_SIZE + skipunits; length += 1 + LINK_SIZE; } else { *code = OP_ALT; PUT(code, 1, (int)(code - last_branch)); bc.current_branch = last_branch = code; code += 1 + LINK_SIZE; } /* Set the lookbehind length (if not in a lookbehind the value will be zero) and then advance past the vertical bar. */ lookbehindlength = META_DATA(*pptr); pptr++; } /* Control never reaches here */ } /************************************************* * Check for anchored pattern * *************************************************/ /* Try to find out if this is an anchored regular expression. Consider each alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then it's anchored. However, if this is a multiline pattern, then only OP_SOD will be found, because ^ generates OP_CIRCM in that mode. We can also consider a regex to be anchored if OP_SOM starts all its branches. This is the code for \G, which means "match at start of match position, taking into account the match offset". A branch is also implicitly anchored if it starts with .* and DOTALL is set, because that will try the rest of the pattern at all possible matching points, so there is no point trying again.... er .... .... except when the .* appears inside capturing parentheses, and there is a subsequent back reference to those parentheses. We haven't enough information to catch that case precisely. At first, the best we could do was to detect when .* was in capturing brackets and the highest back reference was greater than or equal to that level. However, by keeping a bitmap of the first 31 back references, we can catch some of the more common cases more precisely. ... A second exception is when the .* appears inside an atomic group, because this prevents the number of characters it matches from being adjusted. Arguments: code points to start of the compiled pattern bracket_map a bitmap of which brackets we are inside while testing; this handles up to substring 31; after that we just have to take the less precise approach cb points to the compile data block atomcount atomic group level inassert TRUE if in an assertion Returns: TRUE or FALSE */ static BOOL is_anchored(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, int atomcount, BOOL inassert) { do { PCRE2_SPTR scode = first_significant_code( code + PRIV(OP_lengths)[*code], FALSE); int op = *scode; /* Non-capturing brackets */ if (op == OP_BRA || op == OP_BRAPOS || op == OP_SBRA || op == OP_SBRAPOS) { if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) return FALSE; } /* Capturing brackets */ else if (op == OP_CBRA || op == OP_CBRAPOS || op == OP_SCBRA || op == OP_SCBRAPOS) { int n = GET2(scode, 1+LINK_SIZE); int new_map = bracket_map | ((n < 32)? (1u << n) : 1); if (!is_anchored(scode, new_map, cb, atomcount, inassert)) return FALSE; } /* Positive forward assertion */ else if (op == OP_ASSERT || op == OP_ASSERT_NA) { if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; } /* Condition. If there is no second branch, it can't be anchored. */ else if (op == OP_COND || op == OP_SCOND) { if (scode[GET(scode,1)] != OP_ALT) return FALSE; if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) return FALSE; } /* Atomic groups */ else if (op == OP_ONCE) { if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert)) return FALSE; } /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and it isn't in brackets that are or may be referenced or inside an atomic group or an assertion. Also the pattern must not contain *PRUNE or *SKIP, because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/ with the subject "aab", which matches "b", i.e. not at the start of a line. There is also an option that disables auto-anchoring. */ else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)) { if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 || atomcount > 0 || cb->had_pruneorskip || inassert || (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) return FALSE; } /* Check for explicit anchoring */ else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; code += GET(code, 1); } while (*code == OP_ALT); /* Loop for each alternative */ return TRUE; } /************************************************* * Check for starting with ^ or .* * *************************************************/ /* This is called to find out if every branch starts with ^ or .* so that "first char" processing can be done to speed things up in multiline matching and for non-DOTALL patterns that start with .* (which must start at the beginning or after \n). As in the case of is_anchored() (see above), we have to take account of back references to capturing brackets that contain .* because in that case we can't make the assumption. Also, the appearance of .* inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE or *SKIP does not count, because once again the assumption no longer holds. Arguments: code points to start of the compiled pattern or a group bracket_map a bitmap of which brackets we are inside while testing; this handles up to substring 31; after that we just have to take the less precise approach cb points to the compile data atomcount atomic group level inassert TRUE if in an assertion Returns: TRUE or FALSE */ static BOOL is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, int atomcount, BOOL inassert) { do { PCRE2_SPTR scode = first_significant_code( code + PRIV(OP_lengths)[*code], FALSE); int op = *scode; /* If we are at the start of a conditional assertion group, *both* the conditional assertion *and* what follows the condition must satisfy the test for start of line. Other kinds of condition fail. Note that there may be an auto-callout at the start of a condition. */ if (op == OP_COND) { scode += 1 + LINK_SIZE; if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); switch (*scode) { case OP_CREF: case OP_DNCREF: case OP_RREF: case OP_DNRREF: case OP_FAIL: case OP_FALSE: case OP_TRUE: return FALSE; default: /* Assertion */ if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; do scode += GET(scode, 1); while (*scode == OP_ALT); scode += 1 + LINK_SIZE; break; } scode = first_significant_code(scode, FALSE); op = *scode; } /* Non-capturing brackets */ if (op == OP_BRA || op == OP_BRAPOS || op == OP_SBRA || op == OP_SBRAPOS) { if (!is_startline(scode, bracket_map, cb, atomcount, inassert)) return FALSE; } /* Capturing brackets */ else if (op == OP_CBRA || op == OP_CBRAPOS || op == OP_SCBRA || op == OP_SCBRAPOS) { int n = GET2(scode, 1+LINK_SIZE); int new_map = bracket_map | ((n < 32)? (1u << n) : 1); if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE; } /* Positive forward assertions */ else if (op == OP_ASSERT || op == OP_ASSERT_NA) { if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; } /* Atomic brackets */ else if (op == OP_ONCE) { if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert)) return FALSE; } /* .* means "start at start or after \n" if it isn't in atomic brackets or brackets that may be referenced or an assertion, and as long as the pattern does not contain *PRUNE or *SKIP, because these break the feature. Consider, for example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the start of a line. There is also an option that disables this optimization. */ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) { if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 || atomcount > 0 || cb->had_pruneorskip || inassert || (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) return FALSE; } /* Check for explicit circumflex; anything else gives a FALSE result. Note in particular that this includes atomic brackets OP_ONCE because the number of characters matched by .* cannot be adjusted inside them. */ else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; /* Move on to the next alternative */ code += GET(code, 1); } while (*code == OP_ALT); /* Loop for each alternative */ return TRUE; } /************************************************* * Scan compiled regex for recursion reference * *************************************************/ /* This function scans through a compiled pattern until it finds an instance of OP_RECURSE. Arguments: code points to start of expression utf TRUE in UTF mode Returns: pointer to the opcode for OP_RECURSE, or NULL if not found */ static PCRE2_SPTR find_recurse(PCRE2_SPTR code, BOOL utf) { for (;;) { PCRE2_UCHAR c = *code; if (c == OP_END) return NULL; if (c == OP_RECURSE) return code; /* XCLASS is used for classes that cannot be represented just by a bit map. This includes negated single high-valued characters. CALLOUT_STR is used for callouts with string arguments. In both cases the length in the table is zero; the actual length is stored in the compiled code. */ if (c == OP_XCLASS) code += GET(code, 1); else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); /* Otherwise, we can get the item's length from the table, except that for repeated character types, we have to test for \p and \P, which have an extra two code units of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we must add in its length. */ else { switch(c) { case OP_TYPESTAR: case OP_TYPEMINSTAR: case OP_TYPEPLUS: case OP_TYPEMINPLUS: case OP_TYPEQUERY: case OP_TYPEMINQUERY: case OP_TYPEPOSSTAR: case OP_TYPEPOSPLUS: case OP_TYPEPOSQUERY: if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; break; case OP_TYPEPOSUPTO: case OP_TYPEUPTO: case OP_TYPEMINUPTO: case OP_TYPEEXACT: if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; break; case OP_MARK: case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: code += code[1]; break; } /* Add in the fixed length from the table */ code += PRIV(OP_lengths)[c]; /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be followed by a multi-unit character. The length in the table is a minimum, so we have to arrange to skip the extra units. */ #ifdef MAYBE_UTF_MULTI if (utf) switch(c) { case OP_CHAR: case OP_CHARI: case OP_NOT: case OP_NOTI: case OP_EXACT: case OP_EXACTI: case OP_NOTEXACT: case OP_NOTEXACTI: case OP_UPTO: case OP_UPTOI: case OP_NOTUPTO: case OP_NOTUPTOI: case OP_MINUPTO: case OP_MINUPTOI: case OP_NOTMINUPTO: case OP_NOTMINUPTOI: case OP_POSUPTO: case OP_POSUPTOI: case OP_NOTPOSUPTO: case OP_NOTPOSUPTOI: case OP_STAR: case OP_STARI: case OP_NOTSTAR: case OP_NOTSTARI: case OP_MINSTAR: case OP_MINSTARI: case OP_NOTMINSTAR: case OP_NOTMINSTARI: case OP_POSSTAR: case OP_POSSTARI: case OP_NOTPOSSTAR: case OP_NOTPOSSTARI: case OP_PLUS: case OP_PLUSI: case OP_NOTPLUS: case OP_NOTPLUSI: case OP_MINPLUS: case OP_MINPLUSI: case OP_NOTMINPLUS: case OP_NOTMINPLUSI: case OP_POSPLUS: case OP_POSPLUSI: case OP_NOTPOSPLUS: case OP_NOTPOSPLUSI: case OP_QUERY: case OP_QUERYI: case OP_NOTQUERY: case OP_NOTQUERYI: case OP_MINQUERY: case OP_MINQUERYI: case OP_NOTMINQUERY: case OP_NOTMINQUERYI: case OP_POSQUERY: case OP_POSQUERYI: case OP_NOTPOSQUERY: case OP_NOTPOSQUERYI: if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); break; } #else (void)(utf); /* Keep compiler happy by referencing function argument */ #endif /* MAYBE_UTF_MULTI */ } } } /************************************************* * Check for asserted fixed first code unit * *************************************************/ /* During compilation, the "first code unit" settings from forward assertions are discarded, because they can cause conflicts with actual literals that follow. However, if we end up without a first code unit setting for an unanchored pattern, it is worth scanning the regex to see if there is an initial asserted first code unit. If all branches start with the same asserted code unit, or with a non-conditional bracket all of whose alternatives start with the same asserted code unit (recurse ad lib), then we return that code unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with REQ_NONE in the flags. Arguments: code points to start of compiled pattern flags points to the first code unit flags inassert non-zero if in an assertion Returns: the fixed first code unit, or 0 with REQ_NONE in flags */ static uint32_t find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, uint32_t inassert) { uint32_t c = 0; int cflags = REQ_NONE; *flags = REQ_NONE; do { uint32_t d; int dflags; int xl = (*code == OP_CBRA || *code == OP_SCBRA || *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE); PCRE2_UCHAR op = *scode; switch(op) { default: return 0; case OP_BRA: case OP_BRAPOS: case OP_CBRA: case OP_SCBRA: case OP_CBRAPOS: case OP_SCBRAPOS: case OP_ASSERT: case OP_ASSERT_NA: case OP_ONCE: case OP_SCRIPT_RUN: d = find_firstassertedcu(scode, &dflags, inassert + ((op == OP_ASSERT || op == OP_ASSERT_NA)?1:0)); if (dflags < 0) return 0; if (cflags < 0) { c = d; cflags = dflags; } else if (c != d || cflags != dflags) return 0; break; case OP_EXACT: scode += IMM2_SIZE; /* Fall through */ case OP_CHAR: case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: if (inassert == 0) return 0; if (cflags < 0) { c = scode[1]; cflags = 0; } else if (c != scode[1]) return 0; break; case OP_EXACTI: scode += IMM2_SIZE; /* Fall through */ case OP_CHARI: case OP_PLUSI: case OP_MINPLUSI: case OP_POSPLUSI: if (inassert == 0) return 0; /* If the character is more than one code unit long, we cannot set its first code unit when matching caselessly. Later scanning may pick up multiple code units. */ #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 if (scode[1] >= 0x80) return 0; #elif PCRE2_CODE_UNIT_WIDTH == 16 if (scode[1] >= 0xd800 && scode[1] <= 0xdfff) return 0; #endif #endif if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } else if (c != scode[1]) return 0; break; } code += GET(code, 1); } while (*code == OP_ALT); *flags = cflags; return c; } /************************************************* * Add an entry to the name/number table * *************************************************/ /* This function is called between compiling passes to add an entry to the name/number table, maintaining alphabetical order. Checking for permitted and forbidden duplicates has already been done. Arguments: cb the compile data block name the name to add length the length of the name groupno the group number tablecount the count of names in the table so far Returns: nothing */ static void add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, unsigned int groupno, uint32_t tablecount) { uint32_t i; PCRE2_UCHAR *slot = cb->name_table; for (i = 0; i < tablecount; i++) { int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); if (crc == 0 && slot[IMM2_SIZE+length] != 0) crc = -1; /* Current name is a substring */ /* Make space in the table and break the loop for an earlier name. For a duplicate or later name, carry on. We do this for duplicates so that in the simple case (when ?(| is not used) they are in order of their numbers. In all cases they are in the order in which they appear in the pattern. */ if (crc < 0) { (void)memmove(slot + cb->name_entry_size, slot, CU2BYTES((tablecount - i) * cb->name_entry_size)); break; } /* Continue the loop for a later or duplicate name */ slot += cb->name_entry_size; } PUT2(slot, 0, groupno); memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); /* Add a terminating zero and fill the rest of the slot with zeroes so that the memory is all initialized. Otherwise valgrind moans about uninitialized memory when saving serialized compiled patterns. */ memset(slot + IMM2_SIZE + length, 0, CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); } /************************************************* * Skip in parsed pattern * *************************************************/ /* This function is called to skip parts of the parsed pattern when finding the length of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find the end of the branch, it is called to skip over an internal lookaround or (DEFINE) group, and it is also called to skip to the end of a class, during which it will never encounter nested groups (but there's no need to have special code for that). When called to find the end of a branch or group, pptr must point to the first meta code inside the branch, not the branch-starting code. In other cases it can point to the item that causes the function to be called. Arguments: pptr current pointer to skip from skiptype PSKIP_CLASS when skipping to end of class PSKIP_ALT when META_ALT ends the skip PSKIP_KET when only META_KET ends the skip Returns: new value of pptr NULL if META_END is reached - should never occur or for an unknown meta value - likewise */ static uint32_t * parsed_skip(uint32_t *pptr, uint32_t skiptype) { uint32_t nestlevel = 0; for (;; pptr++) { uint32_t meta = META_CODE(*pptr); switch(meta) { default: /* Just skip over most items */ if (meta < META_END) continue; /* Literal */ break; /* This should never occur. */ case META_END: return NULL; /* The data for these items is variable in length. */ case META_BACKREF: /* Offset is present only if group >= 10 */ if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET; break; case META_ESCAPE: /* A few escapes are followed by data items. */ switch (META_DATA(*pptr)) { case ESC_P: case ESC_p: pptr += 1; break; case ESC_g: case ESC_k: pptr += 1 + SIZEOFFSET; break; } break; case META_MARK: /* Add the length of the name. */ case META_COMMIT_ARG: case META_PRUNE_ARG: case META_SKIP_ARG: case META_THEN_ARG: pptr += pptr[1]; break; /* These are the "active" items in this loop. */ case META_CLASS_END: if (skiptype == PSKIP_CLASS) return pptr; break; case META_ATOMIC: case META_CAPTURE: case META_COND_ASSERT: case META_COND_DEFINE: case META_COND_NAME: case META_COND_NUMBER: case META_COND_RNAME: case META_COND_RNUMBER: case META_COND_VERSION: case META_LOOKAHEAD: case META_LOOKAHEADNOT: case META_LOOKAHEAD_NA: case META_LOOKBEHIND: case META_LOOKBEHINDNOT: case META_LOOKBEHIND_NA: case META_NOCAPTURE: case META_SCRIPT_RUN: nestlevel++; break; case META_ALT: if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr; break; case META_KET: if (nestlevel == 0) return pptr; nestlevel--; break; } /* The extra data item length for each meta is in a table. */ meta = (meta >> 16) & 0x7fff; if (meta >= sizeof(meta_extra_lengths)) return NULL; pptr += meta_extra_lengths[meta]; } /* Control never reaches here */ return pptr; } /************************************************* * Find length of a parsed group * *************************************************/ /* This is called for nested groups within a branch of a lookbehind whose length is being computed. If all the branches in the nested group have the same length, that is OK. On entry, the pointer must be at the first element after the group initializing code. On exit it points to OP_KET. Caching is used to improve processing speed when the same capturing group occurs many times. Arguments: pptrptr pointer to pointer in the parsed pattern isinline FALSE if a reference or recursion; TRUE for inline group errcodeptr pointer to the errorcode lcptr pointer to the loop counter group number of captured group or -1 for a non-capturing group recurses chain of recurse_check to catch mutual recursion cb pointer to the compile data Returns: the group length or a negative number */ static int get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr, int group, parsed_recurse_check *recurses, compile_block *cb) { int branchlength; int grouplength = -1; /* The cache can be used only if there is no possibility of there being two groups with the same number. We do not need to set the end pointer for a group that is being processed as a back reference or recursion, but we must do so for an inline group. */ if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0) { uint32_t groupinfo = cb->groupinfo[group]; if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) { if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET); return groupinfo & GI_FIXED_LENGTH_MASK; } } /* Scan the group. In this case we find the end pointer of necessity. */ for(;;) { branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); if (branchlength < 0) goto ISNOTFIXED; if (grouplength == -1) grouplength = branchlength; else if (grouplength != branchlength) goto ISNOTFIXED; if (**pptrptr == META_KET) break; *pptrptr += 1; /* Skip META_ALT */ } if (group > 0) cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength); return grouplength; ISNOTFIXED: if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH; return -1; } /************************************************* * Find length of a parsed branch * *************************************************/ /* Return a fixed length for a branch in a lookbehind, giving an error if the length is not fixed. On entry, *pptrptr points to the first element inside the branch. On exit it is set to point to the ALT or KET. Arguments: pptrptr pointer to pointer in the parsed pattern errcodeptr pointer to error code lcptr pointer to loop counter recurses chain of recurse_check to catch mutual recursion cb pointer to compile block Returns: the length, or a negative value on error */ static int get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, parsed_recurse_check *recurses, compile_block *cb) { int branchlength = 0; int grouplength; uint32_t lastitemlength = 0; uint32_t *pptr = *pptrptr; PCRE2_SIZE offset; parsed_recurse_check this_recurse; /* A large and/or complex regex can take too long to process. This can happen more often when (?| groups are present in the pattern because their length cannot be cached. */ if ((*lcptr)++ > 2000) { *errcodeptr = ERR35; /* Lookbehind is too complicated */ return -1; } /* Scan the branch, accumulating the length. */ for (;; pptr++) { parsed_recurse_check *r; uint32_t *gptr, *gptrend; uint32_t escape; uint32_t group = 0; uint32_t itemlength = 0; if (*pptr < META_END) { itemlength = 1; } else switch (META_CODE(*pptr)) { case META_KET: case META_ALT: goto EXIT; /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the actual termination. */ case META_ACCEPT: case META_FAIL: pptr = parsed_skip(pptr, PSKIP_ALT); if (pptr == NULL) goto PARSED_SKIP_FAILED; goto EXIT; case META_MARK: case META_COMMIT_ARG: case META_PRUNE_ARG: case META_SKIP_ARG: case META_THEN_ARG: pptr += pptr[1] + 1; break; case META_CIRCUMFLEX: case META_COMMIT: case META_DOLLAR: case META_PRUNE: case META_SKIP: case META_THEN: break; case META_OPTIONS: pptr += 1; break; case META_BIGVALUE: itemlength = 1; pptr += 1; break; case META_CLASS: case META_CLASS_NOT: itemlength = 1; pptr = parsed_skip(pptr, PSKIP_CLASS); if (pptr == NULL) goto PARSED_SKIP_FAILED; break; case META_CLASS_EMPTY_NOT: case META_DOT: itemlength = 1; break; case META_CALLOUT_NUMBER: pptr += 3; break; case META_CALLOUT_STRING: pptr += 3 + SIZEOFFSET; break; /* Only some escapes consume a character. Of those, \R and \X are never allowed because they might match more than character. \C is allowed only in 32-bit and non-UTF 8/16-bit modes. */ case META_ESCAPE: escape = META_DATA(*pptr); if (escape == ESC_R || escape == ESC_X) return -1; if (escape > ESC_b && escape < ESC_Z) { #if PCRE2_CODE_UNIT_WIDTH != 32 if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C) { *errcodeptr = ERR36; return -1; } #endif itemlength = 1; if (escape == ESC_p || escape == ESC_P) pptr++; /* Skip prop data */ } break; /* Lookaheads do not contribute to the length of this branch, but they may contain lookbehinds within them whose lengths need to be set. */ case META_LOOKAHEAD: case META_LOOKAHEADNOT: case META_LOOKAHEAD_NA: *errcodeptr = check_lookbehinds(pptr + 1, &pptr, recurses, cb, lcptr); if (*errcodeptr != 0) return -1; /* Ignore any qualifiers that follow a lookahead assertion. */ switch (pptr[1]) { case META_ASTERISK: case META_ASTERISK_PLUS: case META_ASTERISK_QUERY: case META_PLUS: case META_PLUS_PLUS: case META_PLUS_QUERY: case META_QUERY: case META_QUERY_PLUS: case META_QUERY_QUERY: pptr++; break; case META_MINMAX: case META_MINMAX_PLUS: case META_MINMAX_QUERY: pptr += 3; break; default: break; } break; /* A nested lookbehind does not contribute any length to this lookbehind, but must itself be checked and have its lengths set. */ case META_LOOKBEHIND: case META_LOOKBEHINDNOT: case META_LOOKBEHIND_NA: if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb)) return -1; break; /* Back references and recursions are handled by very similar code. At this stage, the names generated in the parsing pass are available, but the main name table has not yet been created. So for the named varieties, scan the list of names in order to get the number of the first one in the pattern, and whether or not this name is duplicated. */ case META_BACKREF_BYNAME: if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0) goto ISNOTFIXED; /* Fall through */ case META_RECURSE_BYNAME: { int i; PCRE2_SPTR name; BOOL is_dupname = FALSE; named_group *ng = cb->named_groups; uint32_t meta_code = META_CODE(*pptr); uint32_t length = *(++pptr); GETPLUSOFFSET(offset, pptr); name = cb->start_pattern + offset; for (i = 0; i < cb->names_found; i++, ng++) { if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0) { group = ng->number; is_dupname = ng->isdup; break; } } if (group == 0) { *errcodeptr = ERR15; /* Non-existent subpattern */ cb->erroroffset = offset; return -1; } /* A numerical back reference can be fixed length if duplicate capturing groups are not being used. A non-duplicate named back reference can also be handled. */ if (meta_code == META_RECURSE_BYNAME || (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)) goto RECURSE_OR_BACKREF_LENGTH; /* Handle as a numbered version. */ } goto ISNOTFIXED; /* Duplicate name or number */ /* The offset values for back references < 10 are in a separate vector because otherwise they would use more than two parsed pattern elements on 64-bit systems. */ case META_BACKREF: if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 || (cb->external_flags & PCRE2_DUPCAPUSED) != 0) goto ISNOTFIXED; group = META_DATA(*pptr); if (group < 10) { offset = cb->small_ref_offset[group]; goto RECURSE_OR_BACKREF_LENGTH; } /* Fall through */ /* For groups >= 10 - picking up group twice does no harm. */ /* A true recursion implies not fixed length, but a subroutine call may be OK. Back reference "recursions" are also failed. */ case META_RECURSE: group = META_DATA(*pptr); GETPLUSOFFSET(offset, pptr); RECURSE_OR_BACKREF_LENGTH: if (group > cb->bracount) { cb->erroroffset = offset; *errcodeptr = ERR15; /* Non-existent subpattern */ return -1; } if (group == 0) goto ISNOTFIXED; /* Local recursion */ for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++) { if (META_CODE(*gptr) == META_BIGVALUE) gptr++; else if (*gptr == (META_CAPTURE | group)) break; } /* We must start the search for the end of the group at the first meta code inside the group. Otherwise it will be treated as an enclosed group. */ gptrend = parsed_skip(gptr + 1, PSKIP_KET); if (gptrend == NULL) goto PARSED_SKIP_FAILED; if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */ for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break; if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ this_recurse.prev = recurses; this_recurse.groupptr = gptr; /* We do not need to know the position of the end of the group, that is, gptr is not used after the call to get_grouplength(). Setting the second argument FALSE stops it scanning for the end when the length can be found in the cache. */ gptr++; grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group, &this_recurse, cb); if (grouplength < 0) { if (*errcodeptr == 0) goto ISNOTFIXED; return -1; /* Error already set */ } itemlength = grouplength; break; /* A (DEFINE) group is never obeyed inline and so it does not contribute to the length of this branch. Skip from the following item to the next unpaired ket. */ case META_COND_DEFINE: pptr = parsed_skip(pptr + 1, PSKIP_KET); break; /* Check other nested groups - advance past the initial data for each type and then seek a fixed length with get_grouplength(). */ case META_COND_NAME: case META_COND_NUMBER: case META_COND_RNAME: case META_COND_RNUMBER: pptr += 2 + SIZEOFFSET; goto CHECK_GROUP; case META_COND_ASSERT: pptr += 1; goto CHECK_GROUP; case META_COND_VERSION: pptr += 4; goto CHECK_GROUP; case META_CAPTURE: group = META_DATA(*pptr); /* Fall through */ case META_ATOMIC: case META_NOCAPTURE: case META_SCRIPT_RUN: pptr++; CHECK_GROUP: grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group, recurses, cb); if (grouplength < 0) return -1; itemlength = grouplength; break; /* Exact repetition is OK; variable repetition is not. A repetition of zero must subtract the length that has already been added. */ case META_MINMAX: case META_MINMAX_PLUS: case META_MINMAX_QUERY: if (pptr[1] == pptr[2]) { switch(pptr[1]) { case 0: branchlength -= lastitemlength; break; case 1: itemlength = 0; break; default: /* Check for integer overflow */ if (lastitemlength != 0 && /* Should not occur, but just in case */ INT_MAX/lastitemlength < pptr[1] - 1) { *errcodeptr = ERR87; /* Integer overflow; lookbehind too big */ return -1; } itemlength = (pptr[1] - 1) * lastitemlength; break; } pptr += 2; break; } /* Fall through */ /* Any other item means this branch does not have a fixed length. */ default: ISNOTFIXED: *errcodeptr = ERR25; /* Not fixed length */ return -1; } /* Add the item length to the branchlength, checking for integer overflow and for the branch length exceeding the limit. */ if (INT_MAX - branchlength < (int)itemlength || (branchlength += itemlength) > LOOKBEHIND_MAX) { *errcodeptr = ERR87; return -1; } /* Save this item length for use if the next item is a quantifier. */ lastitemlength = itemlength; } EXIT: *pptrptr = pptr; return branchlength; PARSED_SKIP_FAILED: *errcodeptr = ERR90; return -1; } /************************************************* * Set lengths in a lookbehind * *************************************************/ /* This function is called for each lookbehind, to set the lengths in its branches. An error occurs if any branch does not have a fixed length that is less than the maximum (65535). On exit, the pointer must be left on the final ket. The function also maintains the max_lookbehind value. Any lookbehind branch that contains a nested lookbehind may actually look further back than the length of the branch. The additional amount is passed back from get_branchlength() as an "extra" value. Arguments: pptrptr pointer to pointer in the parsed pattern errcodeptr pointer to error code lcptr pointer to loop counter recurses chain of recurse_check to catch mutual recursion cb pointer to compile block Returns: TRUE if all is well FALSE otherwise, with error code and offset set */ static BOOL set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr, parsed_recurse_check *recurses, compile_block *cb) { PCRE2_SIZE offset; int branchlength; uint32_t *bptr = *pptrptr; READPLUSOFFSET(offset, bptr); /* Offset for error messages */ *pptrptr += SIZEOFFSET; do { *pptrptr += 1; branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); if (branchlength < 0) { /* The errorcode and offset may already be set from a nested lookbehind. */ if (*errcodeptr == 0) *errcodeptr = ERR25; if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset; return FALSE; } if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength; *bptr |= branchlength; /* branchlength never more than 65535 */ bptr = *pptrptr; } while (*bptr == META_ALT); return TRUE; } /************************************************* * Check parsed pattern lookbehinds * *************************************************/ /* This function is called at the end of parsing a pattern if any lookbehinds were encountered. It scans the parsed pattern for them, calling set_lookbehind_lengths() for each one. At the start, the errorcode is zero and the error offset is marked unset. The enables the functions above not to override settings from deeper nestings. This function is called recursively from get_branchlength() for lookaheads in order to process any lookbehinds that they may contain. It stops when it hits a non-nested closing parenthesis in this case, returning a pointer to it. Arguments pptr points to where to start (start of pattern or start of lookahead) retptr if not NULL, return the ket pointer here recurses chain of recurse_check to catch mutual recursion cb points to the compile block lcptr points to loop counter Returns: 0 on success, or an errorcode (cb->erroroffset will be set) */ static int check_lookbehinds(uint32_t *pptr, uint32_t **retptr, parsed_recurse_check *recurses, compile_block *cb, int *lcptr) { int errorcode = 0; int nestlevel = 0; cb->erroroffset = PCRE2_UNSET; for (; *pptr != META_END; pptr++) { if (*pptr < META_END) continue; /* Literal */ switch (META_CODE(*pptr)) { default: return ERR70; /* Unrecognized meta code */ case META_ESCAPE: if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p) pptr += 1; break; case META_KET: if (--nestlevel < 0) { if (retptr != NULL) *retptr = pptr; return 0; } break; case META_ATOMIC: case META_CAPTURE: case META_COND_ASSERT: case META_LOOKAHEAD: case META_LOOKAHEADNOT: case META_LOOKAHEAD_NA: case META_NOCAPTURE: case META_SCRIPT_RUN: nestlevel++; break; case META_ACCEPT: case META_ALT: case META_ASTERISK: case META_ASTERISK_PLUS: case META_ASTERISK_QUERY: case META_BACKREF: case META_CIRCUMFLEX: case META_CLASS: case META_CLASS_EMPTY: case META_CLASS_EMPTY_NOT: case META_CLASS_END: case META_CLASS_NOT: case META_COMMIT: case META_DOLLAR: case META_DOT: case META_FAIL: case META_PLUS: case META_PLUS_PLUS: case META_PLUS_QUERY: case META_PRUNE: case META_QUERY: case META_QUERY_PLUS: case META_QUERY_QUERY: case META_RANGE_ESCAPED: case META_RANGE_LITERAL: case META_SKIP: case META_THEN: break; case META_RECURSE: pptr += SIZEOFFSET; break; case META_BACKREF_BYNAME: case META_RECURSE_BYNAME: pptr += 1 + SIZEOFFSET; break; case META_COND_DEFINE: pptr += SIZEOFFSET; nestlevel++; break; case META_COND_NAME: case META_COND_NUMBER: case META_COND_RNAME: case META_COND_RNUMBER: pptr += 1 + SIZEOFFSET; nestlevel++; break; case META_COND_VERSION: pptr += 3; nestlevel++; break; case META_CALLOUT_STRING: pptr += 3 + SIZEOFFSET; break; case META_BIGVALUE: case META_OPTIONS: case META_POSIX: case META_POSIX_NEG: pptr += 1; break; case META_MINMAX: case META_MINMAX_QUERY: case META_MINMAX_PLUS: pptr += 2; break; case META_CALLOUT_NUMBER: pptr += 3; break; case META_MARK: case META_COMMIT_ARG: case META_PRUNE_ARG: case META_SKIP_ARG: case META_THEN_ARG: pptr += 1 + pptr[1]; break; case META_LOOKBEHIND: case META_LOOKBEHINDNOT: case META_LOOKBEHIND_NA: if (!set_lookbehind_lengths(&pptr, &errorcode, lcptr, recurses, cb)) return errorcode; break; } } return 0; } /************************************************* * External function to compile a pattern * *************************************************/ /* This function reads a regular expression in the form of a string and returns a pointer to a block of store holding a compiled version of the expression. Arguments: pattern the regular expression patlen the length of the pattern, or PCRE2_ZERO_TERMINATED options option bits errorptr pointer to errorcode erroroffset pointer to error offset ccontext points to a compile context or is NULL Returns: pointer to compiled data block, or NULL on error, with errorcode and erroroffset set */ PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options, int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext) { BOOL utf; /* Set TRUE for UTF mode */ BOOL ucp; /* Set TRUE for UCP mode */ BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */ BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */ pcre2_real_code *re = NULL; /* What we will return */ compile_block cb; /* "Static" compile-time data */ const uint8_t *tables; /* Char tables base pointer */ PCRE2_UCHAR *code; /* Current pointer in compiled code */ PCRE2_SPTR codestart; /* Start of compiled code */ PCRE2_SPTR ptr; /* Current pointer in pattern */ uint32_t *pptr; /* Current pointer in parsed pattern */ PCRE2_SIZE length = 1; /* Allow for final END opcode */ PCRE2_SIZE usedlength; /* Actual length used */ PCRE2_SIZE re_blocksize; /* Size of memory block */ PCRE2_SIZE big32count = 0; /* 32-bit literals >= 0x80000000 */ PCRE2_SIZE parsed_size_needed; /* Needed for parsed pattern */ int32_t firstcuflags, reqcuflags; /* Type of first/req code unit */ uint32_t firstcu, reqcu; /* Value of first/req code unit */ uint32_t setflags = 0; /* NL and BSR set flags */ uint32_t skipatstart; /* When checking (*UTF) etc */ uint32_t limit_heap = UINT32_MAX; uint32_t limit_match = UINT32_MAX; /* Unset match limits */ uint32_t limit_depth = UINT32_MAX; int newline = 0; /* Unset; can be set by the pattern */ int bsr = 0; /* Unset; can be set by the pattern */ int errorcode = 0; /* Initialize to avoid compiler warn */ int regexrc; /* Return from compile */ uint32_t i; /* Local loop counter */ /* Comments at the head of this file explain about these variables. */ uint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE]; uint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE]; named_group named_groups[NAMED_GROUP_LIST_SIZE]; /* The workspace is used in different ways in the different compiling phases. It needs to be 16-bit aligned for the preliminary parsing scan. */ uint32_t c16workspace[C16_WORK_SIZE]; PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace; /* -------------- Check arguments and set up the pattern ----------------- */ /* There must be error code and offset pointers. */ if (errorptr == NULL || erroroffset == NULL) return NULL; *errorptr = ERR0; *erroroffset = 0; /* There must be a pattern! */ if (pattern == NULL) { *errorptr = ERR16; return NULL; } /* A NULL compile context means "use a default context" */ if (ccontext == NULL) ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context)); /* PCRE2_MATCH_INVALID_UTF implies UTF */ if ((options & PCRE2_MATCH_INVALID_UTF) != 0) options |= PCRE2_UTF; /* Check that all undefined public option bits are zero. */ if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 || (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0) { *errorptr = ERR17; return NULL; } if ((options & PCRE2_LITERAL) != 0 && ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 || (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0)) { *errorptr = ERR92; return NULL; } /* A zero-terminated pattern is indicated by the special length value PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */ if ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED))) patlen = PRIV(strlen)(pattern); if (patlen > ccontext->max_pattern_length) { *errorptr = ERR88; return NULL; } /* From here on, all returns from this function should end up going via the EXIT label. */ /* ------------ Initialize the "static" compile data -------------- */ tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables); cb.lcc = tables + lcc_offset; /* Individual */ cb.fcc = tables + fcc_offset; /* character */ cb.cbits = tables + cbits_offset; /* tables */ cb.ctypes = tables + ctypes_offset; cb.assert_depth = 0; cb.bracount = 0; cb.cx = ccontext; cb.dupnames = FALSE; cb.end_pattern = pattern + patlen; cb.erroroffset = 0; cb.external_flags = 0; cb.external_options = options; cb.groupinfo = stack_groupinfo; cb.had_recurse = FALSE; cb.lastcapture = 0; cb.max_lookbehind = 0; cb.name_entry_size = 0; cb.name_table = NULL; cb.named_groups = named_groups; cb.named_group_list_size = NAMED_GROUP_LIST_SIZE; cb.names_found = 0; cb.open_caps = NULL; cb.parens_depth = 0; cb.parsed_pattern = stack_parsed_pattern; cb.req_varyopt = 0; cb.start_code = cworkspace; cb.start_pattern = pattern; cb.start_workspace = cworkspace; cb.workspace_size = COMPILE_WORK_SIZE; /* Maximum back reference and backref bitmap. The bitmap records up to 31 back references to help in deciding whether (.*) can be treated as anchored or not. */ cb.top_backref = 0; cb.backref_map = 0; /* Escape sequences \1 to \9 are always back references, but as they are only two characters long, only two elements can be used in the parsed_pattern vector. The first contains the reference, and we'd like to use the second to record the offset in the pattern, so that forward references to non-existent groups can be diagnosed later with an offset. However, on 64-bit systems, PCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first occurrence of \1 to \9, indexed by the second parsed_pattern value. All other references have enough space for the offset to be put into the parsed pattern. */ for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET; /* --------------- Start looking at the pattern --------------- */ /* Unless PCRE2_LITERAL is set, check for global one-time option settings at the start of the pattern, and remember the offset to the actual regex. With valgrind support, make the terminator of a zero-terminated pattern inaccessible. This catches bugs that would otherwise only show up for non-zero-terminated patterns. */ #ifdef SUPPORT_VALGRIND if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1)); #endif ptr = pattern; skipatstart = 0; if ((options & PCRE2_LITERAL) == 0) { while (patlen - skipatstart >= 2 && ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && ptr[skipatstart+1] == CHAR_ASTERISK) { for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) { uint32_t c, pp; pso *p = pso_list + i; if (patlen - skipatstart - 2 >= p->length && PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name), p->length) == 0) { skipatstart += p->length + 2; switch(p->type) { case PSO_OPT: cb.external_options |= p->value; break; case PSO_FLG: setflags |= p->value; break; case PSO_NL: newline = p->value; setflags |= PCRE2_NL_SET; break; case PSO_BSR: bsr = p->value; setflags |= PCRE2_BSR_SET; break; case PSO_LIMM: case PSO_LIMD: case PSO_LIMH: c = 0; pp = skipatstart; if (!IS_DIGIT(ptr[pp])) { errorcode = ERR60; ptr += pp; goto HAD_EARLY_ERROR; } while (IS_DIGIT(ptr[pp])) { if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */ c = c*10 + (ptr[pp++] - CHAR_0); } if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) { errorcode = ERR60; ptr += pp; goto HAD_EARLY_ERROR; } if (p->type == PSO_LIMH) limit_heap = c; else if (p->type == PSO_LIMM) limit_match = c; else limit_depth = c; skipatstart += pp - skipatstart; break; } break; /* Out of the table scan loop */ } } if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ } } /* End of pattern-start options; advance to start of real regex. */ ptr += skipatstart; /* Can't support UTF or UCP if PCRE2 was built without Unicode support. */ #ifndef SUPPORT_UNICODE if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) { errorcode = ERR32; goto HAD_EARLY_ERROR; } #endif /* Check UTF. We have the original options in 'options', with that value as modified by (*UTF) etc in cb->external_options. The extra option PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the surrogate code points cannot be represented in UTF-16. */ utf = (cb.external_options & PCRE2_UTF) != 0; if (utf) { if ((options & PCRE2_NEVER_UTF) != 0) { errorcode = ERR74; goto HAD_EARLY_ERROR; } if ((options & PCRE2_NO_UTF_CHECK) == 0 && (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) goto HAD_ERROR; /* Offset was set by valid_utf() */ #if PCRE2_CODE_UNIT_WIDTH == 16 if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0) { errorcode = ERR91; goto HAD_EARLY_ERROR; } #endif } /* Check UCP lockout. */ ucp = (cb.external_options & PCRE2_UCP) != 0; if (ucp && (cb.external_options & PCRE2_NEVER_UCP) != 0) { errorcode = ERR75; goto HAD_EARLY_ERROR; } /* Process the BSR setting. */ if (bsr == 0) bsr = ccontext->bsr_convention; /* Process the newline setting. */ if (newline == 0) newline = ccontext->newline_convention; cb.nltype = NLTYPE_FIXED; switch(newline) { case PCRE2_NEWLINE_CR: cb.nllen = 1; cb.nl[0] = CHAR_CR; break; case PCRE2_NEWLINE_LF: cb.nllen = 1; cb.nl[0] = CHAR_NL; break; case PCRE2_NEWLINE_NUL: cb.nllen = 1; cb.nl[0] = CHAR_NUL; break; case PCRE2_NEWLINE_CRLF: cb.nllen = 2; cb.nl[0] = CHAR_CR; cb.nl[1] = CHAR_NL; break; case PCRE2_NEWLINE_ANY: cb.nltype = NLTYPE_ANY; break; case PCRE2_NEWLINE_ANYCRLF: cb.nltype = NLTYPE_ANYCRLF; break; default: errorcode = ERR56; goto HAD_EARLY_ERROR; } /* Pre-scan the pattern to do two things: (1) Discover the named groups and their numerical equivalents, so that this information is always available for the remaining processing. (2) At the same time, parse the pattern and put a processed version into the parsed_pattern vector. This has escapes interpreted and comments removed (amongst other things). In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned 32-bit ints in the parsed pattern is bounded by the length of the pattern plus one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is set. The exceptional case is when running in 32-bit, non-UTF mode, when literal characters greater than META_END (0x80000000) have to be coded as two units. In this case, therefore, we scan the pattern to check for such values. */ #if PCRE2_CODE_UNIT_WIDTH == 32 if (!utf) { PCRE2_SPTR p; for (p = ptr; p < cb.end_pattern; p++) if (*p >= META_END) big32count++; } #endif /* Ensure that the parsed pattern buffer is big enough. When PCRE2_AUTO_CALLOUT is set we have to assume a numerical callout (4 elements) for each character plus one at the end. This is overkill, but memory is plentiful these days. For many smaller patterns the vector on the stack (which was set up above) can be used. */ parsed_size_needed = patlen - skipatstart + big32count; if ((ccontext->extra_options & (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0) parsed_size_needed += 4; if ((options & PCRE2_AUTO_CALLOUT) != 0) parsed_size_needed = (parsed_size_needed + 1) * 5; if (parsed_size_needed >= PARSED_PATTERN_DEFAULT_SIZE) { uint32_t *heap_parsed_pattern = ccontext->memctl.malloc( (parsed_size_needed + 1) * sizeof(uint32_t), ccontext->memctl.memory_data); if (heap_parsed_pattern == NULL) { *errorptr = ERR21; goto EXIT; } cb.parsed_pattern = heap_parsed_pattern; } cb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed + 1; /* Do the parsing scan. */ errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb); if (errorcode != 0) goto HAD_CB_ERROR; /* Workspace is needed to remember information about numbered groups: whether a group can match an empty string and what its fixed length is. This is done to avoid the possibility of recursive references causing very long compile times when checking these features. Unnumbered groups do not have this exposure since they cannot be referenced. We use an indexed vector for this purpose. If there are sufficiently few groups, the default vector on the stack, as set up above, can be used. Otherwise we have to get/free a special vector. The vector must be initialized to zero. */ if (cb.bracount >= GROUPINFO_DEFAULT_SIZE) { cb.groupinfo = ccontext->memctl.malloc( (cb.bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data); if (cb.groupinfo == NULL) { errorcode = ERR21; cb.erroroffset = 0; goto HAD_CB_ERROR; } } memset(cb.groupinfo, 0, (cb.bracount + 1) * sizeof(uint32_t)); /* If there were any lookbehinds, scan the parsed pattern to figure out their lengths. */ if (has_lookbehind) { int loopcount = 0; errorcode = check_lookbehinds(cb.parsed_pattern, NULL, NULL, &cb, &loopcount); if (errorcode != 0) goto HAD_CB_ERROR; } /* For debugging, there is a function that shows the parsed data vector. */ #ifdef DEBUG_SHOW_PARSED fprintf(stderr, "+++ Pre-scan complete:\n"); show_parsed(&cb); #endif /* For debugging capturing information this code can be enabled. */ #ifdef DEBUG_SHOW_CAPTURES { named_group *ng = cb.named_groups; fprintf(stderr, "+++Captures: %d\n", cb.bracount); for (i = 0; i < cb.names_found; i++, ng++) { fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name); } } #endif /* Pretend to compile the pattern while actually just accumulating the amount of memory required in the 'length' variable. This behaviour is triggered by passing a non-NULL final argument to compile_regex(). We pass a block of workspace (cworkspace) for it to compile parts of the pattern into; the compiled code is discarded when it is no longer needed, so hopefully this workspace will never overflow, though there is a test for its doing so. On error, errorcode will be set non-zero, so we don't need to look at the result of the function. The initial options have been put into the cb block, but we still have to pass a separate options variable (the first argument) because the options may change as the pattern is processed. */ cb.erroroffset = patlen; /* For any subsequent errors that do not set it */ pptr = cb.parsed_pattern; code = cworkspace; *code = OP_BRA; (void)compile_regex(cb.external_options, &code, &pptr, &errorcode, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, &length); if (errorcode != 0) goto HAD_CB_ERROR; /* Offset is in cb.erroroffset */ /* This should be caught in compile_regex(), but just in case... */ if (length > MAX_PATTERN_SIZE) { errorcode = ERR20; goto HAD_CB_ERROR; } /* Compute the size of, and then get and initialize, the data block for storing the compiled pattern and names table. Integer overflow should no longer be possible because nowadays we limit the maximum value of cb.names_found and cb.name_entry_size. */ re_blocksize = sizeof(pcre2_real_code) + CU2BYTES(length + (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size); re = (pcre2_real_code *) ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data); if (re == NULL) { errorcode = ERR21; goto HAD_CB_ERROR; } /* The compiler may put padding at the end of the pcre2_real_code structure in order to round it up to a multiple of 4 or 8 bytes. This means that when a compiled pattern is copied (for example, when serialized) undefined bytes are read, and this annoys debuggers such as valgrind. To avoid this, we explicitly write to the last 8 bytes of the structure before setting the fields. */ memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8); re->memctl = ccontext->memctl; re->tables = tables; re->executable_jit = NULL; memset(re->start_bitmap, 0, 32 * sizeof(uint8_t)); re->blocksize = re_blocksize; re->magic_number = MAGIC_NUMBER; re->compile_options = options; re->overall_options = cb.external_options; re->extra_options = ccontext->extra_options; re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags; re->limit_heap = limit_heap; re->limit_match = limit_match; re->limit_depth = limit_depth; re->first_codeunit = 0; re->last_codeunit = 0; re->bsr_convention = bsr; re->newline_convention = newline; re->max_lookbehind = 0; re->minlength = 0; re->top_bracket = 0; re->top_backref = 0; re->name_entry_size = cb.name_entry_size; re->name_count = cb.names_found; /* The basic block is immediately followed by the name table, and the compiled code follows after that. */ codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_entry_size * re->name_count; /* Update the compile data block for the actual compile. The starting points of the name/number translation table and of the code are passed around in the compile data block. The start/end pattern and initial options are already set from the pre-compile phase, as is the name_entry_size field. */ cb.parens_depth = 0; cb.assert_depth = 0; cb.lastcapture = 0; cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); cb.start_code = codestart; cb.req_varyopt = 0; cb.had_accept = FALSE; cb.had_pruneorskip = FALSE; cb.open_caps = NULL; /* If any named groups were found, create the name/number table from the list created in the pre-pass. */ if (cb.names_found > 0) { named_group *ng = cb.named_groups; for (i = 0; i < cb.names_found; i++, ng++) add_name_to_table(&cb, ng->name, ng->length, ng->number, i); } /* Set up a starting, non-extracting bracket, then compile the expression. On error, errorcode will be set non-zero, so we don't need to look at the result of the function here. */ pptr = cb.parsed_pattern; code = (PCRE2_UCHAR *)codestart; *code = OP_BRA; regexrc = compile_regex(re->overall_options, &code, &pptr, &errorcode, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL); if (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY; re->top_bracket = cb.bracount; re->top_backref = cb.top_backref; re->max_lookbehind = cb.max_lookbehind; if (cb.had_accept) { reqcu = 0; /* Must disable after (*ACCEPT) */ reqcuflags = REQ_NONE; re->flags |= PCRE2_HASACCEPT; /* Disables minimum length */ } /* Fill in the final opcode and check for disastrous overflow. If no overflow, but the estimated length exceeds the really used length, adjust the value of re->blocksize, and if valgrind support is configured, mark the extra allocated memory as unaddressable, so that any out-of-bound reads can be detected. */ *code++ = OP_END; usedlength = code - codestart; if (usedlength > length) errorcode = ERR23; else { re->blocksize -= CU2BYTES(length - usedlength); #ifdef SUPPORT_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength)); #endif } /* Scan the pattern for recursion/subroutine calls and convert the group numbers into offsets. Maintain a small cache so that repeated groups containing recursions are efficiently handled. */ #define RSCAN_CACHE_SIZE 8 if (errorcode == 0 && cb.had_recurse) { PCRE2_UCHAR *rcode; PCRE2_SPTR rgroup; unsigned int ccount = 0; int start = RSCAN_CACHE_SIZE; recurse_cache rc[RSCAN_CACHE_SIZE]; for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf); rcode != NULL; rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf)) { int p, groupnumber; groupnumber = (int)GET(rcode, 1); if (groupnumber == 0) rgroup = codestart; else { PCRE2_SPTR search_from = codestart; rgroup = NULL; for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7) { if (groupnumber == rc[p].groupnumber) { rgroup = rc[p].group; break; } /* Group n+1 must always start to the right of group n, so we can save search time below when the new group number is greater than any of the previously found groups. */ if (groupnumber > rc[p].groupnumber) search_from = rc[p].group; } if (rgroup == NULL) { rgroup = PRIV(find_bracket)(search_from, utf, groupnumber); if (rgroup == NULL) { errorcode = ERR53; break; } if (--start < 0) start = RSCAN_CACHE_SIZE - 1; rc[start].groupnumber = groupnumber; rc[start].group = rgroup; if (ccount < RSCAN_CACHE_SIZE) ccount++; } } PUT(rcode, 1, rgroup - codestart); } } /* In rare debugging situations we sometimes need to look at the compiled code at this stage. */ #ifdef DEBUG_CALL_PRINTINT pcre2_printint(re, stderr, TRUE); fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); #endif /* Unless disabled, check whether any single character iterators can be auto-possessified. The function overwrites the appropriate opcode values, so the type of the pointer must be cast. NOTE: the intermediate variable "temp" is used in this code because at least one compiler gives a warning about loss of "const" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the function call. */ if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) { PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; if (PRIV(auto_possessify)(temp, &cb) != 0) errorcode = ERR80; } /* Failed to compile, or error while post-processing. */ if (errorcode != 0) goto HAD_CB_ERROR; /* Successful compile. If the anchored option was not passed, set it if we can determine that the pattern is anchored by virtue of ^ characters or \A or anything else, such as starting with non-atomic .* when DOTALL is set and there are no occurrences of *PRUNE or *SKIP (though there is an option to disable this case). */ if ((re->overall_options & PCRE2_ANCHORED) == 0 && is_anchored(codestart, 0, &cb, 0, FALSE)) re->overall_options |= PCRE2_ANCHORED; /* Set up the first code unit or startline flag, the required code unit, and then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE is set, as the data it would create will not be used. Note that a first code unit (but not the startline flag) is useful for anchored patterns because it can still give a quick "no match" and also avoid searching for a last code unit. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { int minminlength = 0; /* For minimal minlength from first/required CU */ /* If we do not have a first code unit, see if there is one that is asserted (these are not saved during the compile because they can cause conflicts with actual literals that follow). */ if (firstcuflags < 0) firstcu = find_firstassertedcu(codestart, &firstcuflags, 0); /* Save the data for a first code unit. The existence of one means the minimum length must be at least 1. */ if (firstcuflags >= 0) { re->first_codeunit = firstcu; re->flags |= PCRE2_FIRSTSET; minminlength++; /* Handle caseless first code units. */ if ((firstcuflags & REQ_CASELESS) != 0) { if (firstcu < 128 || (!utf && !ucp && firstcu < 255)) { if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS; } /* The first code unit is > 128 in UTF or UCP mode, or > 255 otherwise. In 8-bit UTF mode, codepoints in the range 128-255 are introductory code points and cannot have another case, but if UCP is set they may do. */ #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 else if (ucp && !utf && UCD_OTHERCASE(firstcu) != firstcu) re->flags |= PCRE2_FIRSTCASELESS; #else else if ((utf || ucp) && firstcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(firstcu) != firstcu) re->flags |= PCRE2_FIRSTCASELESS; #endif #endif /* SUPPORT_UNICODE */ } } /* When there is no first code unit, for non-anchored patterns, see if we can set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all branches start with ^ and also when all branches start with non-atomic .* for non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option that disables this case.) */ else if ((re->overall_options & PCRE2_ANCHORED) == 0 && is_startline(codestart, 0, &cb, 0, FALSE)) re->flags |= PCRE2_STARTLINE; /* Handle the "required code unit", if one is set. In the UTF case we can increment the minimum minimum length only if we are sure this really is a different character and not a non-starting code unit of the first character, because the minimum length count is in characters, not code units. */ if (reqcuflags >= 0) { #if PCRE2_CODE_UNIT_WIDTH == 16 if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */ firstcuflags < 0 || /* First not set */ (firstcu & 0xf800) != 0xd800 || /* First not surrogate */ (reqcu & 0xfc00) != 0xdc00) /* Req not low surrogate */ #elif PCRE2_CODE_UNIT_WIDTH == 8 if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */ firstcuflags < 0 || /* First not set */ (firstcu & 0x80) == 0 || /* First is ASCII */ (reqcu & 0x80) == 0) /* Req is ASCII */ #endif { minminlength++; } /* In the case of an anchored pattern, set up the value only if it follows a variable length item in the pattern. */ if ((re->overall_options & PCRE2_ANCHORED) == 0 || (reqcuflags & REQ_VARY) != 0) { re->last_codeunit = reqcu; re->flags |= PCRE2_LASTSET; /* Handle caseless required code units as for first code units (above). */ if ((reqcuflags & REQ_CASELESS) != 0) { if (reqcu < 128 || (!utf && !ucp && reqcu < 255)) { if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; } #ifdef SUPPORT_UNICODE #if PCRE2_CODE_UNIT_WIDTH == 8 else if (ucp && !utf && UCD_OTHERCASE(reqcu) != reqcu) re->flags |= PCRE2_LASTCASELESS; #else else if ((utf || ucp) && reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) re->flags |= PCRE2_LASTCASELESS; #endif #endif /* SUPPORT_UNICODE */ } } } /* Study the compiled pattern to set up information such as a bitmap of starting code units and a minimum matching length. */ if (PRIV(study)(re) != 0) { errorcode = ERR31; goto HAD_CB_ERROR; } /* If study() set a bitmap of starting code units, it implies a minimum length of at least one. */ if ((re->flags & PCRE2_FIRSTMAPSET) != 0 && minminlength == 0) minminlength = 1; /* If the minimum length set (or not set) by study() is less than the minimum implied by required code units, override it. */ if (re->minlength < minminlength) re->minlength = minminlength; } /* End of start-of-match optimizations. */ /* Control ends up here in all cases. When running under valgrind, make a pattern's terminating zero defined again. If memory was obtained for the parsed version of the pattern, free it before returning. Also free the list of named groups if a larger one had to be obtained, and likewise the group information vector. */ EXIT: #ifdef SUPPORT_VALGRIND if (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1)); #endif if (cb.parsed_pattern != stack_parsed_pattern) ccontext->memctl.free(cb.parsed_pattern, ccontext->memctl.memory_data); if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE) ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data); if (cb.groupinfo != stack_groupinfo) ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data); return re; /* Will be NULL after an error */ /* Errors discovered in parse_regex() set the offset value in the compile block. Errors discovered before it is called must compute it from the ptr value. After parse_regex() is called, the offset in the compile block is set to the end of the pattern, but certain errors in compile_regex() may reset it if an offset is available in the parsed pattern. */ HAD_CB_ERROR: ptr = pattern + cb.erroroffset; HAD_EARLY_ERROR: *erroroffset = ptr - pattern; HAD_ERROR: *errorptr = errorcode; pcre2_code_free(re); re = NULL; goto EXIT; } /* End of pcre2_compile.c */ vfu-4.22/vstring/pcre2/pcre2_error.c0000644000175000017500000003177214145574056015737 0ustar cadecade/************************************************* * Perl-Compatible Regular Expressions * *************************************************/ /* PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge New API code Copyright (c) 2016-2021 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pcre2_internal.h" #define STRING(a) # a #define XSTRING(s) STRING(s) /* The texts of compile-time error messages. Compile-time error numbers start at COMPILE_ERROR_BASE (100). This used to be a table of strings, but in order to reduce the number of relocations needed when a shared library is loaded dynamically, it is now one long string. We cannot use a table of offsets, because the lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, pcre2_get_error_message() counts through to the one it wants - this isn't a performance issue because these strings are used only when there is an error. Each substring ends with \0 to insert a null character. This includes the final substring, so that the whole string ends with \0\0, which can be detected when counting through. */ static const unsigned char compile_error_texts[] = "no error\0" "\\ at end of pattern\0" "\\c at end of pattern\0" "unrecognized character follows \\\0" "numbers out of order in {} quantifier\0" /* 5 */ "number too big in {} quantifier\0" "missing terminating ] for character class\0" "escape sequence is invalid in character class\0" "range out of order in character class\0" "quantifier does not follow a repeatable item\0" /* 10 */ "internal error: unexpected repeat\0" "unrecognized character after (? or (?-\0" "POSIX named classes are supported only within a class\0" "POSIX collating elements are not supported\0" "missing closing parenthesis\0" /* 15 */ "reference to non-existent subpattern\0" "pattern passed as NULL\0" "unrecognised compile-time option bit(s)\0" "missing ) after (?# comment\0" "parentheses are too deeply nested\0" /* 20 */ "regular expression is too large\0" "failed to allocate heap memory\0" "unmatched closing parenthesis\0" "internal error: code overflow\0" "missing closing parenthesis for condition\0" /* 25 */ "lookbehind assertion is not fixed length\0" "a relative value of zero is not allowed\0" "conditional subpattern contains more than two branches\0" "assertion expected after (?( or (?(?C)\0" "digit expected after (?+ or (?-\0" /* 30 */ "unknown POSIX class name\0" "internal error in pcre2_study(): should not occur\0" "this version of PCRE2 does not have Unicode support\0" "parentheses are too deeply nested (stack check)\0" "character code point value in \\x{} or \\o{} is too large\0" /* 35 */ "lookbehind is too complicated\0" "\\C is not allowed in a lookbehind assertion in UTF-" XSTRING(PCRE2_CODE_UNIT_WIDTH) " mode\0" "PCRE2 does not support \\F, \\L, \\l, \\N{name}, \\U, or \\u\0" "number after (?C is greater than 255\0" "closing parenthesis for (?C expected\0" /* 40 */ "invalid escape sequence in (*VERB) name\0" "unrecognized character after (?P\0" "syntax error in subpattern name (missing terminator?)\0" "two named subpatterns have the same name (PCRE2_DUPNAMES not set)\0" "subpattern name must start with a non-digit\0" /* 45 */ "this version of PCRE2 does not have support for \\P, \\p, or \\X\0" "malformed \\P or \\p sequence\0" "unknown property name after \\P or \\p\0" "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " code units)\0" "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0" /* 50 */ "invalid range in character class\0" "octal value is greater than \\377 in 8-bit non-UTF-8 mode\0" "internal error: overran compiling workspace\0" "internal error: previously-checked referenced subpattern not found\0" "DEFINE subpattern contains more than one branch\0" /* 55 */ "missing opening brace after \\o\0" "internal error: unknown newline setting\0" "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0" "(?R (recursive pattern call) must be followed by a closing parenthesis\0" /* "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" */ "obsolete error (should not occur)\0" /* Was the above */ /* 60 */ "(*VERB) not recognized or malformed\0" "subpattern number is too big\0" "subpattern name expected\0" "internal error: parsed pattern overflow\0" "non-octal character in \\o{} (closing brace missing?)\0" /* 65 */ "different names for subpatterns of the same number are not allowed\0" "(*MARK) must have an argument\0" "non-hex character in \\x{} (closing brace missing?)\0" #ifndef EBCDIC "\\c must be followed by a printable ASCII character\0" #else "\\c must be followed by a letter or one of [\\]^_?\0" #endif "\\k is not followed by a braced, angle-bracketed, or quoted name\0" /* 70 */ "internal error: unknown meta code in check_lookbehinds()\0" "\\N is not supported in a class\0" "callout string is too long\0" "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" "using UTF is disabled by the application\0" /* 75 */ "using UCP is disabled by the application\0" "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0" "character code point value in \\u.... sequence is too large\0" "digits missing in \\x{} or \\o{} or \\N{U+}\0" "syntax error or number too big in (?(VERSION condition\0" /* 80 */ "internal error: unknown opcode in auto_possessify()\0" "missing terminating delimiter for callout with string argument\0" "unrecognized string delimiter follows (?C\0" "using \\C is disabled by the application\0" "(?| and/or (?J: or (?x: parentheses are too deeply nested\0" /* 85 */ "using \\C is disabled in this PCRE2 library\0" "regular expression is too complicated\0" "lookbehind assertion is too long\0" "pattern string is longer than the limit set by the application\0" "internal error: unknown code in parsed pattern\0" /* 90 */ "internal error: bad code value in parsed_skip()\0" "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0" "invalid option bits with PCRE2_LITERAL\0" "\\N{U+dddd} is supported only in Unicode (UTF) mode\0" "invalid hyphen in option setting\0" /* 95 */ "(*alpha_assertion) not recognized\0" "script runs require Unicode support, which this version of PCRE2 does not have\0" "too many capturing groups (maximum 65535)\0" "atomic assertion expected after (?( or (?(?C)\0" "\\K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)\0" ; /* Match-time and UTF error texts are in the same format. */ static const unsigned char match_error_texts[] = "no error\0" "no match\0" "partial match\0" "UTF-8 error: 1 byte missing at end\0" "UTF-8 error: 2 bytes missing at end\0" /* 5 */ "UTF-8 error: 3 bytes missing at end\0" "UTF-8 error: 4 bytes missing at end\0" "UTF-8 error: 5 bytes missing at end\0" "UTF-8 error: byte 2 top bits not 0x80\0" "UTF-8 error: byte 3 top bits not 0x80\0" /* 10 */ "UTF-8 error: byte 4 top bits not 0x80\0" "UTF-8 error: byte 5 top bits not 0x80\0" "UTF-8 error: byte 6 top bits not 0x80\0" "UTF-8 error: 5-byte character is not allowed (RFC 3629)\0" "UTF-8 error: 6-byte character is not allowed (RFC 3629)\0" /* 15 */ "UTF-8 error: code points greater than 0x10ffff are not defined\0" "UTF-8 error: code points 0xd800-0xdfff are not defined\0" "UTF-8 error: overlong 2-byte sequence\0" "UTF-8 error: overlong 3-byte sequence\0" "UTF-8 error: overlong 4-byte sequence\0" /* 20 */ "UTF-8 error: overlong 5-byte sequence\0" "UTF-8 error: overlong 6-byte sequence\0" "UTF-8 error: isolated byte with 0x80 bit set\0" "UTF-8 error: illegal byte (0xfe or 0xff)\0" "UTF-16 error: missing low surrogate at end\0" /* 25 */ "UTF-16 error: invalid low surrogate\0" "UTF-16 error: isolated low surrogate\0" "UTF-32 error: code points 0xd800-0xdfff are not defined\0" "UTF-32 error: code points greater than 0x10ffff are not defined\0" "bad data value\0" /* 30 */ "patterns do not all use the same character tables\0" "magic number missing\0" "pattern compiled in wrong mode: 8/16/32-bit error\0" "bad offset value\0" "bad option value\0" /* 35 */ "invalid replacement string\0" "bad offset into UTF string\0" "callout error code\0" /* Never returned by PCRE2 itself */ "invalid data in workspace for DFA restart\0" "too much recursion for DFA matching\0" /* 40 */ "backreference condition or recursion test is not supported for DFA matching\0" "function is not supported for DFA matching\0" "pattern contains an item that is not supported for DFA matching\0" "workspace size exceeded in DFA matching\0" "internal error - pattern overwritten?\0" /* 45 */ "bad JIT option\0" "JIT stack limit reached\0" "match limit exceeded\0" "no more memory\0" "unknown substring\0" /* 50 */ "non-unique substring name\0" "NULL argument passed\0" "nested recursion at the same subject position\0" "matching depth limit exceeded\0" "requested value is not available\0" /* 55 */ "requested value is not set\0" "offset limit set without PCRE2_USE_OFFSET_LIMIT\0" "bad escape sequence in replacement string\0" "expected closing curly bracket in replacement string\0" "bad substitution in replacement string\0" /* 60 */ "match with end before start or start moved backwards is not supported\0" "too many replacements (more than INT_MAX)\0" "bad serialized data\0" "heap limit exceeded\0" "invalid syntax\0" /* 65 */ "internal error - duplicate substitution match\0" "PCRE2_MATCH_INVALID_UTF is not supported for DFA matching\0" ; /************************************************* * Return error message * *************************************************/ /* This function copies an error message into a buffer whose units are of an appropriate width. Error numbers are positive for compile-time errors, and negative for match-time errors (except for UTF errors), but the numbers are all distinct. Arguments: enumber error number buffer where to put the message (zero terminated) size size of the buffer in code units Returns: length of message if all is well negative on error */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size) { const unsigned char *message; PCRE2_SIZE i; int n; if (size == 0) return PCRE2_ERROR_NOMEMORY; if (enumber >= COMPILE_ERROR_BASE) /* Compile error */ { message = compile_error_texts; n = enumber - COMPILE_ERROR_BASE; } else if (enumber < 0) /* Match or UTF error */ { message = match_error_texts; n = -enumber; } else /* Invalid error number */ { message = (unsigned char *)"\0"; /* Empty message list */ n = 1; } for (; n > 0; n--) { while (*message++ != CHAR_NUL) {}; if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA; } for (i = 0; *message != 0; i++) { if (i >= size - 1) { buffer[i] = 0; /* Terminate partial message */ return PCRE2_ERROR_NOMEMORY; } buffer[i] = *message++; } buffer[i] = 0; return (int)i; } /* End of pcre2_error.c */ vfu-4.22/vstring/COPYING0000644000175000017500000004307014145574056013361 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-4.22/vstring/vstring.cpp0000644000175000017500000012605014145574056014526 0ustar cadecade/**************************************************************************** * * VSTRING Library * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * * http://cade.datamax.bg/ * Distributed under the GPL license, you should receive copy of GPL! * * 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* type, so there is no need to change function calls * nor the implementation when you change from char* to VString (and * vice versa). The main difference from other similar libs is that * the dynamic VString class has no visible methods (except operators) * so you will use it as a plain char* 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 string, which resizes automatically * * VSTRLIB part (vstrlib.h and vstrlib.cpp) provides string data * structures which mimic Perl's. There are several classes: * * VArray -- array of VString elements * VTrie -- associative array (hash) of VString elements * VRegexp -- regular expression helper class * * VString, VArray, VTrie use shallow copy and copy-on-write functionality, * so things like str1 = str2, varray1 = varray2 etc. are cheap and fast :) * ***************************************************************************/ #include #include #include #include #include #include #include "vstring.h" #ifndef _HAVE_ITOA_ #define itoa(n,s,r) sprintf(s,"%d",n) #endif /**************************************************************************** ** ** VSTRING BOX ** ****************************************************************************/ VStringBox* VStringBox::clone() { VStringBox* box = new VStringBox(); box->resize_buf( size ); box->sl = sl; box->compact = compact; memcpy( box->s, s, size ); return box; } void VStringBox::resize_buf( int new_size ) { /* FIXME: this breaks a lot of things: ==, strcmp, const 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 / STR_BLOCK_SIZE + ( new_size % STR_BLOCK_SIZE != 0 ); new_size *= STR_BLOCK_SIZE; } if( s == NULL ) { /* first time alloc */ s = (char*)malloc( new_size ); ASSERT( s ); s[ 0 ] = 0; size = new_size; sl = 0; } else if ( size != new_size ) { /* expand/shrink */ s = (char*)realloc( s, new_size ); size = new_size; s[ size - 1 ] = 0; if ( sl > size - 1 ) sl = size - 1; } } /**************************************************************************** ** ** VSTRING ** ****************************************************************************/ void VString::detach() { if ( box->refs() == 1 ) return; VStringBox *new_box = box->clone(); box->unref(); box = new_box; } void VString::i( const int n ) { char tmp[64]; itoa( n, tmp, 10 ); set( tmp ); } void VString::l( const long n ) { char tmp[64]; sprintf( tmp, "%ld", n ); set( tmp ); } void VString::f( const double d ) { char tmp[64]; sprintf( tmp, "%.10f", d ); int z = strlen( tmp ); while( tmp[z-1] == '0' ) z--; if ( tmp[z-1] == '.' ) z--; tmp[z] = 0; set( tmp ); } void VString::fi( const double d ) // sets double as int (w/o frac) { char tmp[64]; sprintf( tmp, "%.0f", d ); set( tmp ); } void VString::set( const char* ps ) { if (ps == NULL || ps[0] == 0) { resize( 0 ); ASSERT( box->s ); box->sl = 0; box->s[ 0 ] = 0; } else { int sl = strlen(ps); resize( sl ); memcpy( box->s, ps, sl ); box->sl = sl; box->s[sl] = 0; } } void VString::cat( const char* ps ) { if (ps == NULL) return; if (ps[0] == 0) return; int psl = strlen( ps ); resize( box->sl + psl ); memcpy( box->s + box->sl, ps, psl ); box->s[ box->sl + psl ] = 0; box->sl += psl; } void VString::setn( const char* ps, int len ) { if ( !ps || len < 1 ) { resize( 0 ); box->sl = 0; box->s[ 0 ] = 0; return; } int z = strlen( ps ); if ( len < z ) z = len; resize( z ); box->sl = z; memcpy( box->s, ps, z ); box->s[z] = 0; } void VString::catn( const char* ps, int len ) { if ( !ps || len < 1 ) return; int z = strlen( ps ); if ( len < z ) z = len; resize( box->sl + z ); memcpy( box->s + box->sl, ps, z ); box->sl += z; box->s[ box->sl ] = 0; } /**************************************************************************** ** ** VString Functions (for class VString) ** ****************************************************************************/ VString &str_mul( VString &target, int n ) // multiplies the VString n times, i.e. "1"*5 = "11111" { target.resize( target.box->sl * n ); str_mul( target.box->s, n ); target.fix(); return target; } VString &str_del( VString &target, int pos, int len ) // deletes `len' chars starting from `pos' { if ( pos > target.box->sl || pos < 0 ) return target; target.detach(); str_del( target.box->s, pos, len ); target.fix(); return target; } VString &str_ins( VString &target, int pos, const char* s ) // inserts `s' in position `pos' { if ( pos > target.box->sl || pos < 0 ) return target; target.resize( target.box->sl + strlen(s) ); str_ins( target.box->s, pos, s ); target.fixlen(); return target; } VString &str_ins_ch( VString &target, int pos, 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.fixlen(); return target; } VString &str_replace( VString &target, const char* out, const char* in ) // replace `out' w. `in' { int outl = strlen( out ); int inl = strlen( 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; } VString &str_copy( VString &target, const char* source, int pos, int len ) // returns `len' chars from `pos' { //FIXME: too many strlen()'s... if ( pos < 0 ) { pos = str_len( source ) + pos; if ( pos < 0 ) pos = 0; } if ( len == -1 ) len = str_len( source ) - pos; if ( len < 1 ) { target = ""; return target; } target.resize( len ); str_copy( target.box->s, source, pos, len ); target.fix(); ASSERT( target.check() ); return target; } VString &str_left( VString &target, const char* source, int len ) // returns `len' chars from the left { return str_copy( target, source, 0, len ); } VString &str_right( VString &target, const char* source, int len ) // returns `len' chars from the right { return str_copy( target, source, strlen( source ) - len, len ); } VString &str_sleft( VString &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; } VString &str_sright( VString &target, int len ) // SelfRight -- just as 'Right' but works on `this' { target.detach(); str_sright( target.box->s, len ); target.fix(); return target; } VString &str_trim_left( VString &target, int len ) // trims `len' chars from the beginning (left) { target.detach(); str_trim_left( target.box->s, len ); target.fix(); return target; } VString &str_trim_right( VString &target, int len ) // trim `len' chars from the end (right) { target.detach(); str_trim_right( target.box->s, len ); target.fix(); return target; } VString &str_cut_left( VString &target, const char* charlist ) // remove all chars `charlist' from the beginning (i.e. from the left) { target.detach(); str_cut_left( target.box->s, charlist ); target.fix(); return target; } VString &str_cut_right( VString &target, const char* charlist ) // remove all chars `charlist' from the end (i.e. from the right) { target.detach(); str_cut_right( target.box->s, charlist ); target.fix(); return target; } VString &str_cut( VString &target, const 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; } VString &str_cut_spc( VString &target ) // does `Cut(" ");' { return str_cut( target, " " ); } VString &str_pad( VString &target, int len, char ch ) { target.resize( (len > 0) ? len : -len ); str_pad( target.box->s, len, ch ); target.fixlen(); return target; } VString &str_comma( VString &target, 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( VString &target, int pos, const char ch ) // sets `ch' 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; } char str_get_ch( VString &target, int pos ) // return 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( VString &target, const char ch ) // adds `ch' at the end { int sl = target.box->sl; target.resize( sl+1 ); target.box->s[sl] = ch; target.box->s[sl+1] = 0; target.fix(); } char* str_word( VString &target, const char* delimiters, char* result ) { target.detach(); str_word( target.box->s, delimiters, result ); target.fix(); return result[0] ? result : NULL; } char* str_rword( VString &target, const char* delimiters, char* result ) { target.detach(); str_rword( target.box->s, delimiters, result ); target.fix(); return result; } int sprintf( int init_size, VString &target, const char *format, ... ) { char *tmp = new char[init_size]; va_list vlist; va_start( vlist, format ); int res = vsnprintf( tmp, init_size, format, vlist ); va_end( vlist ); target = tmp; delete [] tmp; return res; } int sprintf( VString &target, const char *format, ... ) { #define VSPRINTF_BUF_SIZE 1024 char tmp[VSPRINTF_BUF_SIZE]; va_list vlist; va_start( vlist, format ); int res = vsnprintf( tmp, VSPRINTF_BUF_SIZE, format, vlist ); va_end( vlist ); target = tmp; return res; } VString& str_tr ( VString& target, const char *from, const char *to ) { target.detach(); str_tr( target.box->s, from, to ); return target; } VString& str_up ( VString& target ) { target.detach(); str_up( target.box->s ); return target; } VString& str_low( VString& target ) { target.detach(); str_low( target.box->s ); return target; } VString& str_flip_case( VString& target ) { target.detach(); str_flip_case( target.box->s ); return target; } VString& str_reverse( VString& target ) { target.detach(); str_reverse( target.box->s ); return target; } VString &str_squeeze( VString &target, const char* sq_chars ) // squeeze repeating chars to one only { target.detach(); str_squeeze( target.box->s, sq_chars ); target.fix(); return target; } /**************************************************************************** ** ** VString Functions (for char*) ** ****************************************************************************/ char* str_mul( 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 = strlen( target ); int z = (n - 1) * sl; while( z > 0 ) { memcpy( target + z, target, sl ); z -= sl; } target[sl*n] = 0; return target; } int str_find( const char* target, int c, int startpos ) // returns first zero-based position of char, or -1 if not found { if (startpos < 0) return -1; int sl = strlen( target ); if (startpos >= sl) return -1; const char* pc = strchr( target + startpos, c ); if( ! pc ) return -1; return pc - target; } int str_rfind( const char* target, int c ) // returns last zero-based position of char, or -1 if not found { const char* pc = strrchr( target, c ); if( ! pc ) return -1; return pc - target; } int str_find( const char* target, const char* s, int startpos ) // returns first zero-based position of VString, or -1 if not found { if (startpos < 0) return -1; int sl = strlen( target ); if (startpos >= sl) return -1; const char* pc = strstr( target + startpos, s ); if( ! pc ) return -1; return pc - target; } int str_rfind( const char* target, const char* s ) // returns last zero-based position of VString, or -1 if not found { int sl = strlen( target ); int sls = strlen( s ); int z = sl - sls; while ( z > 0 ) { if ( strncmp( target + z, s, sls ) == 0 ) return z; z--; } return -1; } char* str_del( char* target, int pos, int len ) // deletes `len' chars starting from `pos' { int sl = strlen( target ); if ( pos > sl || pos < 0 ) return target; int z = sl - pos - len; if ( pos + len < sl ) memmove( target + pos, target + pos + len, z + 1 ); else target[pos] = 0; return target; } char* str_ins( char* target, int pos, const char* s ) // inserts `s' in position `pos' { int sl = strlen( target ); if ( pos > sl || pos < 0 ) return target; int sls = strlen( s ); if ( sls < 1 ) return target; memmove( target + pos + sls, target + pos, sl - pos + 1 ); memmove( target + pos, s, sls ); return target; } char* str_ins_ch( char* target, int pos, char ch ) // inserts `ch' in position `pos' { char tmp[2]; tmp[0] = ch; tmp[1] = 0; str_ins( target, pos, tmp ); return target; } char* str_replace( char* target, const char* out, const char* in ) // replace `out' w. `in' { int outl = strlen( out ); int inl = strlen( 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; } int str_overlap( const char* target, const char* source, int len ) // check if source and target overlap, returns 1 if they do { int sl = len; if( sl == -1 ) sl = strlen( source ); if ( target >= source && target <= source + sl ) return 1; if ( target + sl >= source && target + sl <= source + sl ) return 1; return 0; } char* str_copy( char* target, const char* source, int pos, int len ) // returns `len' chars from `pos' { ASSERT( len >= -1 ); target[0] = 0; int sl = str_len( source ); if ( pos < 0 ) { pos = sl + pos; if ( pos < 0 ) pos = 0; } if ( pos < 0 || pos >= sl ) return target; if ( len == -1 ) len = sl - pos; // untill the end of the string (default arg) if ( len < 1 ) return target; if ( pos + len >= sl ) len = sl - pos; if( str_overlap( target, source + pos, len ) ) memmove( target, source + pos, len ); else memcpy( target, source + pos, len ); target[ len ] = 0; return target; } char* str_left( char* target, const char* source, int len ) // returns `len' chars from the left { return str_copy( target, source, 0, len ); } char* str_right( char* target, const char* source, int len ) // returns `len' chars from the right { return str_copy( target, source, strlen( source ) - len, len ); } char* str_sleft( char* target, int len ) // SelfLeft -- just as 'Left' but works on `this' { if ( (size_t)len < strlen(target) && len >= 0 ) target[len] = 0; return target; } char* str_sright( char* target, int len ) // SelfRight -- just as 'Right' but works on `this' { int sl = strlen( target ); if( len < sl ) { memmove( target, target + ( sl - len ), len + 1 ); target[len] = 0; } return target; } char* str_trim_left( char* target, int len ) // trims `len' chars from the beginning (left) { int sl = strlen( target ); int sr = sl - len; // result string length (without trailing 0) if( sr >= sl ) return target; if( sr > 0 ) { memmove( target, target + len, sr + 1 ); target[sr] = 0; } else target[0] = 0; return target; } char* str_trim_right( char* target, int len ) // trim `len' chars from the end (right) { int sl = strlen( 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( char* target, int pos, const char ch ) // sets `ch' char at position `pos' { int sl = str_len( target ); if ( pos < 0 ) pos = sl + pos; if ( pos < 0 || pos >= sl ) return; target[pos] = ch; } char str_get_ch( char* target, int pos ) // return 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( char* target, const char ch ) // adds `ch' at the end { int sl = strlen( target ); target[sl] = ch; target[sl+1] = 0; } // return first `word', i.e. from pos 0 to first found delimiter char // after that deletes this `word' from the target char* str_word( char* target, const char* delimiters, char* result ) { int z = 0; int sl = strlen( target ); while ((strchr(delimiters, target[z]) == NULL) && (target[z] != 0)) z++; memmove(result, target, z); result[z] = 0; if ( z > 0 ) { if( z < sl ) memmove( target, target + z + 1, sl - z ); // including trailing zero else target[0] = 0; } return result[0] ? result : NULL; } // ...same but `last' word char* str_rword( char* target, const char* delimiters, char* result ) { result[0] = 0; int sl = strlen( target ); int z = sl - 1; while ( strchr( delimiters, target[z] ) == NULL && z > 0 ) z--; if (z < 0) return NULL; memmove( result, target + z + 1, sl - z ); target[z] = 0; return result; } char* str_cut_left( char* target, const char* charlist ) // remove all chars `charlist' from the beginning (i.e. from the left) { int sl = strlen(target); if (sl == 0) return target; int z = 0; while ((strchr(charlist, target[z]) != NULL) && (target[z] != 0)) z++; if (z == 0) return target; memmove( target, target + z, sl - z + 1 ); return target; } char* str_cut_right( char* target, const char* charlist ) // remove all chars `charlist' from the end (i.e. from the right) { if (strlen(target) == 0) return target; int z = strlen(target) - 1; while ((strchr(charlist, target[z]) != NULL) && (z > 0)) z--; target[z+1] = 0; return target; } char* str_cut( char* target, const char* charlist ) // does `CutR(charlist);CutL(charlist);' { str_cut_left( target, charlist ); str_cut_right( target, charlist ); return target; } char* str_cut_spc( char* target ) // does `Cut(" ");' { str_cut_left( target, " " ); str_cut_right( target, " " ); return target; } // expand string to width 'len' filling with char 'ch' // if len > 0 target will be padded right, else left char* str_pad( char* target, int len, char ch ) { int sl = strlen( target ); int _len; _len = (len >= 0) ? len : - len; if ( _len <= sl ) return target; char *tmp = new char[_len + 1]; // result buffer -- need len + 1 tmp[0] = ch; tmp[1] = 0; str_mul( tmp, _len - sl ); if ( len < 0 ) { strcat( target, tmp ); } else { strcat( tmp, target ); strcpy( target, tmp ); } delete [] tmp; return target; } // insert `commas' for 1000's delimiter or use another delimiter // VString supposed to be a integer or real w/o `e' format char* str_comma( char* target, char delim ) { int dot = str_rfind( target, '.' ); if (dot == -1) dot = strlen( target ); dot -= 3; while( dot > 0 ) { str_ins_ch( target, dot , delim ); dot -= 3; } return target; } // translate chars from `from' to `to' // length of `from' MUST be equal to length of `to' char* str_tr( char* target, const char *from, const char *to ) { ASSERT(strlen( from ) == strlen( to )); if (strlen( from ) != strlen( to )) return target; int z = 0; int sl = strlen( target ); for( z = 0; z < sl; z++ ) { const char *pc = strchr( from, target[z] ); if (pc) target[z] = to[ pc - from ]; } return target; } char* str_up( char* target ) { int sl = strlen( target ); for( int z = 0; z < sl; z++ ) target[z] = toupper( target[z] ); return target; } char* str_low( char* target ) { int sl = strlen( target ); for( int z = 0; z < sl; z++ ) target[z] = tolower( target[z] ); return target; } char* str_flip_case( char* target ) // CUTE nali? :) // vladi { int sl = strlen( 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; } char* str_reverse( char* target ) // reverse the VString: `abcde' becomes `edcba' :) { int z = 0; int x = strlen(target)-1; while( z < x ) { char ch = target[ z ]; target[ z++ ] = target[ x ]; target[ x-- ] = ch; } return target; } char* str_squeeze( char* target, const char* sq_chars ) // squeeze repeating chars to one only { if ( ! target ) return NULL; if ( ! sq_chars ) return NULL; int rc = -1; int pos = 0; while( target[pos] ) { if ( rc == -1 && strchr( sq_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; } /**************************************************************************** ** ** VString Functions (for const char*) ** ****************************************************************************/ VString str_up ( const char* src ) { VString ret = src; return str_up( ret ); } VString str_low( const char* src ) { VString ret = src; return str_low( ret ); } VString str_flip_case( const char* src ) { VString ret = src; return str_flip_case( ret ); } /**************************************************************************** ** ** VString Functions -- common (VString class will pass transparently) ** ****************************************************************************/ int str_count( const char* target, const char* charlist, int startpos ) // returns match count of all chars from `charlist' { if (!target) return 0; int sl = strlen( target ); if ( startpos >= sl || startpos < 0 ) return 0; int z; int cnt = 0; for ( z = startpos; z < sl; z++ ) cnt += ( strchr( charlist, target[z]) != NULL ); return cnt; } int str_str_count( const char* target, const char* s, int startpos ) // returns match count of `s' VString into target { if (!target) return 0; int cnt = 0; int sl = strlen( s ); if ( startpos >= sl || startpos < 0 ) return 0; const char* pc = target + startpos; while( (pc = strstr( pc, s )) ) { pc += sl; cnt++; } return cnt; } int str_is_int( const char* target ) // check if VString is correct int value { if (!target) return 0; char *tmp = strdup( target ); str_cut_spc( tmp ); int dc = str_count( tmp, "0123456789" ); int sl = strlen( tmp ); free( tmp ); return ( dc == sl ); } int str_is_double( const char* target ) // check if VString is correct double (w/o `e' format :( ) { if (!target) return 0; char *tmp = strdup( target ); str_cut_spc( tmp ); int dc = str_count( tmp, "0123456789" ); int cc = str_count( tmp, "." ); int sl = strlen( tmp ); free( tmp ); return ( (dc + cc == sl) && ( cc == 1 ) ); } /*************************************************************************** ** ** VARRAYBOX ** ****************************************************************************/ VArrayBox* VArrayBox::clone() { VArrayBox *new_box = new VArrayBox(); new_box->resize( _size ); new_box->_count = _count; int i; for( i = 0; i < _count; i++ ) { new_box->_data[i] = new VString; *new_box->_data[i] = *_data[i]; } return new_box; } void VArrayBox::resize( int new_size ) { ASSERT( new_size >= 0 ); 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 / VARRAY_BLOCK_SIZE + (new_size % VARRAY_BLOCK_SIZE != 0); new_size *= VARRAY_BLOCK_SIZE; if ( new_size == _size ) return; VString** new_data = new VString*[ new_size ]; ASSERT( new_data ); memset( new_data, 0, new_size * sizeof(VString*) ); if ( _data ) { memcpy( new_data, _data, (_size < new_size ? _size : new_size) * sizeof(VString*) ); delete [] _data; } _size = new_size; _data = new_data; } /*************************************************************************** ** ** VARRAY ** ****************************************************************************/ VArray::VArray() { box = new VArrayBox(); compact = 1; } VArray::VArray( const VArray& arr ) { box = arr.box; box->ref(); compact = 1; } VArray::VArray( const VTrie& tr ) { box = new VArrayBox(); compact = 1; *this = tr; } VArray::~VArray() { box->unref(); } void VArray::detach() { if ( box->refs() == 1 ) return; VArrayBox *new_box = box->clone(); box->unref(); box = new_box; } void VArray::ins( int n, const char* s ) { detach(); ASSERT( n >= 0 && n <= box->_count ); if ( box->_count == box->_size ) box->resize( box->_size + 1 ); memmove( &box->_data[0] + n + 1, &box->_data[0] + n, ( box->_count - n ) * sizeof(VString*) ); box->_count++; box->_data[n] = new VString; box->_data[n]->compact( compact ); box->_data[n]->set( s ); } void VArray::del( int n ) { detach(); if ( n < 0 || n >= box->_count ) return; delete box->_data[n]; memmove( &box->_data[0] + n, &box->_data[0] + n + 1, ( box->_count - n ) * sizeof(VString*) ); box->_count--; if ( box->_size - box->_count > VARRAY_BLOCK_SIZE ) box->resize( box->_count ); } void VArray::set( int n, const char* s ) { detach(); ASSERT( n >= 0 ); if ( n >= box->_count ) { int i = n - box->_count + 1; while ( i-- ) push( "" ); } ASSERT( n < box->_count ); box->_data[n]->set( s ); } const char* VArray::get( int n ) { if ( n < 0 || n >= box->_count ) return NULL; else return box->_data[n]->data(); } int VArray::push( const char* s ) { ins( box->_count, s ); return box->_count; } int VArray::push( VTrie *tr ) { tr->keys_and_values( this, this ); return box->_count; } int VArray::push( VArray *arr ) { ASSERT( arr != this ); int cnt = arr->count(); for( int z = 0; z < cnt; z++ ) push( arr->get( z ) ); return box->_count; } const char* VArray::pop() { if ( box->_count == 0 ) return NULL; _ret_str = get( box->_count - 1 ); del( box->_count - 1 ); return _ret_str.data(); } int VArray::unshift( const char* s ) { ins( 0, s ); return box->_count; } int VArray::unshift( VTrie *tr ) { VArray tmp_arr; tr->keys_and_values( &tmp_arr, &tmp_arr ); unshift( &tmp_arr ); return box->_count; } int VArray::unshift( VArray *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 char* VArray::shift() { if ( box->_count == 0 ) return NULL; _ret_str = get( 0 ); del( 0 ); return _ret_str.data(); } int VArray::fload( const char* fname ) { undef(); FILE* f = fopen( fname, "rt" ); if (!f) return 1; int r = fload( f ); fclose(f); return r; } int VArray::fsave( const char* fname ) { FILE* f = fopen( fname, "wt" ); if (!f) return 1; int r = fsave( f ); fclose(f); return r; } int VArray::fload( FILE* f ) { undef(); char buf[1024]; VString str; while( fgets( buf, sizeof(buf)-1, f ) ) { str += buf; if ( str_get_ch( str, -1 ) != '\n' && !feof(f) ) continue; while ( str_get_ch( str, -1 ) == '\n' ) str_trim_right( str, 1 ); push( str ); str = ""; } return 0; } int VArray::fsave( FILE* f ) { for( int z = 0; z < box->_count; z++ ) { size_t len = strlen( get(z) ); if ( fwrite( get(z), 1, len, f ) != len ) return 2; if ( fwrite( "\n", 1, 1, f ) != 1 ) return 2; } return 0; } void VArray::sort( int rev, int (*q_strcmp)(const char *, const char *) ) { if ( count() > 1 ) q_sort( 0, count() - 1, q_strcmp ? q_strcmp : strcmp ); if ( rev ) // FIXME: not optimal... reverse(); } void VArray::q_sort( int lo, int hi, int (*q_strcmp)(const char *, const char *) ) { int m, l, r; const 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 ) { VString *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 VArray::reverse() { int m = box->_count / 2; for( int z = 0; z < m; z++ ) { VString *t; t = box->_data[z]; box->_data[z] = box->_data[box->_count-1-z]; box->_data[box->_count-1-z] = t; } } void VArray::shuffle() /* Fisher-Yates shuffle */ { int i = box->_count - 1; while( i >= 0 ) { int j = rand() % ( i + 1 ); VString *t; t = box->_data[i]; box->_data[i] = box->_data[j]; box->_data[j] = t; i--; } } void VArray::print() { int z; for( z = 0; z < count(); z++ ) printf( "%d=%s\n", z, get(z) ); } int VArray::max_len() { if ( count() == 0 ) return 0; int l = 0; int z; for( z = 0; z < count(); z++ ) { int sl = strlen(get(z)); if ( sl > l ) l = sl; } return l; } int VArray::min_len() { if ( count() == 0 ) return 0; int l = strlen(get(0)); int z; for( z = 0; z < count(); z++ ) { int sl = strlen(get(z)); if ( sl < l ) l = sl; } return l; } /*************************************************************************** ** ** VTRIENODE ** ****************************************************************************/ VTrieNode::VTrieNode() { next = NULL; down = NULL; c = 0; data = NULL; } VTrieNode::~VTrieNode() { if ( next ) delete next; if ( down ) delete down; if ( data ) delete data; } void VTrieNode::del_node( const char *key, int branch ) { if ( !key ) return; if ( !key[0] ) return; if ( key[1] == 0 ) { // last char reached if ( key[0] != c ) return; // not found // key found if ( data ) delete data; // release key's value data = NULL; if ( down ) { // there are more keys below if ( branch ) { delete down; // delete all keys below c = 0; // mark current as `not used' } } else { // there is no more keys below c = 0; // mark current as `not used' } } else { // last char is not reached if ( key[0] == c ) { // current char is in the key if ( down ) { // try key down down->del_node( key + 1, branch ); if ( down->c == 0 ) { // down node is not used--remove it ASSERT( down->down == NULL ); VTrieNode *tmp = down; down = down->next; delete tmp; } } else return; // not found } else { if ( next ) // char is not in the key, try next { next->del_node( key, branch ); if ( next->c == 0 ) { // down node is not used--remove it ASSERT( next->down == NULL ); VTrieNode *tmp = next; next = next->next; delete tmp; } } else return; // not found } } } VTrieNode* VTrieNode::find_node( const char* key, int create ) { if ( !key || !key[0] ) return NULL; if ( key[0] == c ) { // char in the current key if ( key[1] == 0 ) { // last char and is in the key--found! return this; } else { // not last char... if ( ! down && create ) { // nothing below but should create down = new VTrieNode(); down->c = key[1]; } if ( down ) return down->find_node( key + 1, create ); // search below else return NULL; // not found } } else { // char not in the current key--try next if ( ! next && create ) { // no next but should create next = new VTrieNode(); next->c = key[0]; } if ( next ) return next->find_node( key, create ); // search next else return NULL; // not found } } VTrieNode *VTrieNode::clone() { VTrieNode *tmp = new VTrieNode(); tmp->c = c; if ( next ) tmp->next = next->clone(); if ( down ) tmp->down = down->clone(); if ( data ) tmp->data = new VString( *data ); return tmp; } void VTrieNode::print() { printf( "---------------------------------\n" ); printf( "this = %p\n", this ); printf( "key = %c\n", c ); printf( "next = %p\n", next ); printf( "down = %p\n", down ); printf( "data = %s\n", data ? data->data() : "(null)" ); if (next) next->print(); if (down) down->print(); } /*************************************************************************** ** ** VTRIEBOX ** ****************************************************************************/ VTrieBox* VTrieBox::clone() { VTrieBox *new_box = new VTrieBox(); delete new_box->root; new_box->root = root->clone(); return new_box; } /*************************************************************************** ** ** VTRIE ** ****************************************************************************/ VTrie::VTrie() { box = new VTrieBox(); } VTrie::VTrie( const VArray& arr ) { box = new VTrieBox(); merge( (VArray*)&arr ); } VTrie::VTrie( const VTrie& tr ) { box = tr.box; box->ref(); } VTrie::~VTrie() { box->unref(); } void VTrie::detach() { if ( box->refs() == 1 ) return; VTrieBox *new_box = box->clone(); box->unref(); box = new_box; } void VTrie::trace_node( VTrieNode *node, VArray* keys, VArray *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 VTrie::set( const char* key, const char* value ) { if ( !value || !key || !key[0] ) return; detach(); VTrieNode *node = box->root->find_node( key, 1 ); ASSERT( node ); if ( ! node->data ) node->data = new VString(); node->data->set( value ); } const char* VTrie::get( const char* key ) { if ( !key || !key[0] ) return NULL; VTrieNode *node = box->root->find_node( key ); if ( node && node->data ) return node->data->data(); else return NULL; } void VTrie::del( const char* key ) { if ( !key || !key[0] ) return; detach(); box->root->del_node( key ); } int VTrie::exists( const char* key ) // return != 0 if key exist (with data) { VTrieNode *node = box->root->find_node( key ); if ( node && node->data ) return 1; else return 0; } void VTrie::keys_and_values( VArray *keys, VArray *values ) { trace_node( box->root, keys, values ); } VArray VTrie::keys() { VArray arr; keys_and_values( &arr, NULL ); return arr; } VArray VTrie::values() { VArray arr; keys_and_values( NULL, &arr ); return arr; } void VTrie::reverse() { VArray ka = keys(); VArray va = values(); ASSERT( ka.count() == va.count() ); undef(); int z = ka.count(); while( z-- ) { set( va.get( z ), ka.get( z ) ); } } void VTrie::merge( VTrie *tr ) { ASSERT( tr != this ); VArray ka = tr->keys(); VArray va = tr->values(); ASSERT( ka.count() == va.count() ); int z = ka.count(); while( z-- ) { set( ka.get( z ), va.get( z ) ); } } void VTrie::merge( VArray *arr ) { int z = 0; while( z < arr->count() ) { set( arr->get( z ), arr->get( z + 1 ) ); z += 2; } } int VTrie::fload( const char* fname ) { FILE* f = fopen( fname, "rt" ); if (!f) return 1; int r = fload( f ); fclose(f); return r; } int VTrie::fsave( const char* fname ) { FILE* f = fopen( fname, "wt" ); if (!f) return 1; int r = fsave( f ); fclose(f); return r; } int VTrie::fload( FILE* f ) { VArray arr; int r = arr.fload( f ); if ( r == 0 ) merge( &arr ); return r; } int VTrie::fsave( FILE* f ) { VArray arr; arr.push( this ); return arr.fsave( f ); } void VTrie::print() { VArray ka = keys(); VArray va = values(); ASSERT( ka.count() == va.count() ); while( ka.count() && va.count() ) { printf( "%s=%s\n", ka.pop(), va.pop() ); } } /**************************************************************************** ** ** VString Utilities -- functions and classes ** ****************************************************************************/ VString str_dot_reduce( const char* s, int width ) { VString 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, "..." ); return dest; } /**************************************************************************** ** ** VString file names utilities -- functions and classes ** NOTE: does not use any external function calls! ** ****************************************************************************/ char* str_fix_path( char* s, int slashtype ) { size_t sl = strlen( s ); if ( s[sl-1] != slashtype ) { s[sl] = slashtype; s[sl+1] = 0; } return s; } const char* str_fix_path( VString &s, int slashtype ) { size_t sl = str_len( s ); if ( s[sl-1] != slashtype ) str_add_ch( s, slashtype ); return (const char*)s; } VString str_file_ext( const char *ps ) { VString ext; int len = strlen(ps); int z = len - 1; while ( ps[z] != '.' && ps[z] != '/' && z > 0 ) z--; if ( ps[z] == '.' ) if ( !(z == 0 || (z > 0 && ps[z-1] == '/')) ) // `.name' has no extension! ext = ps + z + 1; return ext; } VString str_file_name( const char *ps ) { VString name; int len = strlen( ps ); int z = len - 1; while ( z >= 0 && ps[z] != '/' ) z--; name = ps + z + 1; z = str_len( name ) - 1; while ( z > 0 && name[z] != '.' && name[z] != '/' ) z--; if ( z > 0 && name[z] == '.') // `.name' has no extension! str_sleft( name, z ); return name; } VString str_file_name_ext( const char *ps ) { VString name; int len = strlen( ps ); int z = len - 1; while ( z >= 0 && ps[z] != '/' ) z--; name = ps + z + 1; return name; } VString str_file_path( const char *ps ) { VString name; int len = strlen( ps ); int z = len; while ( z >= 0 && ps[z] != '/' ) z--; name = ps; str_sleft( name, z+1 ); return name; } VString str_reduce_path( const char* path ) // removes ".."s { VString dest; dest = path; str_replace( dest, "/./", "/" ); int i = -1; while( (i = str_find( dest, "/../" ) ) != -1 ) { int j = i - 1; while( j >= 0 && dest[j] != '/' ) j--; ASSERT( j >= -1 ); if ( j < 0 ) { if ( dest[0] == '/' ) str_del( dest, 0, 3 ); } else str_del( dest, j+1, i+3-j ); } return dest; } /**************************************************************************** ** ** VString Conversions ** ****************************************************************************/ long hex2long( const char* s ) // hex to long { long P = 1; long C = 0; char tmp[255]; strcpy( tmp, s ); str_up( tmp ); str_cut_spc( tmp ); int sl = strlen(tmp); for( int z=sl-1; z >= 0; z-- ) { int i = -1; if( (i = str_find( "0123456789ABCDEF", tmp[z] )) != -1) C = C + P*i; else C = 0; P = P*16; } return C; } /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-4.22/vstring/vstrlib.h0000644000175000017500000002660614145574056014172 0ustar cadecade/**************************************************************************** * * VSTRING Library * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * * http://cade.datamax.bg/ * Distributed under the GPL license, you should receive copy of GPL! * * 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* type, so there is no need to change function calls * nor the implementation when you change from char* to VString (and * vice versa). The main difference from other similar libs is that * the dynamic VString class has no visible methods (except operators) * so you will use it as a plain char* 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 string, which resizes automatically * * VSTRLIB part (vstrlib.h and vstrlib.cpp) provides string data * structures which mimic Perl's. There are several classes: * * VArray -- array of VString elements * VTrie -- associative array (hash) of VString elements * VRegexp -- regular expression helper class * * VString, VArray, VTrie use shallow copy and copy-on-write functionality, * so things like str1 = str2, varray1 = varray2 etc. are cheap and fast :) * ***************************************************************************/ #ifndef _VSTRLIB_H_ #define _VSTRLIB_H_ #include #ifndef ASSERT #define ASSERT assert #endif #include #include #define PCRE2_CODE_UNIT_WIDTH 8 #include #include "vstring.h" /*************************************************************************** ** ** 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 ** ****************************************************************************/ char* time2str( const time_t tim ); time_t str2time( const char* timstr ); int str_find_regexp( const char* target, const 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 char* target, const 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 char *str, char* pattern ); /***************************************************************************** ** ** 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 char *p, int ps, const char *d, int ds ); int mem_quick_search( const char *p, int ps, const char *d, int ds ); int mem_sum_search( const char *p, int ps, const char *d, int ds ); /* no-case versions */ int mem_quick_search_nc( const char *p, int ps, const 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 char *p, int ps, FILE* f, const char* opt = "", int (*mem_search)( const char *p, int ps, const char *d, int ds ) = NULL ); long file_pattern_search( const char *p, int ps, const char* fn, const char* opt = "", int (*mem_search)( const char *p, int ps, const 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 ** *****************************************************************************/ extern int file_grep_max_line; extern int file_grep_lines_read; long file_grep( const char *re_string, const char* file_name, int nocase, off_t spos = -1 ); long file_grep( const 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 ** *****************************************************************************/ long file_string_search( const char *p, const char* fn, const char* opt ); long file_string_search( const char *p, FILE *f, const char* opt ); int mem_string_search( const char *p, const char* d, const char* opt ); /*************************************************************************** ** ** 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 VRegexp { /* 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 char *lp; // last subject data to search in, external, just keep ptr /* no-regexp/hex search pattern */ char* pt; // pattern for MODE_FIND and MODE_HEX int pl; // pattern length int pos; // last match found pos /* common data */ //VString substr; VString errstr; int get_options( const char* opt ); public: VRegexp(); VRegexp( const char* pattern, const char *opt = NULL ); // compiles new regexp ~VRegexp(); int comp( const char* pattern, const 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 char* line ); // execute re against line, return 1 for match int m( const char* line, const char* pattern, const char *opt = NULL ); // same as exec, but compiles first VString 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 VString operator []( int n ) // same as sub() { return sub( n ); } const char* error_str() { return errstr.data(); }; }; /*************************************************************************** ** ** VCHARSET ** ****************************************************************************/ class VCharSet { unsigned char *_data; int _size; // size (in bytes) void resize( int new_size ); public: VCharSet(); ~VCharSet(); void push( int n, int val = 1 ); void undef( int n ); void undef(); int in( int 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 ); */ }; /*************************************************************************** ** ** UTILITIES ** ****************************************************************************/ // split `source' with `regexp_str' regexp VArray str_split( const char* regexp_str, const char* source, int maxcount = -1 ); // split `source' with exact string `delimiter_str' VArray str_split_simple( const char* delimiter_str, const char* source, int maxcount = -1 ); // join array data to single string with `glue' string // returns the result string or store to optional `dest' VString str_join( VArray array, const char* glue = "" ); /*************************************************************************** ** ** MISC FUNCTIONS ** ****************************************************************************/ #endif /* _VSTRLIB_H_ */ /*************************************************************************** ** ** EOF ** ****************************************************************************/ vfu-4.22/vstring/mm.conf0000644000175000017500000000131514145576076013606 0ustar cadecade# # this file is used to create vslib.a library makefile # CC = ! LD = ! AR = ? ar RANLIB = ? ranlib LD = $(CXX) ############################################################################ # make vstring.a MODULES = pcre2 [vstring.a] # DEFAULT = 1 CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I. -Ipcre2 -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = $(LDFLAGS) $(LDDEF) TARGET = libvstring.a SRC = vstring.cpp SRC += vstrlib.cpp [test:vstring.a] CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I. -Ipcre2 -O2 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -Lpcre2 -lpcre2 $(LDFLAGS) $(LDDEF) TARGET = test SRC += test.cpp vfu-4.22/vstring/README.md0000644000175000017500000000764714145574056013617 0ustar cadecade # NAME VSTRING is C++ string manipulation and handling library. # 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. The dynamic string object can be freely exchanged with standard char* 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) implementation provide shallow copy and copy-on-write functionality so assignment operators like: str1 = str2 varr1 = varr2 vtrie1 = vtri2 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* AND VString FUNCTIONS NOTES All functions for char* handling may overflow! If you need safe strings, use the same functions but with VString instead of char*. 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() # 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-4.22/vstring/AUTHORS0000644000175000017500000000017214145574056013372 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-4.22/vstring/README0000644000175000017500000000166614145574056013213 0ustar cadecade VSTRING Library Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" Distributed under the GPL license, see end of this file for full text! VSTRING is C++ string library. It provides dynamic strings and char* compatibility and also Perl-like arrays, hashes and regexp objects. The dynamic string object can be freely exchanged with standard char* 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. 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-4.22/install0000755000175000017500000000115614145574023012217 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-4.22/rx/0000755000175000017500000000000014145574023011251 5ustar cadecadevfu-4.22/rx/rx_deb0000755000175000017500000000351014145574023012441 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-4.22/rx/rx_auto0000755000175000017500000000236314145574023012664 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 nown to me, sorry\n"; exec( '/usr/libexec/vfu/'.$rx, @ARGV ); sub choose { local $_ = shift; return "rx_tar" if /\.(?:tar(?:\.(?:z|gz|xz|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; return undef; } sub file_mtime { return (stat($_[0]))[9]; } vfu-4.22/rx/rx_rar0000755000175000017500000000412214145574023012473 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-4.22/rx/rx_zip0000755000175000017500000000410314145574023012510 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-4.22/rx/rx_tar0000755000175000017500000001011014145574023012467 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; } 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.list"; my $fo; sysopen $fo, $list, O_CREAT | O_EXCL, 0600; print $fo "$_\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; 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+/; 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; } vfu-4.22/rx/rxx0000755000175000017500000001364214145574023012026 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 ); use Data::Dumper; print Dumper( $content ); 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-4.22/rx/jane.ftp0000644000175000017500000000002014145574023012671 0ustar cadecadelocalhost - - - vfu-4.22/rx/rx_rpm0000755000175000017500000000407114145574023012510 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-4.22/rx/aenea.ftp0000644000175000017500000000001414145574023013030 0ustar cadecade192.168.1.1 vfu-4.22/rx/rx_ftp0000755000175000017500000000432414145574023012504 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-4.22/rx/AUTHORS0000644000175000017500000000021414145574023012316 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-4.22/rx/README0000644000175000017500000000222714145574023012134 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-4.22/README.MAC_OSX0000644000175000017500000000103414145574023012626 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@biscom.net cade@datamax.bg vfu-4.22/XWINDOW.NOTES0000644000175000017500000000251514145574023012574 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-4.22/makefile0000644000175000017500000000223614145574023012323 0ustar cadecade ### MAKEMAKE STARTS HERE ####################################################### ### Created by makemake.pl on Thu Aug 6 03:29:10 2020 ######################### ### GLOBAL TARGETS ############################################################# default: mm_update all re: mm_update rebuild li: mm_update link all: mm_update modules clean: mm_update clean-modules rebuild: mm_update rebuild-modules link: mm_update link-modules ### GLOBAL (AND USER) DEFS ##################################################### AR ?= ar LD = $(CXX) MKDIR = mkdir -p MODULES = vstring vslib vfu RANLIB ?= ranlib RMDIR = rm -rf RMFILE = rm -f SRC = *.c *.cpp *.cc *.cxx ### MODULES #################################################################### modules: $(MAKE) -C vstring $(MAKE) -C vslib $(MAKE) -C vfu clean-modules: $(MAKE) -C vstring clean $(MAKE) -C vslib clean $(MAKE) -C vfu clean rebuild-modules: $(MAKE) -C vstring rebuild $(MAKE) -C vslib rebuild $(MAKE) -C vfu rebuild link-modules: $(MAKE) -C vstring link $(MAKE) -C vslib link $(MAKE) -C vfu link mm_update: ### MAKEMAKE ENDS HERE ######################################################### vfu-4.22/vfu-get-latest-git-snapshot.pl0000755000175000017500000000235514151762146016454 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-4.22/vslib/0000755000175000017500000000000014145575505011745 5ustar cadecadevfu-4.22/vslib/unicon.cpp0000644000175000017500000003011014145574047013737 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "unicon.h" /**************************************************************************** ** ** DJGPP/DOS Part ** ****************************************************************************/ #ifdef _TARGET_GO32_ #include #include #include #include "vstring.h" int original_ta; text_info ti; int __fg; int __bg; int __ta; int con_init() { gppconio_init(); gettextinfo( &ti ); original_ta = ti.attribute; return 0; } void con_done() { textattr( original_ta ); return; } void con_suspend() { return; } void con_restore() { return; } void con_reset_screen_size() { return; } void con_ce( int attr ) { if (attr != -1) { int ta = __ta; textattr( attr ); clreol(); textattr( ta ); } else clreol(); } void con_cs( int attr ) { if (attr != -1) { int ta = __ta; textattr( attr ); clrscr(); gotoxy(1,1); textattr( ta ); } else { clrscr(); gotoxy(1,1); } } void con_puts( const char *s ) { #ifdef _TARGET_GO32_ VString str = s; str_replace( str, "\n", "\r\n" ); cputs( str ); #else cputs( s ); #endif } int con_max_x() { return ti.screenwidth; } int con_max_y() { return ti.screenheight; } int con_x() { return wherex(); } int con_y() { return wherey(); } void con_fg( int color ) { __fg = color; __ta = CONCOLOR(__fg,__bg); textattr( __ta ); } void con_bg( int color ) { __bg = color; __ta = CONCOLOR(__fg,__bg); textattr( __ta ); } void con_ta( int attr ) { __ta = attr; __bg = COLORBG(attr); __fg = COLORFG(attr); textattr( attr ); } void con_xy( int x, int y ) { gotoxy( x, y ); } void con_chide() { _setcursortype( _NOCURSOR ); } void con_cshow() { _setcursortype( _NORMALCURSOR ); } int con_kbhit() { return kbhit(); } int con_getch() { char ch = getch(); if ( ch == 0 ) return KEY_PREFIX + getch(); else return ch; } void con_beep() { sound(800); delay(1); // 1/10 second sound(0); } #endif /* _TARGET_GO32_ */ /**************************************************************************** ** ** UNIX Part ** ****************************************************************************/ #ifdef _TARGET_HAVE_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) i=0; else ungetch(i); return(i); } int con_getch() { int i; i=wgetch(conio_scr); if (i==-1) i=0; if (i == 27) if (con_kbhit()) i = KEY_PREFIX + wgetch(conio_scr); #ifndef _NO_ALT_ESCAPE_SAME_ if (i == KEY_PREFIX + 27) i = 27; #endif return(i); } void con_beep() { printf( "\007" ); fflush( stdout ); } #endif /* _TARGET_HAVE_CURSES */ // target have yascreen curses replacement #ifdef _TARGET_HAVE_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 ) { 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==-1) i=0; return(i); } int con_getch() { int i; i=yascreen_getch(ya_s); if (i==-1) i=0; return(i); } void con_beep() { printf( "\007" ); fflush( stdout ); } #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-4.22/vslib/unicon.h0000644000175000017500000003634514145574047013424 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _UNICON_H_ #define _UNICON_H_ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "target.h" #ifdef _TARGET_UNKNOWN_ #error "I don't know what is the target platform" #endif #ifdef _TARGET_UNIX_ #ifdef _TARGET_HAVE_YASCREEN #define inline #include #undef inline #else #if HAVE_CONFIG_H // This will find the curses header on most systems. If it isn't found, // the user will receive an error and compilation will terminate. // These are defined when ../configure is run, and suggested use is // mentioned in the autoconf documentation // #if defined HAVE_NCURSESW_CURSES_H # include #elif defined HAVE_NCURSESW_H # include #elif defined HAVE_NCURSES_CURSES_H # include #elif defined HAVE_NCURSES_H # include #elif defined HAVE_CURSES_H # include #else # error "SysV or X/Open-compatible Curses header file required" #endif #elif defined(_TARGET_LINUX_) #include #elif defined(_TARGET_NETBSD_) #include #else #include #endif #endif #include #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 KEY_CTRL_A 1 #define KEY_CTRL_B 2 #define KEY_CTRL_C 3 #define KEY_CTRL_D 4 #define KEY_CTRL_E 5 #define KEY_CTRL_F 6 #define KEY_CTRL_G 7 #define KEY_CTRL_H 8 #define KEY_CTRL_I 9 #define KEY_CTRL_J 10 #define KEY_CTRL_K 11 #define KEY_CTRL_L 12 #define KEY_CTRL_M 13 #define KEY_CTRL_N 14 #define KEY_CTRL_O 15 #define KEY_CTRL_P 16 #define KEY_CTRL_Q 17 #define KEY_CTRL_R 18 #define KEY_CTRL_S 19 #define KEY_CTRL_T 20 #define KEY_CTRL_U 21 #define KEY_CTRL_V 22 #define KEY_CTRL_W 23 #define KEY_CTRL_X 24 #define KEY_CTRL_Y 25 #define KEY_CTRL_Z 26 #ifdef KEY_ENTER #undef KEY_ENTER #endif #define KEY_ENTER 13 /******* DJGPP/DOS *********************************************************/ #ifdef _TARGET_GO32_ #define KEY_PREFIX 1000 #define KEY_BACKSPACE 8 #define KEY_LEFT (KEY_PREFIX + 75) #define KEY_RIGHT (KEY_PREFIX + 77) #define KEY_UP (KEY_PREFIX + 72) #define KEY_DOWN (KEY_PREFIX + 80) #define KEY_HOME (KEY_PREFIX + 71) #define KEY_END (KEY_PREFIX + 79) #define KEY_PPAGE (KEY_PREFIX + 73) #define KEY_NPAGE (KEY_PREFIX + 81) #define KEY_IC (KEY_PREFIX + 82) #define KEY_DC (KEY_PREFIX + 83) #define KEY_F0 (KEY_PREFIX + 58) #define KEY_F(n) (KEY_PREFIX + (58+(n))) #define KEY_F1 (KEY_PREFIX + 59) #define KEY_F2 (KEY_PREFIX + 60) #define KEY_F3 (KEY_PREFIX + 61) #define KEY_F4 (KEY_PREFIX + 62) #define KEY_F5 (KEY_PREFIX + 63) #define KEY_F6 (KEY_PREFIX + 64) #define KEY_F7 (KEY_PREFIX + 65) #define KEY_F8 (KEY_PREFIX + 66) #define KEY_F9 (KEY_PREFIX + 67) #define KEY_F10 (KEY_PREFIX + 68) #define KEY_SH_F1 (KEY_PREFIX + 84) #define KEY_SH_F2 (KEY_PREFIX + 85) #define KEY_SH_F3 (KEY_PREFIX + 86) #define KEY_SH_F4 (KEY_PREFIX + 87) #define KEY_SH_F5 (KEY_PREFIX + 88) #define KEY_SH_F6 (KEY_PREFIX + 89) #define KEY_SH_F7 (KEY_PREFIX + 90) #define KEY_SH_F8 (KEY_PREFIX + 91) #define KEY_SH_F9 (KEY_PREFIX + 92) #define KEY_SH_F10 (KEY_PREFIX + 93) #define KEY_CTRL_F1 (KEY_PREFIX + 94) #define KEY_CTRL_F2 (KEY_PREFIX + 95) #define KEY_CTRL_F3 (KEY_PREFIX + 96) #define KEY_CTRL_F4 (KEY_PREFIX + 97) #define KEY_CTRL_F5 (KEY_PREFIX + 98) #define KEY_CTRL_F6 (KEY_PREFIX + 99) #define KEY_CTRL_F7 (KEY_PREFIX + 100) #define KEY_CTRL_F8 (KEY_PREFIX + 101) #define KEY_CTRL_F9 (KEY_PREFIX + 102) #define KEY_CTRL_F10 (KEY_PREFIX + 103) #define KEY_ALT_F1 (KEY_PREFIX + 104) #define KEY_ALT_F2 (KEY_PREFIX + 105) #define KEY_ALT_F3 (KEY_PREFIX + 106) #define KEY_ALT_F4 (KEY_PREFIX + 107) #define KEY_ALT_F5 (KEY_PREFIX + 108) #define KEY_ALT_F6 (KEY_PREFIX + 109) #define KEY_ALT_F7 (KEY_PREFIX + 110) #define KEY_ALT_F8 (KEY_PREFIX + 111) #define KEY_ALT_F9 (KEY_PREFIX + 112) #define KEY_ALT_F10 (KEY_PREFIX + 113) #define KEY_ALT_1 (KEY_PREFIX + 120) #define KEY_ALT_2 (KEY_PREFIX + 121) #define KEY_ALT_3 (KEY_PREFIX + 122) #define KEY_ALT_4 (KEY_PREFIX + 123) #define KEY_ALT_5 (KEY_PREFIX + 124) #define KEY_ALT_6 (KEY_PREFIX + 125) #define KEY_ALT_7 (KEY_PREFIX + 126) #define KEY_ALT_8 (KEY_PREFIX + 127) #define KEY_ALT_9 (KEY_PREFIX + 128) #define KEY_ALT_0 (KEY_PREFIX + 129) #define KEY_ALT_MINUS (KEY_PREFIX + 130) #define KEY_ALT_EQ (KEY_PREFIX + 131) #define KEY_ALT_Q (KEY_PREFIX + 16) #define KEY_ALT_W (KEY_PREFIX + 17) #define KEY_ALT_E (KEY_PREFIX + 18) #define KEY_ALT_R (KEY_PREFIX + 19) #define KEY_ALT_T (KEY_PREFIX + 20) #define KEY_ALT_Y (KEY_PREFIX + 21) #define KEY_ALT_U (KEY_PREFIX + 22) #define KEY_ALT_I (KEY_PREFIX + 23) #define KEY_ALT_O (KEY_PREFIX + 24) #define KEY_ALT_P (KEY_PREFIX + 25) #define KEY_ALT_A (KEY_PREFIX + 30) #define KEY_ALT_S (KEY_PREFIX + 31) #define KEY_ALT_D (KEY_PREFIX + 32) #define KEY_ALT_F (KEY_PREFIX + 33) #define KEY_ALT_G (KEY_PREFIX + 34) #define KEY_ALT_H (KEY_PREFIX + 35) #define KEY_ALT_J (KEY_PREFIX + 36) #define KEY_ALT_K (KEY_PREFIX + 37) #define KEY_ALT_L (KEY_PREFIX + 38) #define KEY_ALT_Z (KEY_PREFIX + 44) #define KEY_ALT_X (KEY_PREFIX + 45) #define KEY_ALT_C (KEY_PREFIX + 46) #define KEY_ALT_V (KEY_PREFIX + 47) #define KEY_ALT_B (KEY_PREFIX + 48) #define KEY_ALT_N (KEY_PREFIX + 49) #define KEY_ALT_M (KEY_PREFIX + 50) /******* UNIX/NCURSES ******************************************************/ #elif defined(_TARGET_HAVE_CURSES) #define KEY_F1 (KEY_F(0) + 1) #define KEY_F2 (KEY_F(0) + 2) #define KEY_F3 (KEY_F(0) + 3) #define KEY_F4 (KEY_F(0) + 4) #define KEY_F5 (KEY_F(0) + 5) #define KEY_F6 (KEY_F(0) + 6) #define KEY_F7 (KEY_F(0) + 7) #define KEY_F8 (KEY_F(0) + 8) #define KEY_F9 (KEY_F(0) + 9) #define KEY_F10 (KEY_F(0) + 10) #define KEY_SH_F1 (KEY_F(0) + 11) #define KEY_SH_F2 (KEY_F(0) + 12) #define KEY_SH_F3 (KEY_F(0) + 13) #define KEY_SH_F4 (KEY_F(0) + 14) #define KEY_SH_F5 (KEY_F(0) + 15) #define KEY_SH_F6 (KEY_F(0) + 16) #define KEY_SH_F7 (KEY_F(0) + 17) #define KEY_SH_F8 (KEY_F(0) + 18) #define KEY_SH_F9 (KEY_F(0) + 19) #define KEY_SH_F10 (KEY_F(0) + 20) #define KEY_CTRL_F1 (-1) #define KEY_CTRL_F2 (-1) #define KEY_CTRL_F3 (-1) #define KEY_CTRL_F4 (-1) #define KEY_CTRL_F5 (-1) #define KEY_CTRL_F6 (-1) #define KEY_CTRL_F7 (-1) #define KEY_CTRL_F8 (-1) #define KEY_CTRL_F9 (-1) #define KEY_CTRL_F10 (-1) #define KEY_ALT_F1 (-1) #define KEY_ALT_F2 (-1) #define KEY_ALT_F3 (-1) #define KEY_ALT_F4 (-1) #define KEY_ALT_F5 (-1) #define KEY_ALT_F6 (-1) #define KEY_ALT_F7 (-1) #define KEY_ALT_F8 (-1) #define KEY_ALT_F9 (-1) #define KEY_ALT_F10 (-1) #define KEY_PREFIX 1000 #define KEY_ALT_1 (KEY_PREFIX + '1') #define KEY_ALT_2 (KEY_PREFIX + '2') #define KEY_ALT_3 (KEY_PREFIX + '3') #define KEY_ALT_4 (KEY_PREFIX + '4') #define KEY_ALT_5 (KEY_PREFIX + '5') #define KEY_ALT_6 (KEY_PREFIX + '6') #define KEY_ALT_7 (KEY_PREFIX + '7') #define KEY_ALT_8 (KEY_PREFIX + '8') #define KEY_ALT_9 (KEY_PREFIX + '9') #define KEY_ALT_0 (KEY_PREFIX + '0') #define KEY_ALT_MINUS (KEY_PREFIX + '-') #define KEY_ALT_EQ (KEY_PREFIX + '=') #define KEY_ALT_BACKSPACE (KEY_PREFIX + KEY_BACKSPACE ) #define KEY_ALT_Q (KEY_PREFIX + 'q') #define KEY_ALT_W (KEY_PREFIX + 'w') #define KEY_ALT_E (KEY_PREFIX + 'e') #define KEY_ALT_R (KEY_PREFIX + 'r') #define KEY_ALT_T (KEY_PREFIX + 't') #define KEY_ALT_Y (KEY_PREFIX + 'y') #define KEY_ALT_U (KEY_PREFIX + 'u') #define KEY_ALT_I (KEY_PREFIX + 'i') #define KEY_ALT_O (KEY_PREFIX + 'o') #define KEY_ALT_P (KEY_PREFIX + 'p') #define KEY_ALT_A (KEY_PREFIX + 'a') #define KEY_ALT_S (KEY_PREFIX + 's') #define KEY_ALT_D (KEY_PREFIX + 'd') #define KEY_ALT_F (KEY_PREFIX + 'f') #define KEY_ALT_G (KEY_PREFIX + 'g') #define KEY_ALT_H (KEY_PREFIX + 'h') #define KEY_ALT_J (KEY_PREFIX + 'j') #define KEY_ALT_K (KEY_PREFIX + 'k') #define KEY_ALT_L (KEY_PREFIX + 'l') #define KEY_ALT_Z (KEY_PREFIX + 'z') #define KEY_ALT_X (KEY_PREFIX + 'x') #define KEY_ALT_C (KEY_PREFIX + 'c') #define KEY_ALT_V (KEY_PREFIX + 'v') #define KEY_ALT_B (KEY_PREFIX + 'b') #define KEY_ALT_N (KEY_PREFIX + 'n') #define KEY_ALT_M (KEY_PREFIX + 'm') #elif defined(_TARGET_HAVE_YASCREEN) #define KEY_IC YAS_K_INS #define KEY_BACKSPACE YAS_K_BSP #define KEY_LEFT YAS_K_LEFT #define KEY_RIGHT YAS_K_RIGHT #define KEY_UP YAS_K_UP #define KEY_DOWN YAS_K_DOWN #define KEY_HOME YAS_K_HOME #define KEY_END YAS_K_END #define KEY_DC YAS_K_DEL #define KEY_PPAGE YAS_K_PGUP #define KEY_NPAGE YAS_K_PGDN #define KEY_ALT_A YAS_K_ALT('a') #define KEY_ALT_B YAS_K_ALT('b') #define KEY_ALT_C YAS_K_ALT('c') #define KEY_ALT_D YAS_K_ALT('d') #define KEY_ALT_E YAS_K_ALT('e') #define KEY_ALT_F YAS_K_ALT('f') #define KEY_ALT_G YAS_K_ALT('g') #define KEY_ALT_H YAS_K_ALT('h') #define KEY_ALT_I YAS_K_ALT('i') #define KEY_ALT_J YAS_K_ALT('j') #define KEY_ALT_K YAS_K_ALT('k') #define KEY_ALT_L YAS_K_ALT('l') #define KEY_ALT_M YAS_K_ALT('m') #define KEY_ALT_N YAS_K_ALT('n') #define KEY_ALT_R YAS_K_ALT('r') #define KEY_ALT_S YAS_K_ALT('s') #define KEY_ALT_X YAS_K_ALT('x') #define KEY_ALT_Z YAS_K_ALT('z') #define KEY_ALT_0 YAS_K_ALT('0') #define KEY_ALT_1 YAS_K_ALT('1') #define KEY_ALT_2 YAS_K_ALT('2') #define KEY_ALT_3 YAS_K_ALT('3') #define KEY_ALT_4 YAS_K_ALT('4') #define KEY_ALT_5 YAS_K_ALT('5') #define KEY_ALT_6 YAS_K_ALT('6') #define KEY_ALT_7 YAS_K_ALT('7') #define KEY_ALT_8 YAS_K_ALT('8') #define KEY_ALT_9 YAS_K_ALT('9') #define KEY_ALT_EQ YAS_K_ALT('=') #define KEY_ALT_MINUS YAS_K_ALT('-') #define KEY_ALT_BACKSPACE YAS_K_ALT( KEY_BACKSPACE ) #define KEY_F1 YAS_K_F1 #define KEY_F2 YAS_K_F2 #define KEY_F3 YAS_K_F3 #define KEY_F4 YAS_K_F4 #define KEY_F5 YAS_K_F5 #define KEY_F6 YAS_K_F6 #define KEY_F7 YAS_K_F7 #define KEY_F8 YAS_K_F8 #define KEY_F9 YAS_K_F9 #define KEY_F10 YAS_K_F10 #define KEY_SH_F1 YAS_K_S_F1 #define KEY_SH_F2 YAS_K_S_F2 #define KEY_SH_F3 YAS_K_S_F3 #define KEY_SH_F4 YAS_K_S_F4 #define KEY_SH_F5 YAS_K_S_F5 #define KEY_SH_F6 YAS_K_S_F6 #define KEY_SH_F7 YAS_K_S_F7 #define KEY_SH_F8 YAS_K_S_F8 #define KEY_SH_F9 YAS_K_S_F9 #define KEY_SH_F10 YAS_K_S_F10 #define KEY_CTRL_F1 (-1) #define KEY_CTRL_F2 (-1) #define KEY_CTRL_F3 (-1) #define KEY_CTRL_F4 (-1) #define KEY_CTRL_F5 (-1) #define KEY_CTRL_F6 (-1) #define KEY_CTRL_F7 (-1) #define KEY_CTRL_F8 (-1) #define KEY_CTRL_F9 (-1) #define KEY_CTRL_F10 (-1) #define KEY_ALT_F1 (-1) #define KEY_ALT_F2 (-1) #define KEY_ALT_F3 (-1) #define KEY_ALT_F4 (-1) #define KEY_ALT_F5 (-1) #define KEY_ALT_F6 (-1) #define KEY_ALT_F7 (-1) #define KEY_ALT_F8 (-1) #define KEY_ALT_F9 (-1) #define KEY_ALT_F10 (-1) #define KEY_RESIZE YAS_SCREEN_SIZE #else #error One of ncurses/yascreen libraries is required under UNIX #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 int con_getch(); // get single char from the "keyboard" void con_beep(); // make a "beep" sound #endif /* _UNICON_H_ */ /**************************************************************************** ** EOF ****************************************************************************/ vfu-4.22/vslib/vslib.vpj0000644000175000017500000000362714145574047013615 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-4.22/vslib/form_in.cpp0000644000175000017500000000751614145574047014113 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "form_in.h" #include "scroll.h" BSet FI_LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; BSet FI_DIGITS = "0123456789"; BSet FI_ALPHANUM = FI_LETTERS & FI_DIGITS; BSet FI_REALS = FI_DIGITS & "Ee+-."; BSet FI_USERS = ""; BSet FI_MASKS = "A#$U"; // A-any, #-int, $-real, U-users int EditStrBF = CONCOLOR( chWHITE, cBLUE ); int EditStrFH = CONCOLOR( cBLACK, cWHITE ); int TextInput( int x, int y, const char *prompt, int maxlen, int fieldlen, VString *strres, void (*handlekey)( int key, VString &s, int &pos ) ) { int res = 0; int insert = 1; VString str = *strres; VString tmp; int ch; ScrollPos scroll; scroll.set_min_max( 0, str_len( str ) ); scroll.set_pagesize( fieldlen ); scroll.go( str_len(str) ); int show = 1; int firsthit = 1; int opage = -1; con_cshow(); while(1) { if (opage != scroll.page()) show = 1; if (show) { str_copy( tmp, str, scroll.page(), scroll.pagesize() ); str_pad( tmp, -scroll.pagesize() ); tmp = " " + tmp + " "; if ( scroll.page() > 0 ) str_set_ch( tmp, 0, '<' ); if ( scroll.page()+scroll.pagesize() < str_len(str) ) str_set_ch( tmp, str_len(tmp)-1, '>' ); con_out(x, y, tmp, firsthit ? EditStrFH : EditStrBF ); show = 0; opage = scroll.page(); } con_xy( x + scroll.pos() - scroll.page() + 1 , y ); ch = con_getch(); if( ch >= 32 && ch <= 255 && ch != KEY_BACKSPACE && str_len(str) < maxlen - 1 ) { if (firsthit) { str = ""; scroll.go(0); firsthit = 0; } if (!insert) str_del( str, scroll.pos(), 1 ); str_ins_ch( str, scroll.pos(), ch ); scroll.set_min_max( 0, str_len( str ) ); scroll.go( scroll.pos() ); scroll.down(); show = 1; }; if (firsthit) { show = 1; firsthit = 0; } if( ch == 27 ) { res = 0; break; } else if( ch == 13 ) { *strres = str; res = 1; break; } else if( ch == KEY_CTRL_U ) { scroll.go(0); str = ""; show = 1; } else if( (ch == KEY_BACKSPACE || ch == 8 ) && (scroll.pos() > 0) ) { scroll.up(); str_del( str, scroll.pos(), 1 ); show = 1; } else if ( ch == KEY_IC ) insert = !insert; else if ( ch == KEY_LEFT ) scroll.up(); else if ( ch == KEY_RIGHT ) scroll.down(); else /* if ( ch == KEY_PPAGE ) scroll.ppage(); else if ( ch == KEY_NPAGE ) scroll.npage(); else */ if ( ch == KEY_HOME || ch == KEY_CTRL_A ) scroll.go(0); else if ( ch == KEY_END || ch == KEY_CTRL_E ) scroll.go(str_len(str)); else if ( ( ch == KEY_DC || ch == KEY_CTRL_D ) && scroll.pos() < str_len(str) ) { str_del( str, scroll.pos(), 1 ); show = 1; } else if ( handlekey ) { int npos = scroll.pos(); handlekey( ch, str, npos ); scroll.set_min_max( 0, str_len( str ) ); scroll.go( scroll.pos() ); if (scroll.pos() != npos) scroll.go( npos ); show = 1; } scroll.set_min_max( 0, str_len( str ) ); scroll.go( scroll.pos() ); } con_chide(); return res; } int TextInput( int x, int y, const char *prompt, int maxlen, int fieldlen, char *strres, void (*handlekey)( int key, VString &s, int &pos ) ) { VString str = strres; int res = TextInput( x, y, prompt, maxlen, fieldlen, &str, handlekey ); strcpy( strres, str.data() ); return res; } // eof form_in.cpp vfu-4.22/vslib/form_in.h0000644000175000017500000000230114145574047013543 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 "clusters.h" extern BSet FI_LETTERS; extern BSet FI_DIGITS; extern BSet FI_ALPHANUM; extern BSet FI_REALS; extern BSet FI_USERS; extern BSet FI_MASKS; extern int EditStrBF; // bakground/foreground extern int EditStrFH; // first hit color // allows only FI_USERS chars int FormInput( int x, int y, const char *prompt, const char *mask, VString *strres, void (*handlekey)( int key, VString &s, int &pos ) = NULL ); int TextInput( int x, int y, const char *prompt, int maxlen, int fieldlen, VString *strres, void (*handlekey)( int key, VString &s, int &pos ) = NULL ); int TextInput( int x, int y, const char *prompt, int maxlen, int fieldlen, char *strres, void (*handlekey)( int key, VString &s, int &pos ) = NULL ); #endif //_FORM_IN_H_ vfu-4.22/vslib/Makefile.in0000644000175000017500000004512514145574047014021 0ustar cadecade# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = vslib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = \ $(top_srcdir)/build-aux/m4/ax_check_enable_debug.m4 \ $(top_srcdir)/build-aux/m4/ax_path_lib_pcre.m4 \ $(top_srcdir)/build-aux/m4/ax_require_defined.m4 \ $(top_srcdir)/build-aux/m4/ax_with_curses.m4 \ $(top_srcdir)/build-aux/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AR = ar ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libvscon_a_AR = $(AR) $(ARFLAGS) libvscon_a_LIBADD = am_libvscon_a_OBJECTS = libvscon_a-ansiterm.$(OBJEXT) \ libvscon_a-conmenu.$(OBJEXT) libvscon_a-form_in.$(OBJEXT) \ libvscon_a-unicon.$(OBJEXT) libvscon_a_OBJECTS = $(am_libvscon_a_OBJECTS) libvslib_a_AR = $(AR) $(ARFLAGS) libvslib_a_LIBADD = am_libvslib_a_OBJECTS = clusters.$(OBJEXT) dlog.$(OBJEXT) \ eval.$(OBJEXT) fnmatch2.$(OBJEXT) getopt2.$(OBJEXT) \ scroll.$(OBJEXT) vslib.$(OBJEXT) vstring.$(OBJEXT) \ vstrlib.$(OBJEXT) vsuti.$(OBJEXT) vscrc.$(OBJEXT) libvslib_a_OBJECTS = $(am_libvslib_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libvscon_a_SOURCES) $(libvslib_a_SOURCES) DIST_SOURCES = $(libvscon_a_SOURCES) $(libvslib_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in COPYING README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CURSES_CFLAGS = @CURSES_CFLAGS@ CURSES_LIBS = @CURSES_LIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE_CFLAGS = @PCRE_CFLAGS@ PCRE_LIBS = @PCRE_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__leading_dot = @am__leading_dot@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libvslib.a libvscon.a check_LIBRARIES = $(noinst_LIBRARIES) AM_CXXFLAGS = -Wall libvslib_a_SOURCES = \ clusters.cpp \ dlog.cpp \ eval.cpp \ fnmatch2.cpp \ getopt2.cpp \ scroll.cpp \ vslib.cpp \ vstring.cpp \ vstrlib.cpp \ vsuti.cpp \ vscrc.cpp libvscon_a_SOURCES = \ ansiterm.cpp \ conmenu.cpp \ form_in.cpp \ unicon.cpp libvscon_a_CXXFLAGS = $(AM_CXXFLAGS) # needed for 'make dist' and 'make distcheck' # headers for libvscon noinst_HEADERS = dlog.h target.h vstring.h clusters.h eval.h getopt2.h \ vstrlib.h fnmatch2.h scroll.h vslib.h vsuti.h ansiterm.h \ unicon.h form_in.h conmenu.h all: all-am .SUFFIXES: .SUFFIXES: .cpp .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps vslib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign --ignore-deps vslib/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkLIBRARIES: -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES) clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libvscon.a: $(libvscon_a_OBJECTS) $(libvscon_a_DEPENDENCIES) $(EXTRA_libvscon_a_DEPENDENCIES) $(AM_V_at)-rm -f libvscon.a $(AM_V_AR)$(libvscon_a_AR) libvscon.a $(libvscon_a_OBJECTS) $(libvscon_a_LIBADD) $(AM_V_at)$(RANLIB) libvscon.a libvslib.a: $(libvslib_a_OBJECTS) $(libvslib_a_DEPENDENCIES) $(EXTRA_libvslib_a_DEPENDENCIES) $(AM_V_at)-rm -f libvslib.a $(AM_V_AR)$(libvslib_a_AR) libvslib.a $(libvslib_a_OBJECTS) $(libvslib_a_LIBADD) $(AM_V_at)$(RANLIB) libvslib.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .cpp.o: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` libvscon_a-ansiterm.o: ansiterm.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-ansiterm.o `test -f 'ansiterm.cpp' || echo '$(srcdir)/'`ansiterm.cpp libvscon_a-ansiterm.obj: ansiterm.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-ansiterm.obj `if test -f 'ansiterm.cpp'; then $(CYGPATH_W) 'ansiterm.cpp'; else $(CYGPATH_W) '$(srcdir)/ansiterm.cpp'; fi` libvscon_a-conmenu.o: conmenu.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-conmenu.o `test -f 'conmenu.cpp' || echo '$(srcdir)/'`conmenu.cpp libvscon_a-conmenu.obj: conmenu.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-conmenu.obj `if test -f 'conmenu.cpp'; then $(CYGPATH_W) 'conmenu.cpp'; else $(CYGPATH_W) '$(srcdir)/conmenu.cpp'; fi` libvscon_a-form_in.o: form_in.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-form_in.o `test -f 'form_in.cpp' || echo '$(srcdir)/'`form_in.cpp libvscon_a-form_in.obj: form_in.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-form_in.obj `if test -f 'form_in.cpp'; then $(CYGPATH_W) 'form_in.cpp'; else $(CYGPATH_W) '$(srcdir)/form_in.cpp'; fi` libvscon_a-unicon.o: unicon.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-unicon.o `test -f 'unicon.cpp' || echo '$(srcdir)/'`unicon.cpp libvscon_a-unicon.obj: unicon.cpp $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvscon_a_CXXFLAGS) $(CXXFLAGS) -c -o libvscon_a-unicon.obj `if test -f 'unicon.cpp'; then $(CYGPATH_W) 'unicon.cpp'; else $(CYGPATH_W) '$(srcdir)/unicon.cpp'; fi` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) check: check-am all-am: Makefile $(LIBRARIES) $(HEADERS) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-checkLIBRARIES clean-generic clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-checkLIBRARIES clean-generic clean-noinstLIBRARIES \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: vfu-4.22/vslib/target.h0000644000175000017500000000522114145574047013404 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 #ifdef _TARGET_UNIX_ #ifdef USE_YASCREEN #define _TARGET_HAVE_YASCREEN #else #define _TARGET_HAVE_CURSES #endif #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 //_TARGET_H_ vfu-4.22/vslib/conmenu.h0000644000175000017500000000255514145574047013571 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _CONMENU_H_ #define _CONMENU_H_ #include struct ToggleEntry { int key; char name[64]; int *data; const char **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 ec; // exit char (used by con_menu_box) int ac; // alternative confirm (used by menu box) int st; // scroll type -- 1 dynamic and 0 normal/static char hide_magic[32]; }; extern ConMenuInfo con_default_menu_info; int con_toggle_box( int x, int y, const char *title, ToggleEntry* toggles, ConMenuInfo *menu_info ); int con_menu_box( int x, int y, const char *title, VArray *va, 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 char *title, VArray *va, ConMenuInfo *menu_info ); #endif //_CONMENU_H_ vfu-4.22/vslib/makefile0000644000175000017500000002077114145575505013454 0ustar cadecade ### MAKEMAKE STARTS HERE ####################################################### ### Created by makemake.pl on Fri Nov 19 03:17:30 2021 ######################### ### GLOBAL TARGETS ############################################################# default: mm_update libvslib.a libvscon.a libvscony.a re: mm_update rebuild li: mm_update link all: mm_update libvslib.a libvscon.a libvscony.a test clean: mm_update clean-libvslib.a clean-libvscon.a clean-libvscony.a clean-test rebuild: mm_update rebuild-libvslib.a rebuild-libvscon.a rebuild-libvscony.a rebuild-test link: mm_update link-libvslib.a link-libvscon.a link-libvscony.a link-test ### GLOBAL (AND USER) DEFS ##################################################### AR ?= ar LD = $(CXX) MKDIR = mkdir -p RANLIB ?= ranlib RMDIR = rm -rf RMFILE = rm -f SRC = *.c *.cpp *.cc *.cxx ### TARGET 1: libvslib.a ####################################################### CC_1 = $(CXX) LD_1 = $(CXX) AR_1 = $(AR) rv RANLIB_1 = $(RANLIB) CCFLAGS_1 = -I../vstring -I../vstring/pcre2 -I. -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_1 = $(LDFLAGS) $(LDDEF) DEPFLAGS_1 = ARFLAGS_1 = TARGET_1 = libvslib.a ### SOURCES FOR TARGET 1: libvslib.a ########################################### SRC_1= \ clusters.cpp \ dlog.cpp \ eval.cpp \ fnmatch2.cpp \ getopt2.cpp \ scroll.cpp \ vslib.cpp \ vsuti.cpp \ vscrc.cpp \ #### OBJECTS FOR TARGET 1: libvslib.a ########################################## OBJ_1= \ .OBJ.libvslib.a/clusters.o \ .OBJ.libvslib.a/dlog.o \ .OBJ.libvslib.a/eval.o \ .OBJ.libvslib.a/fnmatch2.o \ .OBJ.libvslib.a/getopt2.o \ .OBJ.libvslib.a/scroll.o \ .OBJ.libvslib.a/vslib.o \ .OBJ.libvslib.a/vsuti.o \ .OBJ.libvslib.a/vscrc.o \ ### TARGET DEFINITION FOR TARGET 1: libvslib.a ################################# .OBJ.libvslib.a: $(MKDIR) .OBJ.libvslib.a libvslib.a: .OBJ.libvslib.a $(OBJ_1) $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) clean-libvslib.a: $(RMFILE) $(TARGET_1) $(RMDIR) .OBJ.libvslib.a rebuild-libvslib.a: clean-libvslib.a libvslib.a re-libvslib.a: rebuild-libvslib.a link-libvslib.a: .OBJ.libvslib.a $(OBJ_1) $(RMFILE) libvslib.a $(AR_1) $(ARFLAGS_1) $(TARGET_1) $(OBJ_1) $(RANLIB_1) $(TARGET_1) ### TARGET OBJECTS FOR TARGET 1: libvslib.a #################################### .OBJ.libvslib.a/clusters.o: clusters.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c clusters.cpp -o .OBJ.libvslib.a/clusters.o .OBJ.libvslib.a/dlog.o: dlog.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c dlog.cpp -o .OBJ.libvslib.a/dlog.o .OBJ.libvslib.a/eval.o: eval.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c eval.cpp -o .OBJ.libvslib.a/eval.o .OBJ.libvslib.a/fnmatch2.o: fnmatch2.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c fnmatch2.cpp -o .OBJ.libvslib.a/fnmatch2.o .OBJ.libvslib.a/getopt2.o: getopt2.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c getopt2.cpp -o .OBJ.libvslib.a/getopt2.o .OBJ.libvslib.a/scroll.o: scroll.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c scroll.cpp -o .OBJ.libvslib.a/scroll.o .OBJ.libvslib.a/vslib.o: vslib.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vslib.cpp -o .OBJ.libvslib.a/vslib.o .OBJ.libvslib.a/vsuti.o: vsuti.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vsuti.cpp -o .OBJ.libvslib.a/vsuti.o .OBJ.libvslib.a/vscrc.o: vscrc.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vscrc.cpp -o .OBJ.libvslib.a/vscrc.o ### TARGET 2: libvscon.a ####################################################### CC_2 = $(CXX) LD_2 = $(CXX) AR_2 = $(AR) rv RANLIB_2 = $(RANLIB) CCFLAGS_2 = -I../vstring -I../vstring/pcre2 -I. -I/usr/include/ncurses -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_2 = $(LDFLAGS) $(LDDEF) DEPFLAGS_2 = ARFLAGS_2 = TARGET_2 = libvscon.a ### SOURCES FOR TARGET 2: libvscon.a ########################################### SRC_2= \ ansiterm.cpp \ conmenu.cpp \ form_in.cpp \ unicon.cpp \ #### OBJECTS FOR TARGET 2: libvscon.a ########################################## OBJ_2= \ .OBJ.libvscon.a/ansiterm.o \ .OBJ.libvscon.a/conmenu.o \ .OBJ.libvscon.a/form_in.o \ .OBJ.libvscon.a/unicon.o \ ### TARGET DEFINITION FOR TARGET 2: libvscon.a ################################# .OBJ.libvscon.a: $(MKDIR) .OBJ.libvscon.a libvscon.a: .OBJ.libvscon.a $(OBJ_2) $(AR_2) $(ARFLAGS_2) $(TARGET_2) $(OBJ_2) $(RANLIB_2) $(TARGET_2) clean-libvscon.a: $(RMFILE) $(TARGET_2) $(RMDIR) .OBJ.libvscon.a rebuild-libvscon.a: clean-libvscon.a libvscon.a re-libvscon.a: rebuild-libvscon.a link-libvscon.a: .OBJ.libvscon.a $(OBJ_2) $(RMFILE) libvscon.a $(AR_2) $(ARFLAGS_2) $(TARGET_2) $(OBJ_2) $(RANLIB_2) $(TARGET_2) ### TARGET OBJECTS FOR TARGET 2: libvscon.a #################################### .OBJ.libvscon.a/ansiterm.o: ansiterm.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c ansiterm.cpp -o .OBJ.libvscon.a/ansiterm.o .OBJ.libvscon.a/conmenu.o: conmenu.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c conmenu.cpp -o .OBJ.libvscon.a/conmenu.o .OBJ.libvscon.a/form_in.o: form_in.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c form_in.cpp -o .OBJ.libvscon.a/form_in.o .OBJ.libvscon.a/unicon.o: unicon.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c unicon.cpp -o .OBJ.libvscon.a/unicon.o ### TARGET 3: libvscony.a ###################################################### CC_3 = $(CXX) LD_3 = $(CXX) AR_3 = $(AR) rv RANLIB_3 = $(RANLIB) CCFLAGS_3 = -I../vstring -I../vstring/pcre2 -I. -I../yascreen -DUSE_YASCREEN -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_3 = $(LDFLAGS) $(LDDEF) DEPFLAGS_3 = ARFLAGS_3 = TARGET_3 = libvscony.a ### SOURCES FOR TARGET 3: libvscony.a ########################################## SRC_3= \ ansiterm.cpp \ conmenu.cpp \ form_in.cpp \ unicon.cpp \ #### OBJECTS FOR TARGET 3: libvscony.a ######################################### OBJ_3= \ .OBJ.libvscony.a/ansiterm.o \ .OBJ.libvscony.a/conmenu.o \ .OBJ.libvscony.a/form_in.o \ .OBJ.libvscony.a/unicon.o \ ### TARGET DEFINITION FOR TARGET 3: libvscony.a ################################ .OBJ.libvscony.a: $(MKDIR) .OBJ.libvscony.a libvscony.a: .OBJ.libvscony.a $(OBJ_3) $(AR_3) $(ARFLAGS_3) $(TARGET_3) $(OBJ_3) $(RANLIB_3) $(TARGET_3) clean-libvscony.a: $(RMFILE) $(TARGET_3) $(RMDIR) .OBJ.libvscony.a rebuild-libvscony.a: clean-libvscony.a libvscony.a re-libvscony.a: rebuild-libvscony.a link-libvscony.a: .OBJ.libvscony.a $(OBJ_3) $(RMFILE) libvscony.a $(AR_3) $(ARFLAGS_3) $(TARGET_3) $(OBJ_3) $(RANLIB_3) $(TARGET_3) ### TARGET OBJECTS FOR TARGET 3: libvscony.a ################################### .OBJ.libvscony.a/ansiterm.o: ansiterm.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c ansiterm.cpp -o .OBJ.libvscony.a/ansiterm.o .OBJ.libvscony.a/conmenu.o: conmenu.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c conmenu.cpp -o .OBJ.libvscony.a/conmenu.o .OBJ.libvscony.a/form_in.o: form_in.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c form_in.cpp -o .OBJ.libvscony.a/form_in.o .OBJ.libvscony.a/unicon.o: unicon.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c unicon.cpp -o .OBJ.libvscony.a/unicon.o ### TARGET 4: test ############################################################# CC_4 = $(CXX) LD_4 = $(CXX) AR_4 = $(AR) rv RANLIB_4 = $(RANLIB) CCFLAGS_4 = -g -I../vstring -I../vstring/pcre2 -I. -I../yascreen -DUSE_YASCREEN -O0 -DTEST $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_4 = -g -L../vstring -L../vstring/pcre2 -L. -lvstring -lvslib -lvscon -lpcre2 -lncurses $(LDFLAGS) $(LDDEF) DEPFLAGS_4 = ARFLAGS_4 = TARGET_4 = test ### SOURCES FOR TARGET 4: test ################################################# SRC_4= \ t/test.cpp \ #### OBJECTS FOR TARGET 4: test ################################################ OBJ_4= \ .OBJ.test/test.o \ ### TARGET DEFINITION FOR TARGET 4: test ####################################### .OBJ.test: $(MKDIR) .OBJ.test test: libvslib.a libvscon.a .OBJ.test $(OBJ_4) $(LD_4) $(OBJ_4) $(LDFLAGS_4) -o $(TARGET_4) clean-test: $(RMFILE) $(TARGET_4) $(RMDIR) .OBJ.test rebuild-test: clean-test test re-test: rebuild-test link-test: .OBJ.test $(OBJ_4) $(RMFILE) test $(LD_4) $(OBJ_4) $(LDFLAGS_4) -o $(TARGET_4) ### TARGET OBJECTS FOR TARGET 4: test ########################################## .OBJ.test/test.o: t/test.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c t/test.cpp -o .OBJ.test/test.o mm_update: ### MAKEMAKE ENDS HERE ######################################################### vfu-4.22/vslib/clusters.h0000644000175000017500000002001514145574047013760 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 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-4.22/vslib/clusters.cpp0000644000175000017500000002262314145574047014322 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 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-4.22/vslib/scroll.cpp0000644000175000017500000000466714145574047013764 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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--; 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++; 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 < 1 ) return 0; // if ( _pos >= _page + _pagesize ) return 0; return 1; } // eof scroll.cpp vfu-4.22/vslib/Makefile.am0000644000175000017500000000143414145574047014003 0ustar cadecadenoinst_LIBRARIES = libvslib.a libvscon.a check_LIBRARIES = $(noinst_LIBRARIES) AM_CXXFLAGS = -Wall libvslib_a_SOURCES = \ clusters.cpp \ dlog.cpp \ eval.cpp \ fnmatch2.cpp \ getopt2.cpp \ scroll.cpp \ vslib.cpp \ vstring.cpp \ vstrlib.cpp \ vsuti.cpp \ vscrc.cpp libvscon_a_SOURCES = \ ansiterm.cpp \ conmenu.cpp \ form_in.cpp \ unicon.cpp libvscon_a_CXXFLAGS = $(AM_CXXFLAGS) # needed for 'make dist' and 'make distcheck' noinst_HEADERS = \ dlog.h \ target.h \ vstring.h \ clusters.h \ eval.h \ getopt2.h \ vstrlib.h \ fnmatch2.h \ scroll.h \ vslib.h \ vsuti.h # headers for libvscon noinst_HEADERS += \ ansiterm.h \ unicon.h \ form_in.h \ conmenu.h vfu-4.22/vslib/eval.cpp0000644000175000017500000000775414145574047013415 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/fnmatch2.cpp0000644000175000017500000001034014145574047014151 0ustar cadecade/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "fnmatch2.h" /* Match STRING against the filename pattern PATTERN, returning zero if it matches, nonzero if not. */ int fnmatch (const char *pattern, const char *string, int flags) { register const char *p = pattern, *n = string; register char c; /* Note that this evalutes C many times. */ #define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c)) while ((c = *p++) != '\0') { c = FOLD (c); switch (c) { case '?': if (*n == '\0') return FNM_NOMATCH; else if ((flags & FNM_FILE_NAME) && *n == '/') return FNM_NOMATCH; else if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) return FNM_NOMATCH; break; case '\\': if (!(flags & FNM_NOESCAPE)) { c = *p++; c = FOLD (c); } if (FOLD (*n) != c) return FNM_NOMATCH; break; case '*': if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) return FNM_NOMATCH; for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) if (((flags & FNM_FILE_NAME) && *n == '/') || (c == '?' && *n == '\0')) return FNM_NOMATCH; if (c == '\0') return 0; { char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; c1 = FOLD (c1); for (--p; *n != '\0'; ++n) if ((c == '[' || FOLD (*n) == c1) && fnmatch (p, n, flags & ~FNM_PERIOD) == 0) return 0; return FNM_NOMATCH; } case '[': { /* Nonzero if the sense of the character class is inverted. */ register int notnot; if (*n == '\0') return FNM_NOMATCH; if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) return FNM_NOMATCH; notnot = (*p == '!' || *p == '^'); if (notnot) ++p; c = *p++; for (;;) { register char cstart = c, cend = c; if (!(flags & FNM_NOESCAPE) && c == '\\') cstart = cend = *p++; cstart = cend = FOLD (cstart); if (c == '\0') /* [ (unterminated) loses. */ return FNM_NOMATCH; c = *p++; c = FOLD (c); if ((flags & FNM_FILE_NAME) && c == '/') /* [/] can never match. */ return FNM_NOMATCH; if (c == '-' && *p != ']') { cend = *p++; if (!(flags & FNM_NOESCAPE) && cend == '\\') cend = *p++; if (cend == '\0') return FNM_NOMATCH; cend = FOLD (cend); c = *p++; } if (FOLD (*n) >= cstart && FOLD (*n) <= cend) goto matched; if (c == ']') break; } if (!notnot) return FNM_NOMATCH; break; matched:; /* Skip the rest of the [...] that already matched. */ while (c != ']') { if (c == '\0') /* [... (unterminated) loses. */ return FNM_NOMATCH; c = *p++; if (!(flags & FNM_NOESCAPE) && c == '\\') /* XXX 1003.2d11 is unclear if this is right. */ ++p; } if (notnot) return FNM_NOMATCH; } break; default: if (c != FOLD (*n)) return FNM_NOMATCH; } ++n; } if (*n == '\0') return 0; if ((flags & FNM_LEADING_DIR) && *n == '/') /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ return 0; return FNM_NOMATCH; } // eof vfu-4.22/vslib/ansiterm.cpp0000644000175000017500000001067314145574047014302 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/dlog.cpp0000644000175000017500000000376514145574047013411 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/vslib.h0000644000175000017500000000125014145574047013233 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 "target.h" #endif //_VLIB_H_ // eof vlib.h vfu-4.22/vslib/getopt2.h0000644000175000017500000000141114145574047013477 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-4.22/vslib/fnmatch2.h0000644000175000017500000000364314145574047013626 0ustar cadecade/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _FNMATCH_H #define _FNMATCH_H 1 /* We #undef these before defining them because some losing systems (HP-UX A.08.07 for example) define these in . */ #undef FNM_PATHNAME #undef FNM_NOESCAPE #undef FNM_PERIOD /* Bits set in the FLAGS argument to `fnmatch'. */ #define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ #define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ #define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ // #if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) #define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ #define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ #define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ // #endif /* Value returned by `fnmatch' if STRING does not match PATTERN. */ #define FNM_NOMATCH 1 /* Match STRING against the filename pattern PATTERN, returning zero if it matches, FNM_NOMATCH if not. */ int fnmatch(const char *__pattern, const char *__string, int __flags); #endif /* fnmatch.h */ vfu-4.22/vslib/COPYING0000644000175000017500000004307014145574047013004 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-4.22/vslib/scroll.h0000644000175000017500000000342314145574047013416 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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; int _pagestep; // step to change page on up/down out of the current page 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; } void set_pagestep( int a_pagestep ) { _pagestep = a_pagestep; if ( _pagestep < 1 ) _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-4.22/vslib/eval.h0000644000175000017500000000117114145574047013045 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/t/0000755000175000017500000000000014145574047012210 5ustar cadecadevfu-4.22/vslib/t/Makefile.in0000644000175000017500000007045614145574047014271 0ustar cadecade# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : check_PROGRAMS = test_vslib$(EXEEXT) subdir = vslib/t ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = \ $(top_srcdir)/build-aux/m4/ax_check_enable_debug.m4 \ $(top_srcdir)/build-aux/m4/ax_path_lib_pcre.m4 \ $(top_srcdir)/build-aux/m4/ax_require_defined.m4 \ $(top_srcdir)/build-aux/m4/ax_with_curses.m4 \ $(top_srcdir)/build-aux/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am_test_vslib_OBJECTS = test.$(OBJEXT) test_vslib_OBJECTS = $(am_test_vslib_OBJECTS) test_vslib_LDADD = $(LDADD) test_vslib_DEPENDENCIES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(test_vslib_SOURCES) DIST_SOURCES = $(test_vslib_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CURSES_CFLAGS = @CURSES_CFLAGS@ CURSES_LIBS = @CURSES_LIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = -L $(top_builddir)/vslib LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE_CFLAGS = @PCRE_CFLAGS@ PCRE_LIBS = @PCRE_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__leading_dot = @am__leading_dot@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CXXFLAGS = -Wall -I$(top_srcdir)/vslib test_vslib_SOURCES = test.cpp TESTS = $(check_PROGRAMS) LDADD = -lvslib -lvscon @PCRE_LIBS@ @CURSES_LIBS@ all: all-am .SUFFIXES: .SUFFIXES: .cpp .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps vslib/t/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign --ignore-deps vslib/t/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkPROGRAMS: -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) test_vslib$(EXEEXT): $(test_vslib_OBJECTS) $(test_vslib_DEPENDENCIES) $(EXTRA_test_vslib_DEPENDENCIES) @rm -f test_vslib$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_vslib_OBJECTS) $(test_vslib_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .cpp.o: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? test_vslib.log: test_vslib$(EXEEXT) @p='test_vslib$(EXEEXT)'; \ b='test_vslib'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-checkPROGRAMS clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ clean-checkPROGRAMS clean-generic cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic distclean-tags \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: vfu-4.22/vslib/t/Makefile.am0000644000175000017500000000032614145574047014245 0ustar cadecadeAM_CXXFLAGS = -Wall -I$(top_srcdir)/vslib check_PROGRAMS = test_vslib test_vslib_SOURCES = test.cpp TESTS=$(check_PROGRAMS) LDFLAGS = -L $(top_builddir)/vslib LDADD = -lvslib -lvscon @PCRE_LIBS@ @CURSES_LIBS@ vfu-4.22/vslib/t/test.cpp0000644000175000017500000002653614145574047013707 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 "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"; // 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 = %d\n", str_len( set ) ); printf( "cat = %d\n", str_len( cat ) ); printf( "setn = %d\n", str_len( setn ) ); printf( "catn = %d\n", str_len( catn ) ); printf( "sete = %d\n", str_len( sete ) ); printf( "setp = %d\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: %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: [%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" ); } int main( int argc, char* argv[] ) { /* 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 } /**/ 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(); //*/ return 0; } vfu-4.22/vslib/getopt2.cpp0000644000175000017500000001076414145574047014045 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-4.22/vslib/THANKS.TO0000644000175000017500000000062114145574047013200 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-4.22/vslib/vslib.cpp0000644000175000017500000000062114145574047013567 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ // eof vslib.cpp vfu-4.22/vslib/mm.conf0000644000175000017500000000425714145575505013235 0ustar cadecade############################################################################ # # (c) Vladi Belperchinov-Shabanski "Cade" 1996-2012 # http://cade.datamax.bg/ # ############################################################################ CC = ! LD = ! AR = ? ar RANLIB = ? ranlib LD = $(CXX) ############################################################################ # make vslib (general features that coul'd be used in any context) [libvslib.a] DEFAULT = 1 CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I. -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = $(LDFLAGS) $(LDDEF) SRC = clusters.cpp dlog.cpp eval.cpp fnmatch2.cpp getopt2.cpp SRC += scroll.cpp vslib.cpp vsuti.cpp vscrc.cpp ############################################################################ # make vscon library that provides unified console handling for multiple # platforms [libvscon.a] DEFAULT = 1 CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I. -I/usr/include/ncurses -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = $(LDFLAGS) $(LDDEF) SRC = ansiterm.cpp conmenu.cpp form_in.cpp unicon.cpp ############################################################################ # yascreen based vscon # [libvscony.a] DEFAULT = 1 CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I. -I../yascreen -DUSE_YASCREEN -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = $(LDFLAGS) $(LDDEF) SRC = ansiterm.cpp conmenu.cpp form_in.cpp unicon.cpp ############################################################################ # make test program [test] CC = $(CXX) LD = $(CXX) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -g -I../vstring -I../vstring/pcre2 -I. -I../yascreen -DUSE_YASCREEN -O0 -DTEST $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -g -L../vstring -L../vstring/pcre2 -L. -lvstring -lvslib -lvscon -lpcre2 -lncurses $(LDFLAGS) $(LDDEF) DEPS = libvslib.a libvscon.a SRC = t/test.cpp ############################################################################ vfu-4.22/vslib/vscrc.cpp0000644000175000017500000001213614145574047013574 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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; */ 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-4.22/vslib/vsuti.h0000644000175000017500000001304514145574047013273 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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; */ 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 ); /***************************************************************************** ** ** 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-4.22/vslib/ansiterm.h0000644000175000017500000000413214145574047013740 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/dlog.h0000644000175000017500000000202414145574047013041 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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-4.22/vslib/AUTHORS0000644000175000017500000000017214145574047013015 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.noxrun.com vfu-4.22/vslib/conmenu.cpp0000644000175000017500000002110214145574047014111 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include #include #include #include #include "conmenu.h" ConMenuInfo con_default_menu_info; int con_toggle_box( int x, int y, const char *title, ToggleEntry* toggles, ConMenuInfo *menu_info ) { ScrollPos scroll; int z; int w = -1; int h = -1; int maxlen = 0; int count = 0; while( toggles[count].key != -1 ) { int sl = strlen( 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 = strlen(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; VString str; VString str1; VString hots = ""; for(z = 0; z < count; z++) if (strncmp("--", toggles[z].name, 2)) str_add_ch( hots, toggles[z].key ); else str_add_ch( hots,' ' ); con_xy(x,y); int ch = 0; str = ""; str = title; str_pad( str,-w, menu_info->bo ? '-' : ' ' ); if (str_len(str) > w) str_sleft(str,w); if (menu_info->bo) str = ".-" + str + "-."; else str = " " + str + " "; con_out(x,y,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 = " ~"; str_pad( str, -(w+2), ' '); } else { int sep = (strncmp("--", toggles[scroll.page()+z].name, 2) == 0); if (sep) { str = ""; str += toggles[scroll.page()+z].name; str_pad( str, -w, '-'); str += "--"; } else { str = " "; str_add_ch( str, toggles[scroll.page()+z].key ); str += " "; str += toggles[scroll.page()+z].name; str_pad( str, -(w-6), ' '); str1 = toggles[scroll.page()+z].states[*(toggles[scroll.page()+z].data)]; str_pad( str1, 6, ' '); str1 += " "; str += " " + str1; } } if (menu_info->bo) str = "|" + str + "|"; else str = " " + str + " "; // if (str.len() > w) StrSLeft(str,w); // str = " " + str + " "; con_out( x, y+z, str, ( scroll.page()+z != scroll.pos() ) ? menu_info->cn : menu_info->ch ); } if (menu_info->bo) { str = ""; str_pad( str, w+2, '-' ); str = "`" + str + "'"; con_out( x, y+scroll.pagesize(), str, menu_info->cn ); } ch = con_getch(); menu_info->ec = ch; if ( ch == KEY_UP ) scroll.up(); if ( ch == KEY_DOWN ) scroll.down(); if ( ch == KEY_NPAGE ) scroll.npage(); if ( ch == KEY_PPAGE ) scroll.ppage(); if ( ch == KEY_HOME ) scroll.home(); if ( ch == KEY_END ) scroll.end(); if ( ch == 27 || ch == 8 || ch == KEY_BACKSPACE ) return 0; // exit on ESC or BS if ( ch < 0 || ch > 255 ) continue; //FIXME: unicode? if ( ch == 13 /* && strncmp("--", toggles[scroll.pos].name, 2) */ ) return 1; z = ( ch == ' ' ) ? scroll.pos() : str_find( hots, ch ); if (z > -1 && strncmp("--", 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 char *title, VArray *va, int hotkeys, ConMenuInfo *menu_info ) { ScrollPos scroll; int z; int w = -1; int h = -1; if (w == -1) w = va->max_len(); if (h == -1) h = va->count(); z = strlen(title); if (w < z) w = z; if (h > va->count()) h = va->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; VString str; VString hots = ""; if ( hotkeys > -1 ) { for(z = 0; z < va->count(); z++) if (strncmp("--", va->get(z), 2)) str_add_ch( hots, char(((const char*)(va->get(z)))[hotkeys]) ); else str_add_ch( hots,' ' ); str_up(hots); } con_xy(x,y); int ch = 0; str = " "; str += title; str += " "; str_pad( str,-(w), menu_info->bo ? '-' : ' ' ); if (str_len(str) > w) str_sleft(str,w); if (menu_info->bo) str = ".-" + str + "-."; else str = " " + str + " "; con_out(x,y,str,menu_info->ti); y++; scroll.wrap = 1; scroll.set_min_max( 0, va->count()-1 ); scroll.set_pagesize( h ); scroll.go( 0 ); while(1) { for( z = 0; z < scroll.pagesize(); z++ ) { str = (scroll.page()+z >= va->count())? "~" : va->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 , (strncmp("--", str, 2) == 0)?'-':' '); if (str_len(str) > w) str = str_dot_reduce( str, w ); if (menu_info->bo) str = "| " + str + " |"; else str = " " + str + " "; con_out( x, y+z, str, ( scroll.page()+z != scroll.pos() ) ? menu_info->cn : menu_info->ch ); } if (menu_info->bo) { str = ""; str_pad( str, w+2, '-' ); str = "`" + str + "'"; con_out( x, y+scroll.pagesize(), str, menu_info->cn ); } ch = con_getch(); menu_info->ec = ch; if ( ch == KEY_UP ) scroll.up(); if ( ch == KEY_DOWN ) scroll.down(); if ( ch == KEY_NPAGE ) scroll.npage(); if ( ch == KEY_PPAGE ) scroll.ppage(); if ( ch == KEY_HOME ) scroll.home(); if ( ch == KEY_END ) scroll.end(); if ( ch == 27 || ch == 8 || ch == KEY_BACKSPACE ) { // exit on ESC or BS menu_info->ac = 0; return -1; } if ( ch < 0 || ch > 255 ) continue; //FIXME: unicode? if ( ch == 13 ) { if (strncmp("--", va->get(scroll.pos()), 2) != 0) // ako e "--" e separator { menu_info->ec = hots[scroll.pos()]; menu_info->ac = -1; return scroll.pos(); } } if ( menu_info->ac > -1 && ch == menu_info->ac ) { if (strncmp("--", va->get(scroll.pos()), 2) != 0) // ako e "--" e separator { menu_info->ec = hots[scroll.pos()]; menu_info->ac = -2; return scroll.pos(); } } z = str_find( hots, toupper(ch) ); if (z > -1) { menu_info->ec = hots[z]; menu_info->ac = -1; return z; } } menu_info->ac = -1; return -1; } int con_full_box( int x, int y, const char *title, VArray *va, ConMenuInfo *menu_info ) { ScrollPos scroll; scroll.wrap = 0; scroll.set_min_max( 0, va->count()-1 ); scroll.set_pagesize( con_max_y() - 3 ); /* one title and two status */ scroll.go( 0 ); char pos_str[32]; con_xy( 1, 1 ); con_puts( title, menu_info->ti ); con_ce( menu_info->ti ); while(4) { VString str; int z; for( z = 0; z < scroll.pagesize(); z++ ) { if ( scroll.page() + z < va->count() ) str = va->get( scroll.page() + z ); else str = "~"; 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( str, c ); con_ce( c ); } sprintf( pos_str, " %5d of %5d", scroll.pos()+1, scroll.max()+1 ); con_out( con_max_x() - 15, 1, pos_str, menu_info->ti ); int ch; switch( (ch = con_getch()) ) { case 27 : menu_info->ec = 27; return -1; break; case 8 : menu_info->ec = 8; return -1; break; case KEY_BACKSPACE : menu_info->ec = KEY_BACKSPACE; return -1; break; case 13 : menu_info->ec = 13; return scroll.pos(); break; case KEY_UP : scroll.up(); break; case KEY_DOWN : scroll.down(); break; case KEY_NPAGE : scroll.npage(); break; case KEY_PPAGE : scroll.ppage(); break; case KEY_HOME : scroll.home(); break; case KEY_END : scroll.end(); break; default: if ( tolower(ch) == tolower(menu_info->ac) ) { menu_info->ec = menu_info->ac; return -2; } break; } } } vfu-4.22/vslib/README0000644000175000017500000000210414145574047012622 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-4.22/vslib/vsuti.cpp0000644000175000017500000003532714145574047013635 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2020 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 ) { #ifdef _TARGET_UNIX_ if (!realpath( src, dest)) strcpy( dest, src ); #endif #ifdef _TARGET_GO32_ _fixpath( src, dest ); #endif return dest; } VString expand_path( const char* src ) { char temp[MAX_PATH]; VString dest = expand_path( src, temp ); 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-4.22/Makefile.am0000644000175000017500000000125214145574023012654 0ustar cadecadeSUBDIRS = yas-libs vslib vslib/t vfu packextrasdir=@docdir@ EXTRA_DIST = \ COPYING \ makefile \ README.MAC_OSX \ vfu.html \ build.sh \ build.docs \ THANKS.TO \ vfu.lsm \ build.netbsd \ FAQ \ mm.conf \ TODO \ vfu.pod \ COMPILE.NOTES \ HISTORY \ mm.proba.conf \ vfu.wiki \ CONFIG \ README \ INSTALL \ README.DOS \ XWINDOW.NOTES packextras_DATA=$(EXTRA_DIST) # Install the English man file and include it when `make dist` is used dist_man_MANS=vfu.1 # Install the configuration to $(sysconfdir) sysconf_DATA = vfu.conf ACLOCAL_AMFLAGS = --install -I build-aux/m4 vfu-4.22/.gitignore0000644000175000017500000000047514145574023012616 0ustar cadecade# build directory build # Created when autoreconf is run by the maintainer. This directory isn't needed # by developers autom4te.cache # These directories are intended to be inside the vfu-dist direcory # but are not under revision control within the vfu-dist directory /vfu/ /vslib/ /yascreen/ # Miscellaneous *~ vfu-4.22/vfu/0000755000175000017500000000000014151762154011421 5ustar cadecadevfu-4.22/vfu/Makefile.in0000644000175000017500000004221514145574042013472 0ustar cadecade# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : bin_PROGRAMS = vfu$(EXEEXT) subdir = vfu ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = \ $(top_srcdir)/build-aux/m4/ax_check_enable_debug.m4 \ $(top_srcdir)/build-aux/m4/ax_path_lib_pcre.m4 \ $(top_srcdir)/build-aux/m4/ax_require_defined.m4 \ $(top_srcdir)/build-aux/m4/ax_with_curses.m4 \ $(top_srcdir)/build-aux/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am_vfu_OBJECTS = see.$(OBJEXT) vfu.$(OBJEXT) vfuarc.$(OBJEXT) \ vfucopy.$(OBJEXT) vfudir.$(OBJEXT) vfufiles.$(OBJEXT) \ vfumenu.$(OBJEXT) vfuopt.$(OBJEXT) vfusys.$(OBJEXT) \ vfutools.$(OBJEXT) vfuuti.$(OBJEXT) vfuview.$(OBJEXT) vfu_OBJECTS = $(am_vfu_OBJECTS) vfu_LDADD = $(LDADD) vfu_DEPENDENCIES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(vfu_SOURCES) DIST_SOURCES = $(vfu_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CURSES_CFLAGS = @CURSES_CFLAGS@ CURSES_LIBS = @CURSES_LIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = -L$(top_builddir)/vslib @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE_CFLAGS = @PCRE_CFLAGS@ PCRE_LIBS = @PCRE_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__leading_dot = @am__leading_dot@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CXXFLAGS = -Wall -I$(top_srcdir)/vslib -I$(top_builddir) @PCRE_CFLAGS@ vfu_SOURCES = \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp LDADD = -lvscon -lvslib @PCRE_LIBS@ # These get packaged when 'make dist' is run. noinst_HEADERS = \ see.h \ vfucopy.h \ vfufiles.h \ vfumenu.h \ vfusetup.h \ vfutools.h \ vfuview.h \ vfuarc.h \ vfudir.h \ vfu.h \ vfuopt.h \ vfusys.h \ vfuuti.h all: all-am .SUFFIXES: .SUFFIXES: .cpp .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps vfu/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign --ignore-deps vfu/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) vfu$(EXEEXT): $(vfu_OBJECTS) $(vfu_DEPENDENCIES) $(EXTRA_vfu_DEPENDENCIES) @rm -f vfu$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(vfu_OBJECTS) $(vfu_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .cpp.o: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic distclean-tags \ distdir dvi dvi-am html html-am info info-am install \ install-am install-binPROGRAMS install-data install-data-am \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: vfu-4.22/vfu/vfutools.h0000644000175000017500000000112314145574042013450 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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-4.22/vfu/vfuuti.cpp0000644000175000017500000003314314145574042013453 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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" /*---------------------------------------------------------------------------*/ fsize_t file_st_size( struct stat* st ) { return st->st_size + ( st->st_size < 0 ) * ((uintmax_t)TYPE_MAXIMUM(off_t) - TYPE_MINIMUM(off_t) + 1); } 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 one = 0; int full = 0; // toggles #ifdef _TARGET_GO32_ int use_SFN = 0; #endif while( a_line[i] ) { if ( a_line[i] == '%' ) { switch( a_line[i+1] ) { #ifdef _TARGET_GO32_ case '_' : use_SFN = 1; break; #endif case 'r' : /* rescan files after */ case 'R' : a_options += "r"; break; // refresh all files after (readdir) case 'f' : /* file name */ case 'F' : case 'g' : case 'G' : one = 0; if ( a_line[i+1] == 'f' ) one = 1; if ( a_line[i+1] == 'F' ) one = 1; full = 0; if ( a_line[i+1] == 'F' ) full = 1; if ( a_line[i+1] == 'G' ) full = 1; // FIXME: 'one' is used to merge F and G options in the future int z; 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( work_mode == WM_ARCHIVE ) { s = files_list_get(z)->name(); } else if( full ) { files_list_get(z)->full_name(); } else { s = files_list_get(z)->name(); } if( !one ) { // need to list files, quote them... // FIXME: this should be optional for all one and !one if ( str_find( s, "'" ) == -1 ) { s = "'" + s +"' "; } else if ( str_find( s, "\"" ) == -1 ) { s = "\"" + s +"\" "; } else { s = ""; } } out += s; } break; case 'e' : /* name only */ case 'E' : /* extension only */ if ( a_line[i+1] == 'e' ) s = str_file_name( s ); else s = files_list_get(FLI)->ext(); out += 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; #ifdef _TARGET_GO32_ str_tr( s, "/", "\\" ); #endif out += s; break; case 'C' : /* startup dir */ s = startup_path; #ifdef _TARGET_GO32_ str_tr( s, "/", "\\" ); #endif out += s; break; case 'a' : /* Archive name */ out += archive_name; break; case 'A' : /* Archive path */ s = archive_path; #ifdef _TARGET_GO32_ str_tr( s, "/", "\\" ); #endif out += 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_getch() == 27) { say2( "Press ENTER to cancel or other key to continue..." ); int key = con_getch(); 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; } /*---------------------------------------------------------------------------*/ int vfu_ask( const char *prompt, const char *allowed ) { int ch = 1; say1( prompt ); while ( strchr( allowed, ch ) == NULL ) ch = con_getch(); return ch; } /*---------------------------------------------------------------------------*/ 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; if (timenow > tim + 6L * 30L * 24L * 60L * 60L /* old */ || timenow < tim - 60L * 60L) /* in the future */ { tfm = "%b %d %Y"; } 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 char* 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( 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, VString &s, int &pos ) { if ( __vfu_get_str_hist_id <= 0 ) return; if ( key != KEY_NPAGE && key != KEY_PPAGE ) return; con_chide(); int z = vfu_hist_menu( 5, 5, "Line History", __vfu_get_str_hist_id ); con_cshow(); if ( z == -1 ) return; s = mb.get(z) + 2; str_cut_spc( s ); pos = str_len( s ); } 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() - 3 - 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 ); char t[1024]; /* FIXME: can be overflowed? */ strcpy( t, target ); int r = TextInput( x, y, "", len, len, t, vfu_get_str_history ); target = t; say1( "" ); say2( "" ); __vfu_get_str_hist_id = 0; if( r ) vfu_hist_add( hist_id, target ); return ( r != 0 ); } /*---------------------------------------------------------------------------*/ // FIXME: TODO: enew function for tmp dir name etc. fname_t vfu_temp_filename; const char* vfu_temp() { strcpy( vfu_temp_filename, tmp_path + "vfu.XXXXXX" ); mkstemp( vfu_temp_filename ); unlink( vfu_temp_filename ); return vfu_temp_filename; } /*---------------------------------------------------------------------------*/ 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] ); } /*###########################################################################*/ /* eof vfuuti.cpp */ vfu-4.22/vfu/makefile0000644000175000017500000003271714145576401013134 0ustar cadecade ### MAKEMAKE STARTS HERE ####################################################### ### Created by makemake.pl on Fri Nov 19 03:25:01 2021 ######################### ### GLOBAL TARGETS ############################################################# default: mm_update vfu re: mm_update rebuild li: mm_update link all: mm_update vfu vfu.yas vfu-debug vfu.yas-debug clean: mm_update clean-vfu clean-vfu.yas clean-vfu-debug clean-vfu.yas-debug rebuild: mm_update rebuild-vfu rebuild-vfu.yas rebuild-vfu-debug rebuild-vfu.yas-debug link: mm_update link-vfu link-vfu.yas link-vfu-debug link-vfu.yas-debug ### GLOBAL (AND USER) DEFS ##################################################### AR ?= ar LD = $(CXX) MKDIR = mkdir -p RANLIB ?= ranlib RMDIR = rm -rf RMFILE = rm -f SRC = *.c *.cpp *.cc *.cxx ### TARGET 1: vfu ############################################################## CC_1 = $(CXX) LD_1 = $(LD) AR_1 = $(AR) rv RANLIB_1 = $(RANLIB) CCFLAGS_1 = -I../vstring -I../vstring/pcre2 -I../vslib -I/usr/include/ncurses -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_1 = -L../vstring -L../vstring/pcre2 -L../vslib -lvstring -lvslib -lvscon -lpcre2 -lncurses $(LDFLAGS) $(LDDEF) DEPFLAGS_1 = ARFLAGS_1 = TARGET_1 = vfu ### SOURCES FOR TARGET 1: vfu ################################################## SRC_1= \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp \ #### OBJECTS FOR TARGET 1: vfu ################################################# OBJ_1= \ .OBJ.vfu/see.o \ .OBJ.vfu/vfu.o \ .OBJ.vfu/vfuarc.o \ .OBJ.vfu/vfucopy.o \ .OBJ.vfu/vfudir.o \ .OBJ.vfu/vfufiles.o \ .OBJ.vfu/vfumenu.o \ .OBJ.vfu/vfuopt.o \ .OBJ.vfu/vfusys.o \ .OBJ.vfu/vfutools.o \ .OBJ.vfu/vfuuti.o \ .OBJ.vfu/vfuview.o \ ### TARGET DEFINITION FOR TARGET 1: vfu ######################################## .OBJ.vfu: $(MKDIR) .OBJ.vfu vfu: .OBJ.vfu $(OBJ_1) $(LD_1) $(OBJ_1) $(LDFLAGS_1) -o $(TARGET_1) clean-vfu: $(RMFILE) $(TARGET_1) $(RMDIR) .OBJ.vfu rebuild-vfu: clean-vfu vfu re-vfu: rebuild-vfu link-vfu: .OBJ.vfu $(OBJ_1) $(RMFILE) vfu $(LD_1) $(OBJ_1) $(LDFLAGS_1) -o $(TARGET_1) ### TARGET OBJECTS FOR TARGET 1: vfu ########################################### .OBJ.vfu/see.o: see.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c see.cpp -o .OBJ.vfu/see.o .OBJ.vfu/vfu.o: vfu.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfu.cpp -o .OBJ.vfu/vfu.o .OBJ.vfu/vfuarc.o: vfuarc.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfuarc.cpp -o .OBJ.vfu/vfuarc.o .OBJ.vfu/vfucopy.o: vfucopy.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfucopy.cpp -o .OBJ.vfu/vfucopy.o .OBJ.vfu/vfudir.o: vfudir.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfudir.cpp -o .OBJ.vfu/vfudir.o .OBJ.vfu/vfufiles.o: vfufiles.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfufiles.cpp -o .OBJ.vfu/vfufiles.o .OBJ.vfu/vfumenu.o: vfumenu.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfumenu.cpp -o .OBJ.vfu/vfumenu.o .OBJ.vfu/vfuopt.o: vfuopt.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfuopt.cpp -o .OBJ.vfu/vfuopt.o .OBJ.vfu/vfusys.o: vfusys.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfusys.cpp -o .OBJ.vfu/vfusys.o .OBJ.vfu/vfutools.o: vfutools.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfutools.cpp -o .OBJ.vfu/vfutools.o .OBJ.vfu/vfuuti.o: vfuuti.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfuuti.cpp -o .OBJ.vfu/vfuuti.o .OBJ.vfu/vfuview.o: vfuview.cpp $(CC_1) $(CFLAGS_1) $(CCFLAGS_1) -c vfuview.cpp -o .OBJ.vfu/vfuview.o ### TARGET 2: vfu.yas ########################################################## CC_2 = $(CXX) LD_2 = $(LD) AR_2 = $(AR) rv RANLIB_2 = $(RANLIB) CCFLAGS_2 = -I../vstring -I../vstring/pcre2 -I../vslib -I../yascreen -DUSE_YASCREEN -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_2 = -L../vstring -L../vstring/pcre2 -L../vslib -L../yascreen -lvstring -lvslib -lvscony -lpcre2 ../yascreen/libyascreen.a -lrt $(LDFLAGS) $(LDDEF) DEPFLAGS_2 = ARFLAGS_2 = TARGET_2 = vfu.yas ### SOURCES FOR TARGET 2: vfu.yas ############################################## SRC_2= \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp \ #### OBJECTS FOR TARGET 2: vfu.yas ############################################# OBJ_2= \ .OBJ.vfu.yas/see.o \ .OBJ.vfu.yas/vfu.o \ .OBJ.vfu.yas/vfuarc.o \ .OBJ.vfu.yas/vfucopy.o \ .OBJ.vfu.yas/vfudir.o \ .OBJ.vfu.yas/vfufiles.o \ .OBJ.vfu.yas/vfumenu.o \ .OBJ.vfu.yas/vfuopt.o \ .OBJ.vfu.yas/vfusys.o \ .OBJ.vfu.yas/vfutools.o \ .OBJ.vfu.yas/vfuuti.o \ .OBJ.vfu.yas/vfuview.o \ ### TARGET DEFINITION FOR TARGET 2: vfu.yas #################################### .OBJ.vfu.yas: $(MKDIR) .OBJ.vfu.yas vfu.yas: .OBJ.vfu.yas $(OBJ_2) $(LD_2) $(OBJ_2) $(LDFLAGS_2) -o $(TARGET_2) clean-vfu.yas: $(RMFILE) $(TARGET_2) $(RMDIR) .OBJ.vfu.yas rebuild-vfu.yas: clean-vfu.yas vfu.yas re-vfu.yas: rebuild-vfu.yas link-vfu.yas: .OBJ.vfu.yas $(OBJ_2) $(RMFILE) vfu.yas $(LD_2) $(OBJ_2) $(LDFLAGS_2) -o $(TARGET_2) ### TARGET OBJECTS FOR TARGET 2: vfu.yas ####################################### .OBJ.vfu.yas/see.o: see.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c see.cpp -o .OBJ.vfu.yas/see.o .OBJ.vfu.yas/vfu.o: vfu.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfu.cpp -o .OBJ.vfu.yas/vfu.o .OBJ.vfu.yas/vfuarc.o: vfuarc.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfuarc.cpp -o .OBJ.vfu.yas/vfuarc.o .OBJ.vfu.yas/vfucopy.o: vfucopy.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfucopy.cpp -o .OBJ.vfu.yas/vfucopy.o .OBJ.vfu.yas/vfudir.o: vfudir.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfudir.cpp -o .OBJ.vfu.yas/vfudir.o .OBJ.vfu.yas/vfufiles.o: vfufiles.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfufiles.cpp -o .OBJ.vfu.yas/vfufiles.o .OBJ.vfu.yas/vfumenu.o: vfumenu.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfumenu.cpp -o .OBJ.vfu.yas/vfumenu.o .OBJ.vfu.yas/vfuopt.o: vfuopt.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfuopt.cpp -o .OBJ.vfu.yas/vfuopt.o .OBJ.vfu.yas/vfusys.o: vfusys.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfusys.cpp -o .OBJ.vfu.yas/vfusys.o .OBJ.vfu.yas/vfutools.o: vfutools.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfutools.cpp -o .OBJ.vfu.yas/vfutools.o .OBJ.vfu.yas/vfuuti.o: vfuuti.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfuuti.cpp -o .OBJ.vfu.yas/vfuuti.o .OBJ.vfu.yas/vfuview.o: vfuview.cpp $(CC_2) $(CFLAGS_2) $(CCFLAGS_2) -c vfuview.cpp -o .OBJ.vfu.yas/vfuview.o ### TARGET 3: vfu-debug ######################################################## CC_3 = $(CXX) LD_3 = $(LD) AR_3 = $(AR) rv RANLIB_3 = $(RANLIB) CCFLAGS_3 = -I../vstring -I../vstring/pcre2 -I../vslib -I/usr/include/ncurses -O0 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_3 = -L../vstring -L../vstring/pcre2 -L../vslib -lvstring -lvslib -lvscon -lpcre2 -lncurses -g $(LDFLAGS) $(LDDEF) DEPFLAGS_3 = ARFLAGS_3 = TARGET_3 = vfu-debug ### SOURCES FOR TARGET 3: vfu-debug ############################################ SRC_3= \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp \ #### OBJECTS FOR TARGET 3: vfu-debug ########################################### OBJ_3= \ .OBJ.vfu-debug/see.o \ .OBJ.vfu-debug/vfu.o \ .OBJ.vfu-debug/vfuarc.o \ .OBJ.vfu-debug/vfucopy.o \ .OBJ.vfu-debug/vfudir.o \ .OBJ.vfu-debug/vfufiles.o \ .OBJ.vfu-debug/vfumenu.o \ .OBJ.vfu-debug/vfuopt.o \ .OBJ.vfu-debug/vfusys.o \ .OBJ.vfu-debug/vfutools.o \ .OBJ.vfu-debug/vfuuti.o \ .OBJ.vfu-debug/vfuview.o \ ### TARGET DEFINITION FOR TARGET 3: vfu-debug ################################## .OBJ.vfu-debug: $(MKDIR) .OBJ.vfu-debug vfu-debug: .OBJ.vfu-debug $(OBJ_3) $(LD_3) $(OBJ_3) $(LDFLAGS_3) -o $(TARGET_3) clean-vfu-debug: $(RMFILE) $(TARGET_3) $(RMDIR) .OBJ.vfu-debug rebuild-vfu-debug: clean-vfu-debug vfu-debug re-vfu-debug: rebuild-vfu-debug link-vfu-debug: .OBJ.vfu-debug $(OBJ_3) $(RMFILE) vfu-debug $(LD_3) $(OBJ_3) $(LDFLAGS_3) -o $(TARGET_3) ### TARGET OBJECTS FOR TARGET 3: vfu-debug ##################################### .OBJ.vfu-debug/see.o: see.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c see.cpp -o .OBJ.vfu-debug/see.o .OBJ.vfu-debug/vfu.o: vfu.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfu.cpp -o .OBJ.vfu-debug/vfu.o .OBJ.vfu-debug/vfuarc.o: vfuarc.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfuarc.cpp -o .OBJ.vfu-debug/vfuarc.o .OBJ.vfu-debug/vfucopy.o: vfucopy.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfucopy.cpp -o .OBJ.vfu-debug/vfucopy.o .OBJ.vfu-debug/vfudir.o: vfudir.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfudir.cpp -o .OBJ.vfu-debug/vfudir.o .OBJ.vfu-debug/vfufiles.o: vfufiles.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfufiles.cpp -o .OBJ.vfu-debug/vfufiles.o .OBJ.vfu-debug/vfumenu.o: vfumenu.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfumenu.cpp -o .OBJ.vfu-debug/vfumenu.o .OBJ.vfu-debug/vfuopt.o: vfuopt.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfuopt.cpp -o .OBJ.vfu-debug/vfuopt.o .OBJ.vfu-debug/vfusys.o: vfusys.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfusys.cpp -o .OBJ.vfu-debug/vfusys.o .OBJ.vfu-debug/vfutools.o: vfutools.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfutools.cpp -o .OBJ.vfu-debug/vfutools.o .OBJ.vfu-debug/vfuuti.o: vfuuti.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfuuti.cpp -o .OBJ.vfu-debug/vfuuti.o .OBJ.vfu-debug/vfuview.o: vfuview.cpp $(CC_3) $(CFLAGS_3) $(CCFLAGS_3) -c vfuview.cpp -o .OBJ.vfu-debug/vfuview.o ### TARGET 4: vfu.yas-debug #################################################### CC_4 = $(CXX) LD_4 = $(LD) AR_4 = $(AR) rv RANLIB_4 = $(RANLIB) CCFLAGS_4 = -I../vstring -I../vstring/pcre2 -I../vslib -I../yascreen -DUSE_YASCREEN -O0 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS_4 = -L../vstring -L../vstring/pcre2 -L../vslib -L../yascreen -lvstring -lvslib -lvscony -lpcre2 ../yascreen/libyascreen.a -lrt -g $(LDFLAGS) $(LDDEF) DEPFLAGS_4 = ARFLAGS_4 = TARGET_4 = vfu.yas-debug ### SOURCES FOR TARGET 4: vfu.yas-debug ######################################## SRC_4= \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp \ #### OBJECTS FOR TARGET 4: vfu.yas-debug ####################################### OBJ_4= \ .OBJ.vfu.yas-debug/see.o \ .OBJ.vfu.yas-debug/vfu.o \ .OBJ.vfu.yas-debug/vfuarc.o \ .OBJ.vfu.yas-debug/vfucopy.o \ .OBJ.vfu.yas-debug/vfudir.o \ .OBJ.vfu.yas-debug/vfufiles.o \ .OBJ.vfu.yas-debug/vfumenu.o \ .OBJ.vfu.yas-debug/vfuopt.o \ .OBJ.vfu.yas-debug/vfusys.o \ .OBJ.vfu.yas-debug/vfutools.o \ .OBJ.vfu.yas-debug/vfuuti.o \ .OBJ.vfu.yas-debug/vfuview.o \ ### TARGET DEFINITION FOR TARGET 4: vfu.yas-debug ############################## .OBJ.vfu.yas-debug: $(MKDIR) .OBJ.vfu.yas-debug vfu.yas-debug: .OBJ.vfu.yas-debug $(OBJ_4) $(LD_4) $(OBJ_4) $(LDFLAGS_4) -o $(TARGET_4) clean-vfu.yas-debug: $(RMFILE) $(TARGET_4) $(RMDIR) .OBJ.vfu.yas-debug rebuild-vfu.yas-debug: clean-vfu.yas-debug vfu.yas-debug re-vfu.yas-debug: rebuild-vfu.yas-debug link-vfu.yas-debug: .OBJ.vfu.yas-debug $(OBJ_4) $(RMFILE) vfu.yas-debug $(LD_4) $(OBJ_4) $(LDFLAGS_4) -o $(TARGET_4) ### TARGET OBJECTS FOR TARGET 4: vfu.yas-debug ################################# .OBJ.vfu.yas-debug/see.o: see.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c see.cpp -o .OBJ.vfu.yas-debug/see.o .OBJ.vfu.yas-debug/vfu.o: vfu.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfu.cpp -o .OBJ.vfu.yas-debug/vfu.o .OBJ.vfu.yas-debug/vfuarc.o: vfuarc.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfuarc.cpp -o .OBJ.vfu.yas-debug/vfuarc.o .OBJ.vfu.yas-debug/vfucopy.o: vfucopy.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfucopy.cpp -o .OBJ.vfu.yas-debug/vfucopy.o .OBJ.vfu.yas-debug/vfudir.o: vfudir.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfudir.cpp -o .OBJ.vfu.yas-debug/vfudir.o .OBJ.vfu.yas-debug/vfufiles.o: vfufiles.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfufiles.cpp -o .OBJ.vfu.yas-debug/vfufiles.o .OBJ.vfu.yas-debug/vfumenu.o: vfumenu.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfumenu.cpp -o .OBJ.vfu.yas-debug/vfumenu.o .OBJ.vfu.yas-debug/vfuopt.o: vfuopt.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfuopt.cpp -o .OBJ.vfu.yas-debug/vfuopt.o .OBJ.vfu.yas-debug/vfusys.o: vfusys.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfusys.cpp -o .OBJ.vfu.yas-debug/vfusys.o .OBJ.vfu.yas-debug/vfutools.o: vfutools.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfutools.cpp -o .OBJ.vfu.yas-debug/vfutools.o .OBJ.vfu.yas-debug/vfuuti.o: vfuuti.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfuuti.cpp -o .OBJ.vfu.yas-debug/vfuuti.o .OBJ.vfu.yas-debug/vfuview.o: vfuview.cpp $(CC_4) $(CFLAGS_4) $(CCFLAGS_4) -c vfuview.cpp -o .OBJ.vfu.yas-debug/vfuview.o mm_update: ### MAKEMAKE ENDS HERE ######################################################### vfu-4.22/vfu/vfudir.cpp0000644000175000017500000007131514145574042013433 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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; /*###########################################################################*/ 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); } } /* FIXME: must call sayX() at the end to clear... */ int vfu_get_dir_name( const char *prompt, VString &target, 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 ch = 0; int insert = 1; int firsthit = 1; pos = str_len( target ); //------------------------------------------------------------------ con_cshow(); say2( target, firsthit ? cINPUT2 : cINPUT ); while(1) { int mx = con_max_x() - 1; VString 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, '<' ); if ( str_len( target ) - page > mx ) str_set_ch( target_out, mx-1, '>' ); say2( target_out, firsthit ? cINPUT2 : cINPUT ); con_xy( pos-page+1, con_max_y() ); if (ch == 0) ch = con_getch(); if (ch == '\\') ch = '/'; /* dos hack :)) */ if ( ch == '/' && str_find( target, '/' ) == -1 && target[0] == '~' ) { target = tilde_expand( target ); str_fix_path( target ); pos = str_len( target ); ch = 0; } if ( ( ch == 8 || ch == KEY_BACKSPACE ) && pos > 0 ) { pos--; str_del( target, pos, 1 ); } else if (ch == KEY_CTRL_A && str_len( target ) > 0) { int z = str_len( target )-1; if ( str_get_ch(target, z) == '/' ) z--; while ( z > 0 && str_get_ch(target,z) != '/' ) z--; z++; str_sleft(target,z); pos = z; } else if ( ch == 9 && str_len( target ) > 0 ) { int z; dir_list.undef(); VString dmain; /* main/base path */ VString dtail; /* item that should be expanded/glob */ dmain = str_file_path( target ); dtail = 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 ); } */ __glob_gdn( dmain, dtail, dir_list, type ); z = dir_list.count()-1; 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 tmp1; if( dtail != str_copy( tmp1, dir_list[li], 0, str_len( dtail ) ) ) continue; xm = li; break; } int mc = 0; /* match count */ int mi = 0; /* 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( mi >= ll ) break; } target.setn( dmain + dir_list[xm], str_len( dmain ) + mi ); pos = str_len( target ); say2( target, cINPUT ); con_xy( pos+1, con_max_y() ); vfu_beep(); ch = con_getch(); if ( ch != 9 ) { dir_list.undef(); continue; } dir_list.sort(); con_chide(); z = vfu_menu_box( 10, 5, "Complete...", &dir_list ); con_cshow(); ch = 0; } else ch = 0; if ( z != -1 ) { while( str_len( target ) > 0 && target[-1] != '/' ) str_chop( target ); target += dir_list[z]; } pos = str_len( target ); dir_list.undef(); if (ch != 0) continue; } else { /* no match found -- cannot complete */ vfu_beep(); } } else if (ch == 13) { res = 1; break; } else if (ch == 27) { target = ""; res = 0; break; } if (ch == KEY_CTRL_U) { target = ""; pos = 0; } else if (ch == KEY_CTRL_X) { fname_t t; if ( target[0] == '~' ) target = tilde_expand( target ); expand_path( target, t ); str_fix_path( t ); target = t; pos = str_len( target ); } else if ( ch >= 32 && ch <= 255 && ch != KEY_BACKSPACE ) // && pos < 70) { if (firsthit) { target = ""; pos = 0; } if (!insert) str_del( target, pos, 1 ); str_ins_ch( target, pos, ch ); pos++; } else if( ch == KEY_LEFT ) { if (pos > 0) pos--; } else if( ch == KEY_RIGHT ) { if (pos < str_len( target )) pos++; } else if ( ch == KEY_IC ) insert = !insert; else if ( ch == KEY_HOME ) pos = 0; else if ( ch == KEY_END ) pos = str_len(target); else if ( ch == KEY_DC && pos < str_len(target) ) str_del( target, pos, 1 ); else if ( ch == KEY_NPAGE || ch == KEY_PPAGE ) { con_chide(); int zz = vfu_hist_menu( 5, 5, ( ch == KEY_PPAGE ) ? "Dir Entry History" : "ChDir History", ( ch == KEY_PPAGE ) ? HID_GETDIR : HID_CHDIR ); con_cshow(); if (zz != -1) { const char* pc = vfu_hist_get( ( ch == KEY_PPAGE ) ? HID_GETDIR : HID_CHDIR, zz ); if ( pc ) { target = pc; pos = str_len( target ); } } } ch = 0; firsthit = 0; } con_chide(); //------------------------------------------------------------------ str_cut_spc( target ); if ( res == 1 && target[0] == '~' ) { target = tilde_expand( target ); str_fix_path( target ); } /* if ( target.len() > 0 ) { // well this tmp is kind of s... ama k'vo da pravi chovek :) // FIXME: dos version? if ( __ExpandGetDirName && target[0] != '/' #ifdef _TARGET_GO32_ && !( target[1] == ':' && target[2] == '/' ) #endif ) target = CPath + target; StrFixPath( target ); // add trailing slash if not exist } */ /* #ifdef _TARGET_UNIX_ leaveok(stdscr, TRUE); #endif */ if ( res == 1 && str_len( target ) > 0 && should_exist && type == 'D' && !dir_exist( target )) { vfu_beep(); int ch = tolower( vfu_ask( "Directory does not exist! Create? " "( ENTER=Yes, ESC=cancel )", "\033\rcC" )); if ( ch == 27 ) { res = 0; target = ""; } else if ( ch == 13 ) if (make_path( target )) { if(tolower( vfu_ask( "Cannot create path! ( ESC=cancel, C=continue-anyway )", "\033Cc" )) == 27) { res = 0; target = ""; } } } say1(" "); say2(" "); if ( str_len( target ) > 0) { if( file_is_dir( target ) ) str_fix_path( target ); vfu_hist_add( HID_GETDIR, target ); } str_cut_spc( 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, "Change dir to..." ); if (z > -1) target = mb.get(z); else return; } else if (z == 1) target = mb.get(0); } VString str = target; str_cut_spc( str ); #ifdef _TARGET_GO32_ if ( str[0] == '/' ) { str_ins_ch( str, 0, ':' ); str_ins_ch( str, 0, work_path[0] ); } else if ( str[1] == ':' && str[2] == 0 ) /* c: d: e: */ { expand_path( str, t ); str = t; } if ( str[1] == ':' && str[2] == '/' ) #else /* _TARGET_GO32_ -> i.e. _TARGET_UNIX_ here*/ if (str[0] == '/') #endif /* _TARGET_GO32_ */ { /* 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, "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; #ifdef _TARGET_GO32_ dosstat(dir, &st); #else stat(new_name, &st); #endif 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 += file_st_size( &st ); } } closedir(dir); /* show some progress :) */ say2( str_dot_reduce( path, con_max_x()-1 ) ); return size; } void tree_rebuild() { #ifdef _TARGET_GO32_ // we do need only files sizes -- so the other stuff under dos is unneeded :) _djstat_flags = _STAT_INODE | _STAT_EXEC_EXT | _STAT_EXEC_MAGIC | _STAT_EXEC_MAGIC | _STAT_DIRSIZE | _STAT_ROOT_TIME | _STAT_WRITEBIT; // _djstat_flags = 0; #endif dir_tree.undef(); size_cache.undef(); say1( "Rebuilding tree..." ); __tree_rebuild_process( "/" ); tree_fix(); #ifdef _TARGET_GO32_ _djstat_flags = 0; #endif 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 ); 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; BSet set; /* used for searching */ set.set_range1( 'a', 'z' ); set.set_range1( 'A', 'Z' ); set.set_range1( '0', '9' ); set.set_str1( "._-~" ); set.set_str1( "?*>[]" ); ScrollPos scroll; scroll.set_min_max( 0, dir_tree.count()-1 ); scroll.set_pagesize( FPS + 2 ); 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 != KEY_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( set.in( key ) || key == 8 || key == KEY_BACKSPACE || key == 9 ) { if ( key == 8 || key == KEY_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 KEY_UP : scroll.up(); break; case KEY_DOWN : scroll.down(); break; case KEY_PPAGE : scroll.ppage(); break; case KEY_NPAGE : scroll.npage(); break; case KEY_HOME : scroll.home(); break; case KEY_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 KEY_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_STR "15" #define SIZE_CACHE_OFFSET_CLEAN (SIZE_CACHE_OFFSET+8+1) void size_cache_load() { size_cache.undef(); 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(); } void size_cache_save() { size_cache.fsave( filename_size_cache ); } int size_cache_cmp( const char* s1, const char* s2 ) { return strcmp( s1 + SIZE_CACHE_OFFSET, s2 + SIZE_CACHE_OFFSET ); } VString size_cache_compose_key( const char *s, fsize_t size ) { const char *ps; fname_t ss; expand_path( s, ss ); 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 ) 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( 0, size_cache_cmp ); } } // 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 ) { size_cache.push( size_cache_compose_key( s, size ) ); } void size_cache_clean( const char *s ) { VString str = size_cache_compose_key( s, 0 ); size_t qc = str_len( str ); str_trim_left( str, SIZE_CACHE_OFFSET_CLEAN ); int sl = str_len( str ); int z = 0; while( z < size_cache.count() ) { const char* ps = size_cache[z].data() + SIZE_CACHE_OFFSET_CLEAN; if ( ( strlen( size_cache[z] ) >= qc && strncmp( ps, str, sl ) == 0 && (ps[sl] == '/' || ps[sl] == 0) ) || ( size_cache[z][SIZE_CACHE_OFFSET_CLEAN] != '|' ) ) size_cache.del( z ); else z++; } } void size_cache_sort() { size_cache.sort( 0, size_cache_cmp ); } /*###########################################################################*/ /* 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 #ifdef _TARGET_GO32_ + 2; // to cut `d:' chars len #endif ; 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, VArray *va ) { 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, "\\", "/" ); va->push( str ); } } return va->count(); } /*###########################################################################*/ fsize_t __dir_size_process( const char* path, int mode, dev_t src_dev = 0 ) { 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) ); #ifdef _TARGET_GO32_ dosstat(dir, &st); #else stat(new_name, &st); #endif int is_dir = int(S_ISDIR(st.st_mode)); if ( is_link && ! ( mode & DIR_SIZE_FOLLOWSYMLINKS ) ) continue; /* skip links */ if ( is_dir ) { if ( src_dev && src_dev != st.st_dev ) continue; strcat( new_name, "/" ); fsize_t dir_size = __dir_size_process( new_name, mode ); if ( dir_size == -1 ) { closedir(dir); return -1; } else { size += dir_size; size_cache_append( new_name, dir_size ); } } else size += file_st_size( &st ); } 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 ) { fname_t t; expand_path( s, t ); //size_cache_clean( t ); str_fix_path( t ); size_cache_clean( t ); 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( t, mode, src_dev ); size_cache_set( t, size, sort ); return size; } /*###########################################################################*/ /* eof vfudir.cpp */ vfu-4.22/vfu/vfuview.cpp0000644000175000017500000002752614145574042013634 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 -- option */ /* 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 #ifdef _TARGET_GO32_ /* under dos/windows file names are not case sensitive */ str_low( str ); #endif 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 ); con_out( con_max_x() - 13, 3, t, cHEADER ); } /*#######################################################################*/ void vfu_drop_all_views() { int z = 0; for( z = 0; z < files_list_count(); z++ ) files_list_get(z)->drop_view(); do_draw += 1; } /*#######################################################################*/ void vfu_draw( int n ) { if ( n < FLP || n > FLP + FPS ) 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); } con_out( 1, n - FLP + 4, view, c ); // con_ce( c ); } /*#######################################################################*/ extern const char *FTIMETYPE[]; /* in vfuopt.cpp */ void vfu_redraw() /* redraw file list and header */ { fname_t t; VString str; str = "Mask: "; str += files_mask; con_out(1,1,str,cINFO); con_ce(cINFO); if ( work_mode == WM_ARCHIVE ) con_out( con_max_x()-34, 1, " [-ARCHIVE-] ", cWARNING ); con_out(con_max_x()-17,1,"Press H for help",cINFO); 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 ); 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 + ")"; 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_owner ) spos += sprintf( spos, " OWNER " ); if ( opt.f_group ) spos += sprintf( spos, " GROUP " ); if ( opt.f_time ) spos += sprintf( spos, "%s TiME ", FTIMETYPE[opt.f_time_type] ); 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, "TP" ); tag_mark_pos = strlen( t ); sel_mark_pos = tag_mark_pos + 2; /* spos += */ sprintf( spos, " #NAME %s", opt.long_name_view ? "( long name view )" : "" ); str_pad( t, - con_max_x() ); str_sleft( t, con_max_x() ); con_out(1,3, t, cHEADER ); show_pos( FLI+1, files_list_count() ); int z; for ( z = 0; z < FPS; z++ ) { ASSERT( FLP + z >= 0 ); if ( FLP + z >= files_list_count() || files_list_is_empty( FLP + z ) ) { con_out( 1, z + 4, "~", cPLAIN ); con_ce( cPLAIN ); } else vfu_draw( FLP + z ); } if ( files_list_count() <= 0 ) 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 + "]"; sprintf( tmp, " %s.%s@%s ", user_id_str.data(), group_id_str.data(), host_name_str.data() ); s2 += tmp; str_pad( s1, - con_max_x() ); str_pad( s2, - con_max_x() ); con_out( 1, con_max_y()-3, s1, cINFO2 ); 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() - FPS && 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() ) FGO( z ); } else FGO( 0 ); vfu_nav_update_pos(); do_draw = 1; /* if ( files_list_count() == 0 ) return; if ( FLI == 0 ) return; FGO( 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 ) FGO( z ); } else FGO( files_list_count() - 1 ); vfu_nav_update_pos(); do_draw = 1; /* if ( files_list_count() == 0 ) return; if ( FLI >= files_list_count() - 1 ) return; FGO( 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 ) FGO( 0 ); if ( files_list_count() == 0 ) FGO( 0 ); if ( files_list_count() > 0 && FLI > files_list_count() - 1 ) FGO( files_list_count() - 1 ); } /* eof vfuview.cpp */ vfu-4.22/vfu/vfu.h0000644000175000017500000002231314145574042012373 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #if defined(__OpenBSD__) #include #else #include #endif #include #else #include #endif #include "vfusetup.h" #include "vfusys.h" // for attrs_t /*############################################ COMPATIBILITY DEF's #####*/ #ifdef _TARGET_GO32_ #define S_ISLNK( p ) 0 #define S_ISSOCK( p ) 0 #define lstat(n,p) memset(p,0,sizeof(p)) #define cuserid(x) 0 #define FNMATCH_FLAGS FNM_CASEFOLD #define FNCASE 0 #define PATH_DELIMITER ";" #endif #ifdef _TARGET_UNIX_ #define FNMATCH_FLAGS 0 #define FNCASE 1 #define PATH_DELIMITER ":" #endif #define FNMATCH(p,s) fnmatch((p),(s),FNMATCH_FLAGS) #define FNMATCH_NC(p,s) fnmatch((p),(s),FNM_CASEFOLD) #define FNMATCH_OC(p,s,n) fnmatch((p),(s),(n)?FNM_CASEFOLD:FNMATCH_FLAGS) #ifdef _TARGET_GO32_ #define pathcmp strcasecmp #define pathncmp strncasecmp #else #define pathcmp strcmp #define pathncmp strncmp #endif 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 { char* _name; /* name with extension ( ref. _fname ) */ char* _name_ext; /* extension ( ref. _fname ) */ char* _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; char* _view; /* up to screen width */ int _color; /* view colors */ /* !!! this is used when full name required */ /* and this is not thread-safe :) */ static char _full_name[MAX_PATH]; 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() { ASSERT(_name); return (const char*)_name; } const char* name_ext() { ASSERT(_name_ext); return (const char*)_name_ext; } const char* ext() { ASSERT(_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 FPS (file_list_index.pagesize()) #define FGO(n) (file_list_index.go(n)) /* 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 VArray file_find_results; // filefind results extern VArray path_bookmarks; /*######################################################################*/ extern VArray user_externals; extern VArray history; extern VArray see_filters; extern VArray panelizers; extern VArray 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 ); /* 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( int ch ); /* 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_directories_sizes( int a_which_one ); void vfu_change_file_mask( const char* a_new_mask ); void bookmark_goto( int a_n ); 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( int key, const char* ext, const char* type, VString *shell_line ); void vfu_user_external_exec( int a_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( int ); 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-4.22/vfu/vfudir.h0000644000175000017500000000372214145574042013075 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _VFUDIR_H_ #define _VFUDIR_H_ #include "vfu.h" extern VArray size_cache; /*###########################################################################*/ 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, VArray *va ); 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 ); void size_cache_clean( const char *s ); void size_cache_sort(); /*###########################################################################*/ #define DIR_SIZE_NORMAL 0 #define DIR_SIZE_FOLLOWSYMLINKS 2 #define DIR_SIZE_SAMEDEVONLY 4 fsize_t vfu_dir_size( const char *s, int sort = 1, int mode = DIR_SIZE_NORMAL ); #endif //_VFUDRI_H_ vfu-4.22/vfu/Makefile.am0000644000175000017500000000132614145574042013457 0ustar cadecadebin_PROGRAMS = vfu AM_CXXFLAGS = -Wall -I$(top_srcdir)/vslib -I$(top_builddir) @PCRE_CFLAGS@ vfu_SOURCES = \ see.cpp \ vfu.cpp \ vfuarc.cpp \ vfucopy.cpp \ vfudir.cpp \ vfufiles.cpp \ vfumenu.cpp \ vfuopt.cpp \ vfusys.cpp \ vfutools.cpp \ vfuuti.cpp \ vfuview.cpp DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" LDFLAGS = -L$(top_builddir)/vslib @LDFLAGS@ LDADD = -lvscon -lvslib @PCRE_LIBS@ # These get packaged when 'make dist' is run. noinst_HEADERS = \ see.h \ vfucopy.h \ vfufiles.h \ vfumenu.h \ vfusetup.h \ vfutools.h \ vfuview.h \ vfuarc.h \ vfudir.h \ vfu.h \ vfuopt.h \ vfusys.h \ vfuuti.h vfu-4.22/vfu/vfumenu.cpp0000644000175000017500000000255414145574042013620 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 char *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 char *title, VArray *va ) { 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, va, 0, &menu_box_info ); vfu_redraw(); return z; } int vfu_menu_box( const char* title, const char* menustr, int row ) { char t[256]; mb.undef(); VString str = menustr; while( str_len(str) ) { str_word(str,",", t); mb.push(t); } if ( row == -1 ) row = con_max_y() - 5 - mb.count(); return vfu_menu_box( 50, row, title, &mb ); } // eof vfumenu.cpp vfu-4.22/vfu/.gitignore0000644000175000017500000000006614145574042013413 0ustar cadecade.OBJ* vfu vfu-debug vfu.yas* autom4te.cache build *~ vfu-4.22/vfu/vfucopy.cpp0000644000175000017500000010157514145574042013631 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 ) { #ifdef _TARGET_GO32_ fname_t _f1; fname_t _f2; _fixpath( src, _f1 ); _fixpath( dst, _f2 ); ASSERT( _f1[1] == ':' && _f2[1] == ':' ); return ( strcasecmp( _f1, _f2 ) != 0 ); #else 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 */ #endif } /* return 0 if src and dst are on the same device */ int device_is_same( const char *src, const char *dst ) { #ifdef _TARGET_GO32_ fname_t _f1; fname_t _f2; _fixpath( src, _f1 ); _fixpath( dst, _f2 ); ASSERT( _f1[1] == ':' && _f2[1] == ':' ); return ( _f1[0] != _f2[0] ); #else 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 ); #endif } #ifdef _TARGET_GO32_ int fast_stat( const char* s, struct stat *st ) { /* NOTE: vfu does not use this info, so don't simulate it under DOS/WinXX -- otherwise it is too slow */ _djstat_flags = _STAT_INODE /* don't simulate inode info */ |_STAT_EXEC_EXT /* don't try recognize exe's */ |_STAT_EXEC_MAGIC /* don't try recognize exe's */ |_STAT_DIRSIZE /* don't get dir sizes */ |_STAT_ROOT_TIME; /* don't get root time */ int r = stat( s, st ); _djstat_flags = 0; return r; } #else #define fast_stat stat #endif /*###########################################################################*/ 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_s = eta % 60; int eta_v = eta_m > 0 ? eta_m : eta_s; char eta_c = eta_m > 0 ? eta_m : eta_s; 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 ); 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; fast_stat( src, &stat_src ); fast_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 ch = 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 = file_st_size( &stat_src ); 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 = file_st_size( &stat_dst ); 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( "Overwrite", "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 ); ch = menu_box_info.ec; if( ch == 'D' ) { VString diff = vfu_temp(); VString cmd; cmd = shell_diff + " '" + dst + "' '" + src + "' > " + diff; system( cmd ); vfu_browse( diff ); unlink( diff ); continue; } break; } say1( "" ); say2( "" ); switch (ch) { case 'Y' : return 1; case 'N' : return 0; case 'A' : copy_info->over_mode = OM_ALWAYS; return 1; case 'V' : copy_info->over_mode = OM_NEVER; return 0; case 'I' : return ( stat_src.st_mtime > stat_dst.st_mtime ); break; case '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 ); 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 ) { char t[128]; vfu_beep(); sprintf(t, "Insufficient disk space! Free: %.0f, Required: %.0f", dev_free, size ); say1( t ); say2( dst ); vfu_menu_box( "Error prompt", "C Continue anyway,S Skip file,N No free space check, Abort (ESC)", -1 ); switch (menu_box_info.ec) { case 'C' : break; case 'S' : copy_info->skipped_count++; return CR_SKIP; break; /* skip it */ case '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( "Create dir error", "C Continue anyway,I Ignore further errors, Abort (ESC)", -1 ); if ( menu_box_info.ec != 'C' ) return CR_ABORT; /* cancel operation */ if ( menu_box_info.ec != '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( "Copy/Move/SymLink error", "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' ) { copy_info->skipped_count++; break; /* consider it ok */ }; if ( menu_box_info.ec == '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( "Copy/Move error", "C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != '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 ) { #ifdef _TARGET_GO32_ ASSERT(!"Symlinks are not supported for this platform!"); return 0; #else 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; #endif /* _TARGET_UNIX_ */ } /*---------------------------------------------------------------------------*/ 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( "Erase error", "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 ); t = "ERASE: " + t + " bytes 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 */ #ifdef _TARGET_GO32_ /* under dos write access rules and delete protection so we have to remove it first */ file_set_mode_str( target, MODE_WRITE_ON ); #endif 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 ) { errno = 0; /* clear error status */ #ifdef _TARGET_GO32_ return 0; /* always ok under dos */ #else return (unlink( target ) != 0); #endif } /* 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 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 ); #ifdef _TARGET_GO32_ if ( a_mode == CM_LINK ) { say1( "LINK: SymLinks are NOT supported on this platform!" ); return; } #endif 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( "Error prompt", "C Continue anyway,E Erase first, Abort (ESC)", -1 ); if ( menu_box_info.ec == 'E' ) { unlink( t ); menu_box_info.ec = 'C'; } if ( menu_box_info.ec != '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(); sprintf(t, "Insufficient disk space! Free: %.0f, Required: %.0f", dev_free, copy_info.files_size ); say1( t ); say2( target ); vfu_menu_box( "Error prompt", "C Continue anyway, Abort (ESC)", -1 ); if ( menu_box_info.ec != '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( "Copy/Move error", "C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != '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.fi( copy_info.current_size ); vfu_str_comma( str ); str = copy_info.description + " DONE: " + str + " bytes."; } else { str = copy_info.description; str += " DONE"; } say2( str + " Target free: " + size_str_compact( device_avail_space( target ) ) + " (" + 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 ) files_list_del(z); else if ( r != CR_ABORT && !ignore_copy_errors ) { vfu_beep(); say1( target ); say2errno(); vfu_menu_box( "Erase error", "C Continue operation,I Ignore further errors, Abort (ESC)" ); if ( menu_box_info.ec != 'C' && menu_box_info.ec != 'I' ) r = CR_ABORT; if ( menu_box_info.ec == '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() { if( sel_count == 0 ) { say( 1, cINFO, "CLIPBOARD: no files selected, %d files already in clipboard", clipboard_copy_info.files_count ); return; } clipboard.undef(); clipboard_copy_info.reset(); VString keep = "1"; int z; for ( z = 0; z < files_list_count(); z++ ) { TF *fi = files_list_get(z); if ( !fi->sel ) continue; clipboard[ fi->full_name() ] = keep; } __copy_calc_totals( clipboard_copy_info, 0 ); clipboard_copy_info.no_free_check = !opt.copy_free_space_check; clipboard_copy_info.over_mode = OM_ASK; /* 0 */ clipboard_copy_info.abort = 0; say( 1, cINFO, "CLIPBOARD: %d files added.", clipboard_copy_info.files_count ); 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( "Copy/Move/Symlink error", "C Continue operation, Abort (ESC)" ); if ( menu_box_info.ec != '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" ); clipboard.undef(); clipboard_copy_info.reset(); } void clipboard_view() { mb = clipboard.keys(); if( mb.count() == 0 ) { say2( "CLIPBOARD: empty" ); return; } vfu_menu_box( 5, 5, "File Clipboard Content" ); } void clipboard_menu( int act ) { if ( act == 0 ) { mb.undef(); mb.push( "A Add files to the clipboard" ); mb.push( "P Copy files here" ); mb.push( "O Move files here" ); mb.push( "L Symlink files here" ); mb.push( "E Clear clipboard" ); mb.push( "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( "--- " + fcnt + " files, " + fsize + " bytes" ); if ( vfu_menu_box( 5, 5, "File Clipboard " + fcnt + " files, " + fsize + " bytes" ) == -1 ) return; act = menu_box_info.ec; } act = toupper( act ); switch( act ) { case 'A': clipboard_add(); break; case 'P': clipboard_paste( CM_COPY ); break; case 'O': clipboard_paste( CM_MOVE ); break; case 'L': clipboard_paste( CM_LINK ); break; case 'E': clipboard_clear(); break; case 'V': clipboard_view(); break; } } /*---------------------------------------------------------------------------*/ vfu-4.22/vfu/vfu.cpp0000644000175000017500000024662714145574042012746 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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; VArray file_find_results; // filefind results VArray path_bookmarks; /*######################################################################*/ VArray user_externals; VArray history; VArray see_filters; VArray panelizers; VArray 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 */ char TF::_full_name[MAX_PATH]; /*######################################################################*/ int do_draw; int do_draw_status; /*######################################################################*/ /* Message issues */ char say_buf[1024]; VString say_str; void say( int line, int attr, const char* format, ... ) { ASSERT( line == 1 || line == 2 ); va_list vlist; va_start( vlist, format ); vsnprintf( say_buf, sizeof(say_buf), format, vlist ); va_end( vlist ); say_str = str_dot_reduce( say_buf, con_max_x()-1 ); con_out( 1, con_max_y() - ( (line == 1) ? 1 : 0 ), say_str, 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 TF::reset() /* reset -- NULL all fields */ { _name = _name_ext = _ext = NULL; 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 = NULL; _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() { if ( _name ) delete [] _name; if ( _view ) delete [] _view; reset(); } /*-----------------------------------------------------------------------*/ const char* TF::full_name( int fix ) { ASSERT( _name ); if ( _name[0] == '/' ) { strcpy( _full_name, _name ); } else { if ( work_mode == WM_ARCHIVE ) strcpy( _full_name, archive_path ); else strcpy( _full_name, work_path ); strcat( _full_name, _name ); } if ( fix && _is_dir ) strcat( _full_name, "/" ); /* i.e. str_fix_path() */ return _full_name; } /*-----------------------------------------------------------------------*/ void TF::set_name( const char* a_new_name ) { if ( _name ) delete [] _name; _name = new char[ strlen(a_new_name) + 1 ]; ASSERT( _name ); /* this is run-time err but for now will be asserted */ strcpy( _name, a_new_name ); int last_slash = str_rfind( _name, '/' ); if ( last_slash == -1 ) _name_ext = _name; else _name_ext = _name + last_slash + 1; int last_dot = str_rfind( _name, '.' ); if ( last_dot == -1 || last_dot == 0 ) /* no dot or dot-file (hidden) */ _ext = _name + strlen( _name ); else _ext = _name + last_dot; //_color = get_item_color( this ); /* this is duplicated here and in update_stat() */ 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() { if ( ! _view ) return; delete [] _view; _view = NULL; } /*-----------------------------------------------------------------------*/ const char* TF::view() { if ( ! _view ) refresh_view(); ASSERT( _view ); return (const char*)_view; } /*-----------------------------------------------------------------------*/ void TF::refresh_view() { ASSERT( _name ); ASSERT( _name_ext ); ASSERT( _ext ); char stmode[16] = ""; // 10 + 1sep char stowner[16+64] = ""; /* +64 just to keep safe (not too much anyway) */ char stgroup[16+64] = ""; /* +64 just to keep safe (not too much anyway) */ 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) { struct passwd* _pwd = getpwuid(_st.st_uid); if (_pwd) snprintf( stowner, sizeof(stowner), "%8s", _pwd->pw_name ); else snprintf( stowner, sizeof(stowner), "%8d", _st.st_uid); stowner[8] = 0; /* safe */ strcat( stowner, " " ); /* field separator */ } if (opt.f_group) { struct group* _grp = getgrgid(_st.st_gid); if (_grp) snprintf( stgroup, sizeof(stgroup), "%8s", _grp->gr_name ); else snprintf( stgroup, sizeof(stgroup), "%8d", _st.st_gid); stgroup[8] = 0; /* safe */ strcat( stgroup, " " ); /* field separator */ } if (opt.f_time ) { time_str_compact( vfu_opt_time( _st ), sttime ); 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; int x = con_max_x(); if ( str_len( view ) > x ) str_sleft( view, x ); else str_pad( view, - x ); if ( _view ) delete [] _view; _view = new char[ con_max_x() + 1 ]; /* +1 for the zero :) */ strcpy( _view, view ); ASSERT( _view ); ASSERT( strlen( _view ) == (size_t)con_max_x() ); } /*-----------------------------------------------------------------------*/ void TF::update_stat( const struct stat* a_new_stat, int a_is_link ) { ASSERT( _name ); ASSERT( _name_ext ); ASSERT( _ext ); 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 = file_st_size( &_st ); _color = -1; drop_view(); } /*######################################################################*/ void vfu_help() { say1center( HEADER ); say2center( CONTACT ); mb.undef(); mb.push( "*keypad -- navigation keys" ); mb.push( "ENTER -- enter into directory/View file ( `+' and `=' too )"); mb.push( "BACKSPC -- (BS) chdir to parent directory ( `-' and ^H too )" ); mb.push( "Alt + - -- exit current archive ( Alt + BACKSPACE too )" ); mb.push( "TAB -- edit entry: filename, atrrib's/mode, owner, group"); mb.push( "R.Arrow -- rename current file " ); mb.push( "SPACE -- select/deselect current list item" ); mb.push( "ESC -- exit menu"); mb.push( "ESC+ESC -- exit menu"); mb.push( "1 -- toggle `mode' field on/off " ); mb.push( "2 -- toggle `owner' field on/off " ); mb.push( "3 -- toggle `group' field on/off " ); mb.push( "4 -- toggle `time' field on/off " ); mb.push( "5 -- toggle `size' field on/off " ); mb.push( "6 -- toggle `type' field on/off " ); mb.push( "7 -- toggle `time type' field change/modify/access time " ); mb.push( "8 -- turn on all fields" ); mb.push( "0 -- toggle long name view ( show only type and file name )" ); mb.push( "~ -- change current dir to HOME directory" ); mb.push( "A -- arrange/Sort file list" ); mb.push( "B -- browse(view) selected (if selection) or current file" ); mb.push( "Alt+B -- browse(view) ONLY current file (regardless selection)" ); mb.push( "C -- copy selected (if selection) or current file(s)" ); mb.push( "Alt+C -- copy ONLY current file (regardless selection)" ); mb.push( "D -- change directory" ); mb.push( "Ctrl+D -- directory tree " ); mb.push( "Alt+D -- chdir history " ); mb.push( "E -- erase(remove) selected (if selection) or current file(s)!" ); mb.push( "Alt+E -- erase(remove) ONLY current file (regardless selection)!" ); mb.push( "F -- change file masks (space-delimited) "); mb.push( "Ctrl+F -- reset file mask to `*'" ); mb.push( "G -- global select/deselect" ); mb.push( "H -- this help text" ); mb.push( "I -- edit file" ); mb.push( "Q -- exit here (to the current directory)"); mb.push( "R -- reload directory/refresh file list" ); mb.push( "Ctrl+R -- recursive reload... " ); mb.push( "Alt+R -- reload/tree menu" ); mb.push( "J -- jump to mountpoint" ); mb.push( "L -- symlink selected/current file(s) into new directory" ); mb.push( "Ctrl+L -- refresh/redraw entire screen" ); mb.push( "M -- move selected (if selection) or current file(s)" ); mb.push( "Alt+M -- move ONLY current file (regardless selection)" ); mb.push( "N -- file find" ); mb.push( "Alt+N -- file find menu" ); mb.push( "O -- options(toggles) menu" ); mb.push( "P -- file clipboard menu" ); mb.push( "S -- incremental filename search" ); mb.push( "Alt+S -- repeat last used incremental search entry (find next)" ); /* mb.push( "Ctrl+C -- copy files to clipboard" ); mb.push( "Ctrl+X -- cut files to clipboard" ); mb.push( "Ctrl+V -- paste (copy) files from clipboard to current directory" ); */ mb.push( "T -- tools menu (including rename and classify tools)" ); mb.push( "U -- user menu (user external commands bound to menu) " ); mb.push( "X -- exit to the old(startup) directory "); mb.push( "Alt+X -- exit to the old(startup) directory "); mb.push( "Z -- calculate directories sizes menu" ); mb.push( "Ctrl+Z -- show size of the current (under the cursor >>) directory"); mb.push( "Alt+Z -- show all directories sizes ( or Alt+Z )" ); mb.push( "V -- edit vfu.conf file"); mb.push( "! -- shell (also available with '?')" ); mb.push( "/ -- command line" ); mb.push( "vfu uses these (one of) these config files:"); mb.push( " 1. $HOME/$RC_PREFIX/vfu/vfu.conf"); mb.push( " 2. $HOME/.vfu/vfu.conf"); mb.push( " 3. " FILENAME_CONF_GLOBAL0 ); mb.push( " 4. " FILENAME_CONF_GLOBAL1 ); mb.push( " 5. " FILENAME_CONF_GLOBAL2 ); mb.push( "" ); vfu_menu_box( 1, 4, "VFU Help ( PageUp/PageDown to scroll )" ); mb.undef(); do_draw = 1; } /*--------------------------------------------------------------------------*/ void vfu_init() { char t[MAX_PATH]; if( expand_path( "." ) == "" ) chdir( "/" ); 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() */ #ifdef _TARGET_GO32_ user_id_str = "dosuser"; group_id_str = "dos"; gethostname( t, MAX_PATH-1 ); host_name_str = t; __opendir_flags = __OPENDIR_FIND_HIDDEN; #else /* _TARGET_UNIX_ */ uid_t _uid = getuid(); gid_t _gid = getgid(); struct passwd* _pwd = getpwuid(_uid); struct group* _grp = getgrgid(_gid); if ( _pwd ) user_id_str = _pwd->pw_name; else user_id_str = (int)_uid; if ( _grp ) group_id_str = _grp->gr_name; else group_id_str = (int)_gid; gethostname( t, MAX_PATH-1 ); host_name_str = t; #endif startup_path = work_path; tmp_path = ""; if ( getenv( "TEMP" ) ) tmp_path = getenv( "TEMP" ); if ( getenv( "TMP" ) ) tmp_path = getenv( "TMP" ); if ( tmp_path == "" ) { #ifdef _TARGET_GO32_ tmp_path = "c:/tmp/"; #else tmp_path = "/tmp/"; #endif } else str_fix_path( tmp_path ); if ( getenv( "HOME" ) ) home_path = getenv( "HOME" ); else { home_path = tmp_path; home_path += user_id_str; home_path += "/"; make_path( home_path ); } #ifdef _TARGET_GO32_ str_tr( home_path, "\\", "/" ); #endif #ifdef _TARGET_GO32_ shell_diff = "fc"; #else shell_diff = "/usr/bin/diff"; #endif /* 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_conf = rc_path; filename_tree = rc_path; filename_size_cache = rc_path; filename_history = rc_path; filename_ffr = rc_path; filename_opt += FILENAME_OPT; filename_conf += FILENAME_CONF; filename_tree += FILENAME_TREE; filename_size_cache += FILENAME_SIZE_CACHE; filename_history += FILENAME_HISTORY; filename_ffr += FILENAME_FFR; if ( access( filename_conf, R_OK ) != 0 ) { /* cannot find local/user conf file, try copy one */ if ( access( FILENAME_CONF_GLOBAL0, R_OK ) == 0 ) { VArray va; va.fload( FILENAME_CONF_GLOBAL0 ); va.fsave( filename_conf ); } else if ( access( FILENAME_CONF_GLOBAL1, R_OK ) == 0 ) { VArray va; va.fload( FILENAME_CONF_GLOBAL1 ); va.fsave( filename_conf ); } else if ( access( FILENAME_CONF_GLOBAL2, R_OK ) == 0 ) { VArray va; va.fload( FILENAME_CONF_GLOBAL2 ); va.fsave( filename_conf ); } } if ( access( filename_conf, R_OK ) != 0 ) { /* cannot find local/user conf file, try globals */ if ( access( FILENAME_CONF_GLOBAL0, R_OK ) == 0 ) filename_conf = FILENAME_CONF_GLOBAL0; else if ( access( FILENAME_CONF_GLOBAL1, R_OK ) == 0 ) filename_conf = FILENAME_CONF_GLOBAL1; else if ( access( FILENAME_CONF_GLOBAL2, R_OK ) == 0 ) filename_conf = FILENAME_CONF_GLOBAL2; /* if we get here then no readable conf file found */ } /* shell setup */ shell_prog = ""; if (getenv("SHELL")) shell_prog = getenv("SHELL"); #ifdef _TARGET_GO32_ if ( shell_prog == "" ) if(getenv("COMSPEC")) shell_prog = getenv("COMSPEC"); #endif if (getenv("VFU_SHELL")) shell_prog = getenv("VFU_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(); 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 */ ////////////////////////////////////////// // setup signals to VFUdone // this is a patch but at least will reset terminal and save settings 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 ) { chdir( a_path ); #ifdef _TARGET_GO32_ return; // this is enough under DOS #else VString str; if ( getenv( "VFU_EXIT" ) ) str = getenv( "VFU_EXIT" ); else { str = tmp_path; str_fix_path( str ); str += "vfu.exit."; str += user_id_str; } unlink( str ); int fdx = open( str, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR ); if( fdx == -1 ) return; close( fdx ); FILE *f = fopen( str, "wt" ); if ( ! f ) return; fputs( a_path, f); fclose( f ); #endif } /*--------------------------------------------------------------------------*/ /* return 0 for exit-confirmed! */ int vfu_exit( const char* a_path ) { int z; mb.undef(); mb.push( "X Exit (to startup path)" ); mb.push( "Q Quit (to work path) " ); if ( a_path == NULL ) { vfu_beep(); z = vfu_menu_box( 50, 5, " 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 */ int ch = 0; 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()? */ ch = con_getch(); /* FILE *zf = fopen( "/tmp/getch.txt", "awt" ); fprintf( zf, "%d\n", ch ); fclose(zf); */ if( ch == 0 || ch == KEY_RESIZE ) ch = KEY_CTRL_L; if ( ch >= 'A' && ch <= 'Z' ) ch = tolower( ch ); say1( "" ); if ( user_id_str == "root" ) say2center( "*** WARNING: YOU HAVE GOT ROOT PRIVILEGES! ***" ); else say2( "" ); if ( work_mode == WM_NORMAL || work_mode == WM_ARCHIVE ) switch (ch) { /* actually this is ANY work_mode (since there are two modes only) */ case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '0' : vfu_toggle_view_fields( ch ); break; case '.' : vfu_toggle_view_fields( ch ); vfu_rescan_files( 0 ); break; case 's' : vfu_inc_search( 0 ); break; case KEY_ALT_S : vfu_inc_search( 1 ); break; case KEY_CTRL_L: do_draw = 3; break; case 'q' : if( vfu_exit( work_path ) == 0 ) return; break; case KEY_ALT_X : case 'x' : if( vfu_exit( startup_path ) == 0 ) return; break; case 27 : if( vfu_exit( NULL ) == 0 ) return; break; case KEY_UP : vfu_nav_up(); break; case KEY_DOWN : vfu_nav_down(); break; case KEY_PPAGE : vfu_nav_ppage(); break; case KEY_NPAGE : vfu_nav_npage(); break; case KEY_CTRL_A : case KEY_HOME : vfu_nav_home(); break; case KEY_CTRL_E : case KEY_END : vfu_nav_end(); break; case 'h' : vfu_help(); break; case 'f' : vfu_change_file_mask( NULL ); break; case KEY_CTRL_F : vfu_change_file_mask( "*" ); break; case KEY_CTRL_D : tree_view(); break; case KEY_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 KEY_CTRL_R : vfu_rescan_files( 1 ); break; case 'r' : vfu_rescan_files( 0 ); break; case ' ' : vfu_nav_select(); break; #ifdef _TARGET_UNIX_ case KEY_BACKSPACE : #endif case 8 : case '-' : vfu_action_minus(); break; case KEY_ALT_BACKSPACE : case KEY_ALT_MINUS : vfu_action_minus( 2 ); break; case 13 : case '+' : case '=' : vfu_action_plus( ch ); break; case KEY_LEFT : if (opt.lynx_navigation) vfu_action_minus(); break; case KEY_RIGHT : if (opt.lynx_navigation) vfu_action_plus( '+' ); else if ( work_mode == WM_NORMAL ) vfu_rename_file_in_place(); break; case 'd' : vfu_chdir( NULL ); break; case KEY_ALT_D : vfu_chdir_history(); break; case KEY_ALT_EQ : case '>' : opt.long_name_view = !opt.long_name_view; vfu_drop_all_views(); do_draw = 1; break; case 'a' : vfu_arrange_files(); break; case 'g' : vfu_global_select(); break; case 'o' : vfu_options(); break; case 'v' : vfu_edit_conf_file(); break; case '!' : case '?' : con_cs(); vfu_shell( shell_prog, 0 ); do_draw = 3; break; case 'u' : vfu_user_menu(); break; /* not documented unless here :) */ case KEY_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 '*' : FGO( rand() % files_list_count() ); do_draw = 1; break; case 'z' : vfu_directories_sizes( 0 ); break; case KEY_ALT_Z : vfu_directories_sizes( 'A' ); break; case KEY_CTRL_Z : vfu_directories_sizes( 'Z' ); break; } if ( work_mode == WM_ARCHIVE ) switch (ch) { case 'c' : vfu_extract_files( 0 ); break; case KEY_ALT_C : vfu_extract_files( 1 ); break; } if ( work_mode == WM_NORMAL ) switch (ch) { case 'b' : case KEY_ALT_B : if ( ch == 'b' && sel_count > 0 ) vfu_browse_selected_files(); else { if ( files_list_count() > 0 ) vfu_browse( files_list_get(FLI)->name(), ch == KEY_ALT_B ); else say1( "No files" ); } break; case 'n' : vfu_file_find( 0 ); break; case KEY_ALT_N : vfu_file_find( 1 ); break; case '~' : vfu_chdir( home_path ); break; case '/' : 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 KEY_ALT_M : vfu_copy_files(1, CM_MOVE); break; case 'c' : vfu_copy_files(sel_count == 0, CM_COPY); break; case KEY_ALT_C : vfu_copy_files(1, CM_COPY); break; case 'l' : vfu_copy_files(sel_count == 0, CM_LINK); break; case KEY_ALT_L : vfu_copy_files(1, CM_LINK); break; case 'e' : vfu_erase_files(sel_count == 0); break; case KEY_ALT_E : vfu_erase_files(1); break; case 'j' : vfu_jump_to_mountpoint( 0 ); break; case KEY_ALT_J : vfu_jump_to_mountpoint( 1 ); break; case KEY_ALT_1 : bookmark_goto( '1' ); break; case KEY_ALT_2 : bookmark_goto( '2' ); break; case KEY_ALT_3 : bookmark_goto( '3' ); break; case KEY_ALT_4 : bookmark_goto( '4' ); break; case KEY_ALT_5 : bookmark_goto( '5' ); break; case KEY_ALT_6 : bookmark_goto( '6' ); break; case KEY_ALT_7 : bookmark_goto( '7' ); break; case KEY_ALT_8 : bookmark_goto( '8' ); break; case KEY_ALT_9 : bookmark_goto( '9' ); break; case '`' : bookmark_goto(-1 ); 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 ( ( KEY_F1 <= ch && ch <= KEY_F10) || ( KEY_SH_F1 <= ch && ch <= KEY_SH_F10) || ( KEY_ALT_F1 <= ch && ch <= KEY_ALT_F10) || ( KEY_CTRL_F1 <= ch && ch <= KEY_CTRL_F10) || ( ch == KEY_IC) ) vfu_user_external_exec( ch ); } } /*--------------------------------------------------------------------------*/ 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; #ifdef _TARGET_GO32_ str_tr( temp, "\\", "/" ); #endif 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' : 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 ); FGO( 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( int ch ) { switch( ch ) { case '1' : opt.f_mode = !opt.f_mode; break; case '2' : opt.f_owner = !opt.f_owner; break; case '3' : opt.f_group = !opt.f_group; break; case '4' : opt.f_time = !opt.f_time; break; case '5' : opt.f_size = !opt.f_size; break; case '6' : opt.f_type = !opt.f_type; break; case '7' : opt.f_time_type++; if (opt.f_time_type > 2) opt.f_time_type = 0; break; case '8' : opt.f_mode = opt.f_owner = opt.f_group = opt.f_time = opt.f_size = opt.f_type = 1; break; case '0' : opt.long_name_view = !opt.long_name_view; break; case '.' : 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_getch(); } 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(); } chdir( work_path ); /* in case SHELL changed directory... (DOS only :)) */ 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 ( files_list_get(FLI)->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", fname ); str_replace( str, "%F", 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", fname ); str_replace( str, "%F", full_fname ); str += " > "; 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", fname ); str_replace( str, "%F", full_fname ); } vfu_shell( str, "" ); } do_draw = 2; say1(""); say2(""); if ( tmp_name != "" ) unlink( tmp_name ); } /*--------------------------------------------------------------------------*/ void vfu_action_plus( int key ) { if ( files_list_count() == 0 ) return; TF *fi = files_list_get(FLI); 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 ) { 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 ( key == KEY_ENTER && vfu_user_external_find( KEY_ENTER, fi->ext(), fi->type_str(), NULL ) != -1 ) vfu_user_external_exec( KEY_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 ( key == KEY_ENTER && vfu_user_external_find( KEY_ENTER, fi->ext(), fi->type_str(), NULL ) != -1 ) vfu_user_external_exec( KEY_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 ) { #ifdef _TARGET_GO32_ if ( work_path[1] == ':' && work_path[2] == '/' && work_path[3] == 0 ) return; #else if ( work_path[0] == '/' && work_path[1] == 0 ) return; #endif 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 ) { FGO(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 = files_list_get(FLI); 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() { char ch; mb.undef(); mb.push( "S All" ); mb.push( "A All (+Dirs)" ); mb.push( "R Reverse selection" ); mb.push( "C Clear selection" ); mb.push( "P Pack (list selected only)" ); mb.push( "H Hide selected" ); mb.push( "D Different" ); mb.push( ". Hide dirs" ); mb.push( ", Hide dotfiles" ); mb.push( "= Select by mask (with directories)" ); mb.push( "+ Select by mask (w/o directories)" ); mb.push( "- Deselect by mask" ); mb.push( "L Select Same..." ); mb.push( "X Extended Select..." ); if ( vfu_menu_box( 30, 5, "Select Files and Directories" ) == -1 ) return; ch = menu_box_info.ec; if (ch == 'X') { if ( work_mode != WM_NORMAL ) { say1( "Extended Select is not available in this mode." ); return; } mb.undef(); mb.push( "A Select to the begin of the list" ); mb.push( "E Select to the end of the list" ); mb.push( "--searching--" ); mb.push( "F Find string (no case)" ); mb.push( "S Scan string (case sense)" ); mb.push( "H Hex string" ); mb.push( "/ Regular expression" ); mb.push( "\\ Reg.exp (no case)" ); // mb.push( "--other--" ); // mb.push( "M Mode/Attributes" ); if ( vfu_menu_box( 32, 6, "Extended Select" ) == -1 ) return; ch = menu_box_info.ec; if (ch == 'S') ch = 'B'; /* 'B' trans scan */ if (ch == 'H') ch = 'E'; /* 'E' trans hex */ if (ch == 'A') ch = '<'; /* '<' trans to begin */ if (ch == 'E') ch = '>'; /* '>' trans to end */ } switch(ch) { case '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 'A' : { for (int z = 0; z < files_list_count(); z++) files_list_get(z)->sel = 1; } break; case '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 'C' : { int z; for (z = 0; z < files_list_count(); z++) files_list_get(z)->sel = 0; } break; case '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 '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 '.' : { 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 ',' : { 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 '+' : case '=' : case '-' : { VString m; int selaction = 0; if (ch != '-') selaction = 1; if (ch == '+') say1("Select by mask: (w/o directories)"); else if (ch == '=') say1("Select by mask: (with directories)"); else say1("Deselect by mask:"); if ( vfu_get_str( "", m, HID_GS_MASK )) { VArray sm; sm = str_split( " +", m ); while( (m = sm.pop()) != "" ) { if (opt.mask_auto_expand) vfu_expand_mask( m ); int z = 0; for (z = 0; z < files_list_count(); z++) { if (files_list_get(z)->is_dir() && ch == '+') continue; if (FNMATCH(m,files_list_get(z)->name_ext()) == 0) files_list_get(z)->sel = selaction; } } } say1( " " ); say2( " " ); } break; case '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 '/': case '\\': case 'E': case 'F': case '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( ch ) { case 'F': pos = file_string_search( pat, files_list_get(z)->name(), "i" ); break; case 'B': pos = file_string_search( pat, files_list_get(z)->name(), "" ); break; case 'E': pos = file_string_search( pat, files_list_get(z)->name(), "h" ); break; case '/': pos = file_string_search( pat, files_list_get(z)->name(), "r" ); break; case '\\': 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': { mb.undef(); mb.push( "N Name" ); mb.push( "E Extension" ); mb.push( "S Size" ); mb.push( "T Time" ); mb.push( "I Time (1 min.round)" ); mb.push( "D Date" ); mb.push( "M Date+Time" ); mb.push( "A Attr/Mode" ); #ifndef _TARGET_GO32_ mb.push( "O Owner" ); mb.push( "G Group" ); #endif mb.push( "Y Type (TP)" ); vfu_menu_box( 32, 6, "Select Same..." ); ch = menu_box_info.ec; switch ( ch ) { case 'N' : vfu_global_select_same( GSAME_NAME ); break; case 'E' : vfu_global_select_same( GSAME_EXT ); break; case 'S' : vfu_global_select_same( GSAME_SIZE ); break; case 'M' : vfu_global_select_same( GSAME_DATETIME ); break; case 'T' : vfu_global_select_same( GSAME_TIME ); break; case 'I' : vfu_global_select_same( GSAME_TIME1 ); break; case 'D' : vfu_global_select_same( GSAME_DATE ); break; case 'O' : vfu_global_select_same( GSAME_OWNER ); break; case 'G' : vfu_global_select_same( GSAME_GROUP ); break; case 'A' : vfu_global_select_same( GSAME_MODE ); break; case 'Y' : vfu_global_select_same( GSAME_TYPE ); break; } } break; case '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 '<' : { 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 '>' : { 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( int 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( int key ) { if ( files_list_count() == 0 ) { say1( "Directory is empty: user externals are disabled!" ); return; } VString shell_line; TF *fi = files_list_get(FLI); 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 { char t[128]; snprintf( t, sizeof(t), "No user external defined for this key and extension (%d,%s)", key, fi->ext() ); say1( t ); } } /*--------------------------------------------------------------------------*/ VString tools_last_target; void vfu_tools() { mb.undef(); mb.push( "R Real path" ); mb.push( "D ChDir to symlink path" ); mb.push( "G Go to symlink target" ); mb.push( "B Go back to last target" ); mb.push( "T Make directory" ); mb.push( "P Path bookmarks" ); mb.push( "A Rename tools..." ); mb.push( "C Classify files" ); if ( vfu_menu_box( 30, 5, "Tools" ) == -1 ) return; switch( menu_box_info.ec ) { case 'R' : { fname_t s; expand_path(files_list_get(FLI)->name(), s); say1( s ); break; } case 'D' : { if( ! files_list_get(FLI)->is_link() ) break; tools_last_target = files_list_get(FLI)->full_name(); if( ! files_list_get(FLI)->is_dir() ) break; vfu_chdir( expand_path( files_list_get(FLI)->name() ) ); break; } case 'G' : { if( ! files_list_get(FLI)->is_link() ) break; tools_last_target = files_list_get(FLI)->full_name(); VString target = vfu_readlink( files_list_get(FLI)->full_name() ); vfu_chdir( expand_path( str_file_path( target ) ) ); vfu_goto_filename( str_file_name_ext( target ) ); break; } case 'B' : { if( tools_last_target == "" ) break; VString target = tools_last_target; tools_last_target = files_list_get(FLI)->full_name(); vfu_chdir( expand_path( str_file_path( target ) ) ); vfu_goto_filename( str_file_name_ext( target ) ); break; } case '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_getch(); err++; } if ( err == 0 ) say1( "MKDIR: ok." ); break; } } break; case 'P' : bookmark_goto( -1 ); break; case 'A' : vfu_tool_rename(); break; case 'C' : vfu_tool_classify(); break; } } /*--------------------------------------------------------------------------*/ void bookmark_goto( int n ) { VString t; if ( n == -1 ) { int z; mb.undef(); mb.push( "A Bookmark current directory" ); mb.push( "` Change working directory" ); mb.push( "---" ); for( z = 1; z < 10; z++ ) { const char* ps = path_bookmarks.get( z-1 ); if( !ps ) break; sprintf( t, "%d %s", z%10, ps ); mb.push( str_dot_reduce( t, 60 ) ); } n = vfu_menu_box( 5, 5, "Path bookmarks"); if ( n == -1 ) return; n = menu_box_info.ec; } // FIXME: neshto ne raboti :/ switch( n ) { case '`' : vfu_chdir( NULL ); return; case 'A' : bookmark_hookup(); return; } if ( n >= '1' && n <= '9' && str_len( path_bookmarks[ n - '1' ] ) > 0 ) { vfu_chdir( path_bookmarks[ n - '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 = files_list_get(FLI); int y = (file_list_index.pos() - file_list_index.page()) + 4; int x = tag_mark_pos + 3; VString str = fi->name(); if(TextInput( x, y, "", MAX_PATH, con_max_x() - tag_mark_pos - 4, &str ) && strcmp( fi->name(), str.data() ) ) { 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_directories_sizes( int n ) { int z; char t[256]; int dir_size_mode = __vfu_dir_size_followsymlinks | __vfu_dir_size_samedevonly; n = toupper( n ); if ( n == 0 ) { mb.undef(); mb.push( "E Specify directory" ); mb.push( "Z Directory under cursor" ); mb.push( ". Current directory `.'" ); mb.push( "S Selected directories" ); mb.push( "A All dir's in the list" ); mb.push( "--directory size options--" ); mb.push( "N Normal" ); mb.push( "Y Follow symlinks (WARNING: may loop!)" ); mb.push( "V Keep on the same device/filesystem only" ); if ( vfu_menu_box( 5, FPS - 8, "Directory size of:" ) == -1 ) return; n = menu_box_info.ec; } say1( "Calculating files size. Press ESCAPE to cancel calculation." ); if ( n == 'E' || n == '.' ) /* specific directory */ { VString target = work_path; if ( n == '.' ) 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 ); if ( dir_size == -1 ) return; VString dir_size_str; dir_size_str.fi( dir_size ); vfu_str_comma( dir_size_str ); snprintf( t, sizeof(t), "Dir size of: %s", target.data() ); say1( t ); snprintf( t, sizeof(t), "Size: %s bytes", dir_size_str.data() ); say2( t ); } else if ( n == 'A' || n == 'S' ) /* all or selected */ { for( z = 0; z < files_list_count(); z++) { TF *fi = files_list_get(z); if ( fi->is_dir() ) /* dirs */ { if ( n == 'S' && !fi->sel ) continue; /* if not sel'd and required -- skip */ /* if ( n == 'A' ) continue; // all */ fsize_t dir_size = vfu_dir_size( fi->name(), 0, dir_size_mode ); if ( dir_size == -1 ) break; fi->set_size( dir_size ); } } size_cache_sort(); say1(""); say2(""); } else if ( n == 'Z' ) /* single one, under cursor */ { if ( files_list_get(FLI)->is_dir() ) { files_list_get(FLI)->set_size( vfu_dir_size( files_list_get(FLI)->name(), 1, dir_size_mode ) ); say1(""); say2(""); } else say1("This is not directory..."); } else if( n == '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( n == '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( n == '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" ); } do_draw = 1; update_status(); if ( opt.sort_order == 'S' && n < 0 ) vfu_sort_files(); } /*--------------------------------------------------------------------------*/ void vfu_edit_entry( ) { char errstr[128]; int one = ( sel_count == 0 ); int z; VString str; mb.undef(); mb.push( "M Mode" ); mb.push( "A Octal Mode" ); mb.push( "O Owner/Group" ); mb.push( "N Name (TAB)" ); mb.push( "T Time/Touch Mod+Acc Times" ); mb.push( "I Modify Time" ); mb.push( "E Access Time" ); mb.push( "L Edit SymLink Reference" ); if ( sel_count ) { /* available only when selection exist */ mb.push( "--"); mb.push( "+ Target: Toggle" ); mb.push( "C Target: Current File" ); mb.push( "S Target: Selection" ); } while(1) { while(1) { str = "Edit entry: "; str += one ? "current file" : "[ SELECTION ] "; menu_box_info.ac = 9; z = vfu_menu_box( 50, 5, str ); if ( z == -1 ) return; /* canceled */ if (menu_box_info.ac == -2 ) menu_box_info.ec = 'N'; if (menu_box_info.ec == '+') { one = !one; continue; } if (menu_box_info.ec == 'S') { one = 0; continue; } if (menu_box_info.ec == 'C') { one = 1; continue; } break; } if ( menu_box_info.ec == 'N' ) /* name (rename) */ { vfu_rename_file_in_place(); break; } else if ( menu_box_info.ec == 'M' || menu_box_info.ec == 'A' ) /* attributes/mode */ { mode_str_t new_mode; int ok = 1; int err = 0; if ( menu_box_info.ec == 'M' ) { if (one) { strcpy( new_mode, files_list_get(FLI)->mode_str() ); file_get_mode_str( files_list_get(FLI)->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; 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 == 'T' || menu_box_info.ec == 'I' || menu_box_info.ec == '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 == 'M') tb.modtime = new_time; if (menu_box_info.ec == 'S') tb.actime = new_time; if (menu_box_info.ec == 'T') tb.modtime = new_time; if (menu_box_info.ec == '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 == 'O' ) { #ifdef _TARGET_GO32_ say1( "Change owner/group function is not supported under DOS filesystem" ); break; #endif 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' ) { #ifdef _TARGET_GO32_ say1( "Edit SymLink reference is not supported under DOS filesystem" ); #else if ( ! one ) { say1( "Cannot edit symlink reference for selection..." ); break; } TF* fi = files_list_get(FLI); 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." ); } } #endif break; } } return; } /*--------------------------------------------------------------------------*/ void vfu_jump_to_mountpoint( int all ) { VString str; char t[2048]; int z; VArray va; #ifdef _TARGET_UNIX_ if ( va.fload( "/etc/mtab" ) ) return; #endif #ifdef _TARGET_GO32_ str = home_path; str += "_vfu.mtb"; if ( va.fload( str ) ) return; if (all) { va.ins( 0, "- b:/" ); va.ins( 0, "- a:/" ); } #endif 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 */ #ifdef _TARGET_GO32_ if (toupper(t[0]) >= 'A' && toupper(t[0]) <= 'Z' && toupper(t[1]) == ':') hk = toupper(t[0]); #endif 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(str); } menu_box_info.ac = KEY_CTRL_U; z = vfu_menu_box( 5, 5, "Jump to mount-point (free/total) Ctrl+U=umount" ); if ( z == -1 ) return; if (menu_box_info.ac == -2) { 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( 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( des ); } if ( mb.count() == 0 ) { say1("No menu user externals defined..."); return; } z = vfu_menu_box( 5, 5, "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, "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 ) { FGO(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, /* origin path */ const char* fname, /* full file name */ const struct stat* st, /* stat struture or NULL */ int is_link, /* 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; file_find_results.push( str ); str = str_dot_reduce( str, con_max_x()-1 ); con_puts( "\r" ); con_puts( str, cSTATUS ); con_puts( "\n" ); str = "Found items: "; str += __ff_rescount; say1( str ); return 0; } void vfu_file_find( int menu ) { VString str; char ch; if (menu) { if ( vfu_menu_box("File find", "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; ch = menu_box_info.ec; } else ch = 'N'; if ( ch == 'L' ) { if ( file_find_results.count() == 0 ) file_find_results.fload( filename_ffr ); vfu_file_find_results(); return; } if ( ch == 'D' ) { file_find_results.undef(); vfu_file_find_results(); /* FIXME: this will show `no results' warning */ return; } __ff_pattern = ""; if ( str_find( "FSB/\\", ch ) != -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 (ch == 'F' ) __ff_opt = "i "; else if (ch == 'S' ) __ff_opt = " "; else if (ch == 'B' ) __ff_opt = " "; else if (ch == 'E' ) __ff_opt = "h "; else if (ch == '/' ) __ff_opt = "r "; else if (ch == '\\') __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 ); con_out( 1, 1, HEADER ); sprintf( str, "Find mask: %s", vfu_hist_get( HID_FFMASK, 0 ) ); con_out( 1, 2, str ); sprintf( str, "Start path: %s", __ff_path.data() ); con_out( 1, 3, str ); if ( __ff_pattern != "" ) { sprintf( str, "Containing pattern: %s", __ff_pattern.data() ); 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( "T Rescan DirTree" ); list.push(""); mb.push( "F Rescan Files" ); list.push(""); mb.push( "R Rescan Files Recursive" ); list.push(""); mb.push( "L Refresh all views/screen (Ctrl+L)" ); list.push(""); if ( panelizers.count() > 0 ) { mb.push( "--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(t); list.push(str); } } z = vfu_menu_box( 25, 5, "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 'T' : tree_rebuild(); break; case 'F' : vfu_read_files( 0 ); break; case 'R' : vfu_read_files( 1 ); break; case 'L' : vfu_reset_screen(); break; } } /*--------------------------------------------------------------------------*/ void vfu_inc_search( int use_last_one ) { VString str; int key; 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 pattern: ( ALT+S for next match )" ); key = 9; } else { say1( "Enter incremental" + no_case_opt_str + "search pattern: ( TAB for next match )" ); key = con_getch(); } VRegexp size_re("^size:(\\d+)$"); while( ( key >= 32 && key <= 255 ) || key == 8 || key == KEY_BACKSPACE || key == 9 ) { if ( key == 8 || key == KEY_BACKSPACE ) str_trim_right( str, 1 ); else if ( key != 9 ) str_add_ch( str, key ); say2( str ); if ( files_list_count() == 0 ) { key = con_getch(); continue; } int z; if ( key == 9 ) { z = FLI+1; if ( z > file_list_index.max() ) z = file_list_index.min(); } 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 = atoi( size_re[1] ); else vfu_expand_mask( s_mask ); while(1) { if ( z > file_list_index.max() ) z = file_list_index.min(); if ( z < file_list_index.min() ) z = file_list_index.max(); 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) { FGO(z); vfu_redraw(); show_pos( FLI+1, files_list_count() ); } if( use_last_one ) break; else key = con_getch(); } 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; FGO(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://soul.datamax.bg/~cade/vfu]\nThank You for using VFU!\n\n", HEADER ); */ return 0; } /*######################################################################*/ vfu-4.22/vfu/vfusetup.h0000644000175000017500000000674714151762154013471 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 "4.22" #define HEADER "VF/U v" VFU_VERSION " Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski 'Cade'" #define CONTACT " [http://cade.datamax.bg]" #ifdef _TARGET_GO32_ #define FILENAME_OPT "vfu.opt" #define FILENAME_CONF "vfu.cfg" #define FILENAME_TREE "vfu.tre" #define FILENAME_SIZE_CACHE "vfu.siz" #define FILENAME_HISTORY "vfu.hst" #define FILENAME_FFR "vfu.ffr" #else #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" #endif // 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" #define MAX_FILES 8388608 // 8 Mi, with 8-byte pointer this acquires 64MiB /* 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-4.22/vfu/vfumenu.h0000644000175000017500000000137214145574042013262 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 char *title, ToggleEntry* toggles ); int vfu_menu_box( int x, int y, const char *title, VArray *va = &mb ); int vfu_menu_box( const char* title, const char* menustr, int row = -1 ); #endif /* _VFUMENU_H_ */ /* eof vfumenu.h */ vfu-4.22/vfu/vfufiles.h0000644000175000017500000000334414145574042013421 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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-4.22/vfu/vfuview.h0000644000175000017500000000175714145574042013277 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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-4.22/vfu/vfuopt.h0000644000175000017500000000513714145574042013123 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _VFUOPT_H_ #define _VFUOPT_H_ #include "see.h" #include "vfuuti.h" extern const char *NOYES[]; extern const char *FTIMETYPE[]; extern const char *TAGMARKS[]; extern const char *COMMA_TYPES[]; struct Options { int sort_order; int 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; SeeViewerOptions svo; SeeEditorOptions seo; }; 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(); void vfu_settings_save(); void vfu_edit_conf_file(); void vfu_options(); #endif /* _VFUOPT_H_ */ vfu-4.22/vfu/vfufiles.cpp0000644000175000017500000004126214145574042013755 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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(); /* FIXME: perhaps we could check work_mode here? ... anyway will ASSERT it */ 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 = 'U'; } else if ( list_panelizer.count() ) { ASSERT( work_mode == WM_NORMAL ); vfu_read_pszlist_files(); opt.sort_order = '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 ); update_status(); vfu_nav_update_pos(); vfu_sort_files(); vfu_drop_all_views(); FGO(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 ) { #ifdef _TARGET_GO32_ mode_str_t mode_str; file_get_mode_str( st->st_mode, mode_str ); if ( mode_str[7] == 'H' || mode_str[8] == 'S' ) return 0; #else if ( ne[0] == '.' ) return 0; #endif } 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 ) ); } 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 ); if ( opt.auto_mount && files_list_count() == 1 && ( FNMATCH( files_list_get(0)->name(), "automount" ) == 0 || FNMATCH( files_list_get(0)->name(), ".automount" ) == 0 ) ) { VString tmp_file_name; tmp_file_name += tmp_path; tmp_file_name += "vfu_automount_error."; tmp_file_name += user_id_str; VString str = work_path; chdir( "/" ); str = "mount " + str + " 2> " + tmp_file_name; say1( "AutoMount point detected, executing:" ); say2( str ); int err; if ( (err = system( str )) == 0) { //--------------- files_list_trim(); sel_count = 0; sel_size = 0; files_size = 0; //--------------- chdir( work_path ); ftwalk( ".", __vfu_ftw_add, a_recursive ? -1 : 1 ); } else { char t[128]; FILE *f = fopen( tmp_file_name, "r" ); t[0] = 0; fgets( t, sizeof(t), f ); fclose(f); str_tr( t, "\n\r", " " ); say1( "AutoMount failed! ( press ESC ) reason:" ); say2( t ); con_beep(); con_getch(); } unlink( tmp_file_name ); } } /*---------------------------------------------------------------------------*/ 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 == 'U') return 0; switch (opt.sort_order) { case 'N' : z = pathcmp(f1->name(), f2->name()); break; case 'M' : z = namenumcmp(f1->name(), f2->name()); break; case 'E' : z = pathcmp(f1->ext(), f2->ext()); break; case 'S' : z = (f2->size() < f1->size()) - (f2->size() > f1->size()); break; case 'T' : z = f1->st()->st_mtime - f2->st()->st_mtime; break; case 'H' : z = f1->st()->st_ctime - f2->st()->st_ctime; break; case 'C' : z = f1->st()->st_atime - f2->st()->st_atime; break; case 'A' : z = strcmp( f1->mode_str(), f2->mode_str() ); break; case '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 '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 '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 == 'A' || opt.sort_direction == 'D' ); if (z) { z = (z > 0) - (z < 0); if (opt.sort_direction == '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 == '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() ) { FGO(z); break; } } } /*---------------------------------------------------------------------------*/ void vfu_arrange_files() { int _ord; int _rev; mb.undef(); mb.push( "N Name" ); mb.push( "M Name### (RTFM)" ); mb.push( "E Extension" ); mb.push( "S Size" ); mb.push( "T Modify Time" ); mb.push( "H Change Time" ); mb.push( "C Access Time" ); mb.push( "D Attr/mode" ); mb.push( "O Owner" ); mb.push( "G Group" ); mb.push( "Y Type (TP)" ); mb.push( "U Unsorted" ); mb.push( "---" ); mb.push( "R Randomize" ); mb.push( "V Move Entry" ); mb.push( "---" ); mb.push( "A Quick swap NAME/CHANGE" ); if ( vfu_menu_box( 50, 5, "Arrange" ) == -1 ) return; _ord = menu_box_info.ec; if ( _ord == 'A' ) { opt.sort_direction = 'A'; opt.sort_order = opt.sort_order == 'N' ? 'H' : 'N'; vfu_sort_files(); say1( VString( "File list is now arranged by " ) + ( opt.sort_order == 'N' ? "NAME (ASCENDING)" : "CHANGE TIME (ASCENDING)" ) ); return; } if (_ord == 'V' ) { vfu_file_entry_move(); return; } if (_ord == '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 == 'U' ) { say1( "Next directory rescan will be unsorted." ); return; } mb.undef(); mb.push( "A Ascending"); mb.push( "D Descending" ); if ( vfu_menu_box( 50, 5, "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 KEY_UP : vfu_nav_up(); break; case KEY_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-4.22/vfu/COPYING0000644000175000017500000004307014145574042012460 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-4.22/vfu/see.cpp0000644000175000017500000013552014145574042012707 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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"; char bg_xlat_table[2][64] = { " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º¼¾¿€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—?™šœžŸ", "abwgdevzijklmnoprstufhc`[]yxuqABWGDEVZIJKLMNOPRSTUFHC`[]YXUQ" }; char bgw_xlat_table[2][64] = { "àáâãäåæçèéêëìíîïðñòóôõö÷øùúüþÿÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÜÞß", "abwgdevzijklmnoprstufhc`[]yxuqABWGDEVZIJKLMNOPRSTUFHC`[]YXUQ" }; #define MAXCOLS 1024 /*--------------------------------------------------------------------*/ 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 + 32]; /* +32 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 += "|"; 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 ); 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 ); 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 ); int show_lmark = 0; int show_rmark = 0; int show_eol = -1; if ( col > 0 ) { if (col >= z) { buff[0] = 0; show_lmark = 1; z = 0; } else { str_trim_left( buff, col ); z -= col; } } if ( z > cols ) { buff[cols] = 0; show_rmark = 1; } else { if ( opt->show_eol && !show_lmark ) show_eol = z+1; } str_pad( buff, -cols ); con_out( 1, opt->ymin+y, buff, (opt->grid && y%2==0) ? opt->ch : opt->cn); if ( re.ok() && re.m( buff ) ) con_out( re.sub_sp(0)+1, opt->ymin+y, re.sub(0), CONCOLOR( cBLACK, cWHITE ) ); if (show_lmark) con_out(1,opt->ymin+y,"<",chRED); if (show_rmark) con_out( opt->xmax, opt->ymin+y, ">", chRED ); if (show_eol != -1) 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 ) con_out( opt->xmax - 7, opt->ymin, "BG XLAT", chRED ); if ( xlat == 2 ) 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: " ); if (!TextInput( 15, opt->ymax, "", 20, 20, &sss )) { draw(); return; } 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: " ); if (!TextInput( 15, opt->ymax, "", 20, 20, &sss )) { draw(); return; } 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 ) { //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; sss = opt->last_search; if(!TextInput( opt->xmin+ii, opt->ymax, "", opt->xmax-ii-4, opt->xmax-ii-4, &sss )) { draw(); return 1; } 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 KEY_RIGHT : if (bytepos == 0 && !in_text) bytepos = 1; else if (epos < editbs - 1) { epos++; if (!in_text) bytepos = 0; } break; case KEY_LEFT : if (bytepos == 1 && !in_text ) bytepos = 0; else if (epos > 0) { epos--; if (!in_text) bytepos = 1; } break; case KEY_DOWN : if ( epos + rowsz < editbs ) epos += rowsz; break; case KEY_UP : if ( epos - rowsz >= 0 ) epos -= rowsz; break; case KEY_PPAGE : epos = epos % rowsz; break; case KEY_NPAGE : epos = editbs - editbs % rowsz + epos % rowsz; if (epos >= editbs) epos = editbs - 1; break; case KEY_HOME : epos = epos - epos % rowsz; bytepos = 0; break; case KEY_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 = KEY_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 = KEY_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 = KEY_CTRL_L; if ( ch == 27 || ch == '-' || ch == 'q' || ch == KEY_ALT_X || ch == KEY_BACKSPACE ) return ch; int z = 0; while( escape_keys[z] ) if ( escape_keys[z++] == ch ) return ch; switch(ch) { case KEY_F1 : case KEY_ALT_H : case '?' : case 'h' : case 'H' : help(); break; case KEY_UP : up(); draw(); break; case 13 : case KEY_DOWN : down(); draw(); break; case 'b' : case 'B' : case KEY_PPAGE : for ( z = 0; z < rows; z++ ) up(); draw(); break; case ' ' : case KEY_NPAGE : for ( z = 0; z < rows; z++ ) down(); draw(); break; case 'p' : case 'P' : case KEY_HOME : if (fpos == 0) col = 0; else home(); draw(); break; case KEY_END : end(); draw(); break; case KEY_CTRL_E : end2(); draw(); break; case KEY_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 KEY_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 KEY_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 KEY_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 ); 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++; i--; } continue; } buff[z] = ch; z++; if ( ch == '\n' ) break; } 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( "" ); /* 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 += "|"; 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 ); 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 ); 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( "" ); int c = real_col(); VString str = va[sv.pos()]; VString 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] == ' ' || str[z] == '\t') ) { if ( str[z] == '\t' ) nc += opt->tabsize; else nc++; z++; } str = va[sv.pos()]; col = nc; while( nc-- ) str_ins_ch( str, 0, ' ' ); 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( int ch ) { if ( ch < 0 || ch > 255 ) return; if ( ch == 13 || ch == 10 ) { kenter(); return; } mod = 1; if ( va.count() == 0 ) va.push( "" ); VString 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, ' ' ); str_ins_ch( str, c, ch ); 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 ) { 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, "" ); } 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; VString str = va[n]; str_cut_right( str, " \t\n\r" ); va.set( n, str ); } else for ( int z = 0; z < va.count(); z++ ) { VString str = va[z]; str_cut_right( str, " \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 ); sss = opt->last_pipe_cmd; if(!TextInput( opt->xmin+ii, opt->ymax, "", opt->xmax-ii-4, opt->xmax-ii-4, &sss )) { draw(); return; } 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++ ) { VString 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] == '\\' ) 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 ) { VString sss; status( "Find %s: ", no_case?"(no case)":"(case sense)" ); int ii = str_len(sss)+2; sss = opt->last_search; if(!TextInput( opt->xmin+ii, opt->ymax, "", opt->xmax-ii-4, opt->xmax-ii-4, &sss )) { draw(); return 1; } str_sleft( sss, MAX_SEARCH_LEN ); strcpy( opt->last_search, sss ); opt->no_case = no_case; if ( opt->no_case ) str_up( opt->last_search ); return find_next(); } /*--------------------------------------------------------------------*/ void SeeEditor::help() { con_out( 1, 1, help_str ); do_draw = 1; con_getch(); } /*--------------------------------------------------------------------*/ int SeeEditor::run() { con_cshow(); draw(); set_cursor(); int key; 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; key = con_getch(); if( key == 0 ) key = KEY_CTRL_L; if (key == KEY_CTRL_C) { mod = 0; /* it is `quit' i.e. no save so this should be ok */ return key; } if (key == KEY_CTRL_X) { save(); return key; } else if ( key == 27 || key == KEY_ALT_X ) { if ( request_quit() == 0 ) return key; else continue; } if ( key == KEY_CTRL_K ) { pend = key; con_out( SEEDCOL, SEEDROW, "^K", opt->cs ); set_cursor(); key = con_getch(); draw_line( sv.pos() ); } switch( key ) { case KEY_CTRL_N : case KEY_DOWN : down(); break; case KEY_CTRL_P : case KEY_UP : up(); break; case KEY_CTRL_B : case KEY_LEFT : left(); break; case KEY_CTRL_F : case KEY_RIGHT : right(); break; case KEY_CTRL_U : if ( pend == KEY_CTRL_K ) sv.home(); else sv.ppage(); break; case KEY_PPAGE : sv.ppage(); break; case KEY_CTRL_V : if ( pend == KEY_CTRL_K ) sv.end(); else sv.npage(); break; case KEY_NPAGE : sv.npage(); break; case KEY_CTRL_A : case KEY_HOME : home(); break; case KEY_CTRL_E : case KEY_END : end(); break; case KEY_INSERT : opt->insert = !opt->insert; break; case KEY_CTRL_Y : remove_line(); break; /* SeedKxxx functions are for KEYxxx handles */ case KEY_ALT_H : case KEY_F1 : help(); break; case KEY_CTRL_S : case KEY_F2 : save(); break; case KEY_CTRL_D : if ( pend == KEY_CTRL_K ) save(); else kdel(); break; case KEY_ALT_F : find( 1 ); break; case KEY_ALT_S : find( 0 ); break; case KEY_ALT_G : case KEY_F3 : find_next(); break; case KEY_DEL : kdel(); break; #ifndef _TARGET_GO32_ case KEY_BACKSPACE : #endif case KEY_CTRL_H : kbs(); break; case 10 : case 13 : kenter(); break; case KEY_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 KEY_CTRL_W : insert_pipe_cmd(); break; case KEY_CTRL_T : opt->auto_indent = !opt->auto_indent; status( (opt->auto_indent) ? "AutoIndent ON" : "AutoIndent OFF" ); break; case KEY_ALT_0 : case KEY_ALT_1 : case KEY_ALT_2 : case KEY_ALT_3 : case KEY_ALT_4 : case KEY_ALT_5 : case KEY_ALT_6 : case KEY_ALT_7 : case KEY_ALT_8 : case KEY_ALT_9 : if (key == KEY_ALT_0) key = KEY_ALT_9+1; return key; case 27 : return key; default : kinsert( key ); 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-4.22/vfu/vfuarc.h0000644000175000017500000000117314145574042013062 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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-4.22/vfu/vfusys.h0000644000175000017500000000367614145574042013145 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _VFUSYS_H_ #define _VFUSYS_H_ #include #include #include /* following defines are taken from coreutils-5.2.1 Copyright (C) 1989, 1991-2004 Free Software Foundation, Inc. to address the file size problem when file is larger than 2GB */ #ifndef CHAR_BIT # define CHAR_BIT 8 #endif #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0)) #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t))) /* 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) #ifdef _TARGET_GO32_ int file_get_sfn( const char *in, char *out ); int file_get_lfn( const char *in, char *out ); #endif #endif /* _VFUSYS_H_ */ /* eof vfusys.h */ vfu-4.22/vfu/vfucopy.h0000644000175000017500000001110714145574042013265 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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(); void clipboard_paste( int mode ); void clipboard_clear(); void clipboard_view(); void clipboard_menu( int act ); #endif //_VFUCOPY_H_ vfu-4.22/vfu/mm.conf0000644000175000017500000000346614145576401012713 0ustar cadecade############################################################################ # # 1996-2020 (c) Vladi Belperchinov-Shabanski "Cade" # http://cade.datamax.bg/ # ############################################################################ CC = ! LD = ! AR = ? ar RANLIB = ? ranlib LD = $(CXX) [vfu] DEFAULT = 1 CC = $(CXX) LD = $(LD) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I../vslib -I/usr/include/ncurses -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -L../vstring -L../vstring/pcre2 -L../vslib -lvstring -lvslib -lvscon -lpcre2 -lncurses $(LDFLAGS) $(LDDEF) SRC = *.cpp [vfu.yas] CC = $(CXX) LD = $(LD) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I../vslib -I../yascreen -DUSE_YASCREEN -O2 $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -L../vstring -L../vstring/pcre2 -L../vslib -L../yascreen -lvstring -lvslib -lvscony -lpcre2 ../yascreen/libyascreen.a -lrt $(LDFLAGS) $(LDDEF) SRC = *.cpp [vfu-debug] CC = $(CXX) LD = $(LD) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I../vslib -I/usr/include/ncurses -O0 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -L../vstring -L../vstring/pcre2 -L../vslib -lvstring -lvslib -lvscon -lpcre2 -lncurses -g $(LDFLAGS) $(LDDEF) SRC = *.cpp [vfu.yas-debug] CC = $(CXX) LD = $(LD) AR = $(AR) rv RANLIB = $(RANLIB) CCFLAGS = -I../vstring -I../vstring/pcre2 -I../vslib -I../yascreen -DUSE_YASCREEN -O0 -g $(CFLAGS) $(CPPFLAGS) $(CCDEF) LDFLAGS = -L../vstring -L../vstring/pcre2 -L../vslib -L../yascreen -lvstring -lvslib -lvscony -lpcre2 ../yascreen/libyascreen.a -lrt -g $(LDFLAGS) $(LDDEF) SRC = *.cpp ############################################################################ vfu-4.22/vfu/vfutools.cpp0000644000175000017500000002436114145574042014014 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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, char ch, char *tmp ) { tmp[0] = 0; strcpy( tmp, str_file_path( fname ) ); if (ch == 'N') { strcat( tmp, str_file_name( fname ) ); if (strlen(fname) == strlen(tmp)) strcat( tmp, ".---" ); } else if (ch == 'E') { strcat( tmp, str_file_ext( fname ) ); if (strlen(tmp) == 0) strcat( tmp, "---" ); } else { strcat( tmp, str_file_name( fname ) ); str_sleft( tmp, ch - '0' ); if (strlen(fname) == strlen(tmp)) strcat( tmp, ".---" ); } } void vfu_tool_classify() { /* FIXME: how this will handle files with path in the list? */ fname_t tmp; if ( sel_count == 0 ) { say1( "Classify function works only on already selected files..." ); return; } mb.undef(); mb.push( "N Name "); mb.push( "E Ext" ); mb.push( "1 First 1 letter " ); mb.push( "2 First 2 letters" ); mb.push( "3 First 3 letters" ); mb.push( "4 First 4 letters" ); mb.push( "5 First 5 letters" ); mb.push( "6 First 6 letters" ); mb.push( "7 First 7 letters" ); mb.push( "8 First 8 letters" ); mb.push( "9 First 9 letter" ); if ( vfu_menu_box( 32, 6, "Classify files by") == -1 ) return; char ch = menu_box_info.ec; mb.undef(); 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(), ch, tmp ); int found = 0; for ( i = 0; i < mb.count(); i++ ) if ( pathcmp( tmp, mb[i] ) == 0 ) { found = 1; break; } if ( ! found ) mb.push( tmp ); } for ( i = 0; i < mb.count(); i++ ) { if( dir_exist( mb[i] ) ) continue; int res = make_path( mb[i] ); if ( res ) { fname_t t; sprintf( t, "Cannot create directory: %s, (press a key for cancel)", mb.get(i) ); say1( t ); say2errno(); con_getch(); 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(), ch, 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( "1 README.TXT => readme.txt" ); mb.push( "2 README.TXT => readme.TXT" ); mb.push( "3 README.TXT => README.txt" ); mb.push( "4 readme.txt => README.TXT" ); mb.push( "5 readme.txt => README.txt" ); mb.push( "6 readme.txt => readme.TXT" ); mb.push( "_ Replace spaces with _" ); mb.push( "Y Simplify name (RTFM)" ); mb.push( "S Sequential rename" ); mb.push( "T Prefix with current date+time" ); mb.push( "D Prefix with current date" ); mb.push( "W Swap SymLink with Original" ); mb.push( "R Replace SymLink with Original" ); if (vfu_menu_box( 32, 6, "Rename Tools" ) == -1) return; switch( menu_box_info.ec ) { case 'S' : vfu_tool_seq_rename(); break; case 'R' : vfu_tool_replace_sym_org(0); break; case 'W' : vfu_tool_replace_sym_org(1); break; case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '_' : case '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 == '1' || menu_box_info.ec == '2') str_low( t ); if (menu_box_info.ec == '4' || menu_box_info.ec == '5') str_up( t ); new_name += t; t = str_file_ext( fi->name() ); if (menu_box_info.ec == '1' || menu_box_info.ec == '3') str_low( t ); if (menu_box_info.ec == '4' || menu_box_info.ec == '6') str_up( t ); if (strlen(t) > 0) { new_name += "."; new_name += t; } if (menu_box_info.ec == '_') str_tr( new_name, " ", "_" ); if (menu_box_info.ec == '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) /* FIXME: full name ? */ { fi->set_name( new_name ); do_draw = 2; /* FIXME: this should be optimized? */ } else err++; } else err++; } sprintf( t, "Rename complete (errors: %d)", err ); say1( t ); break; case 'T' : case '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 == '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->is_dir() ) continue; // why not? ;) 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) /* FIXME: full name ? */ { fi->set_name( new_name ); do_draw = 2; /* FIXME: this should be optimized? */ } 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; /* FIXME: this should be optimized? */ 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; // FIXME: dali? 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) { symlink( sym, org ); } fi->update_stat(); do_draw = 2; /* FIXME: this should be optimized? */ } char t[256]; sprintf( t, "Replace complete (errors: %d)", err ); say1( t ); } /*------------------------------------------------------------------------*/ /* eof vfutools.cpp */ vfu-4.22/vfu/vfuopt.cpp0000644000175000017500000003534414151762154013461 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.datamax.bg/ * * 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 char *NOYES[] = { " - ", "YES", NULL }; const char *NOYESPRECOPY[] = { " - ", "YES", "PRELIM", NULL }; const char *FTIMETYPE[] = { "CHANGE", "MODIFY", "ACCESS", NULL }; #ifdef _TARGET_GO32_ const char *TAGMARKS[] = { ">>", "=>", "->", "Í", "Ä", " ¯", "¯¯", NULL }; #else const char *TAGMARKS[] = { ">>", "=>", "->", NULL }; #endif const char *SIIEC[] = { "IEC", "SI ", NULL }; const char *COMMA_TYPES[] = { "'", "`", ",", " ", "_", NULL }; ToggleEntry Toggles[] = { // { "[a] 1234567890123456", &(opt.some) }, { 0 , "--screen--", NULL, NULL }, { '1', "Show Mode field", &(opt.f_mode), NOYES }, { '2', "Show Owner field", &(opt.f_owner), NOYES }, { '3', "Show Group field", &(opt.f_group), NOYES }, { '4', "Show Time field", &(opt.f_time), NOYES }, { '5', "Show Size field", &(opt.f_size), NOYES }, { '6', "Show Type field", &(opt.f_type), NOYES }, { '7', " Time Type ", &(opt.f_time_type), FTIMETYPE }, { '8', "Long name view ", &(opt.long_name_view), NOYES }, { ' ', "TagMark type ", &(opt.tag_mark_type), TAGMARKS }, { ' ', "Use colors ", &(opt.use_colors), NOYES }, { ' ', "Use /etc/DIR_COLORS", &(opt.use_dir_colors), NOYES }, { ' ', "Lowercase extensions for configs", &(opt.lower_case_ext_config), NOYES }, { '.', "Show hidden files", &(opt.show_hidden_files), NOYES }, { 0 , "--navigation--", NULL, NULL }, { 'i', "Use internal viewer", &(opt.internal_browser), NOYES }, { 'I', "Use internal editor", &(opt.internal_editor), NOYES }, { ' ', "Use menu borders", &(opt.menu_borders), NOYES }, { 0 , "--trees/dirs-- " , NULL, NULL }, { ' ', "Compact DirTree", &(opt.tree_compact), NOYES }, { ' ', "CDTree (cdpath) ", &(opt.tree_cd), NOYES }, { 0 , "--troubleshooting--", NULL, NULL }, { ' ', "Clear screen on shell", &(opt.shell_cls), NOYES }, { 0 , "--compatibility--", NULL, NULL }, { ' ', "Lynx style navigation", &(opt.lynx_navigation), NOYES }, { ' ', "Mask auto expand", &(opt.mask_auto_expand), NOYES }, { ' ', "Use CWD as target for COPY/MOVE", &(opt.default_copy_to_cwd), NOYES }, { 0 , "--other--", NULL, NULL }, /* { ' ', "Can Zap/Erase READ-ONLY Files?!", &(opt.zap_ro), NOYES }, ? */ { ' ', "Keep directories on top of the list", &(opt.sort_top_dirs), NOYES }, { ' ', "Smart HOME/END keys (only Top Dirs mode)", &(opt.smart_home_end), NOYES }, { ' ', "Case insensitive file/dir names matching", &(opt.no_case_glob), NOYES }, { 'b', "Allow beep!", &(opt.allow_beep), NOYES }, { 's', "Free space check on copy", &(opt.copy_free_space_check), NOYES }, { ' ', "Auto mount on change dir", &(opt.auto_mount), NOYES}, { ' ', "Preserve selection (after rescan)", &(opt.keep_selection), NOYES}, { ' ', "Preserve ownership/mode on copy?", &(opt.copy_keep_mode), NOYES }, { ' ', "Show user's free space", &(opt.show_user_free), NOYES }, { ' ', "Calc/Show bytes on copy", &(opt.copy_calc_totals), NOYESPRECOPY }, { ' ', "Calc/Show bytes freed on erase", &(opt.bytes_freed), NOYES }, { ' ', "Prefer GiB in disk usage status", &(opt.use_gib_usage), NOYES }, { ' ', "Show file/dir sizes in units", &(opt.use_si_sizes), SIIEC }, { ' ', "1000s separator type", &(opt.comma_type), COMMA_TYPES }, { -1, "---", 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 KEY_IC; if ( strcmp( key_name, "INS" ) == 0 ) return KEY_IC; if ( strcmp( key_name, "INSERT" ) == 0 ) return KEY_IC; if ( strcmp( key_name, "ENTER" ) == 0 ) return KEY_ENTER; if ( strcmp( key_name, "RETURN" ) == 0 ) return KEY_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 KEY_F1 + atoi( key_name + 1 ) - 1; else if ( toupper(key_name[0]) == '@' ) return KEY_ALT_F1 + atoi( key_name + 2 ) - 1; else if ( toupper(key_name[0]) == '^' ) return KEY_CTRL_F1 + atoi( key_name + 2 ) - 1; else if ( toupper(key_name[0]) == '#' ) return KEY_SH_F1 + atoi( key_name + 2 ) - 1; } return 0; } /*---------------------------------------------------------------------------*/ void vfu_settings_load() { VString str; user_externals.undef(); history.undef(); see_filters.undef(); panelizers.undef(); archive_extensions.undef(); path_bookmarks.undef(); /***** LOAD DEFAULTS *******/ memset( &opt, 0, sizeof( opt ) ); opt.svo.reset(); opt.seo.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; /***** LOAD DEFAULTS END ***/ FILE *fsett; Options tmp_opt; memset( &tmp_opt, 0, sizeof( tmp_opt ) ); 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 ); 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" ); char line[1024]; if ( (fsett = fopen( filename_conf, "r")) ) { while(fgets(line, 1024, fsett)) { if ( line[0] == '#' ) continue; if ( line[0] == ';' ) continue; str_cut( line, "\n\r" ); 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; } } fclose(fsett); } #ifdef _TARGET_GO32_ int z; for ( z = 0; z < 16; z++ ) str_low( ext_colors[z] ); #endif if (opt.use_dir_colors) vfu_load_dir_colors(); // if (file_load_crc32( filename_size_cache, &size_cache, sizeof(size_cache))) // memset( &size_cache, 0, sizeof(size_cache) ); size_cache_load(); } /*---------------------------------------------------------------------------*/ 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", filename_conf ); str_replace( line, "%F", 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, "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-4.22/vfu/vfusys.cpp0000644000175000017500000001735314145574042013475 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #include "vfu.h" #include "vfusys.h" #include "vfuuti.h" #include "vfumenu.h" #ifdef _TARGET_GO32_ #include #endif /*###########################################################################*/ 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'; #ifndef _TARGET_GO32_ 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'; #endif #ifdef _TARGET_GO32_ mod_str[4]=mod_str[5]=mod_str[6]=mod_str[7]=mod_str[8]=mod_str[9]='-'; #endif } /*---------------------------------------------------------------------------*/ 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; #ifndef _TARGET_GO32_ 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; #endif 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 char* AONOFF1[] = { "YES", " - ", " ? ", NULL }; const char* AONOFF2[] = { "YES", " - ", NULL }; #define AONOFF ( allow_masking ? AONOFF1 : AONOFF2 ) ToggleEntry mode_toggles[] = { { ' ', "Read Owner", &mode_i[ 1], AONOFF }, { ' ', "Write Owner", &mode_i[ 2], AONOFF }, { ' ', "Exec/Srch Owner", &mode_i[ 3], AONOFF }, { ' ', "Read Group", &mode_i[ 4], AONOFF }, { ' ', "Write Group", &mode_i[ 5], AONOFF }, { ' ', "Exec/Srch Group", &mode_i[ 6], AONOFF }, { ' ', "Read Other", &mode_i[ 7], AONOFF }, { ' ', "Write Other", &mode_i[ 8], AONOFF }, { ' ', "Exec/Srch Other", &mode_i[ 9], AONOFF }, { ' ', "Set user id", &mode_i[10], AONOFF }, { ' ', "Set group id", &mode_i[11], AONOFF }, { ' ', "Sticky Bit", &mode_i[12], AONOFF }, { -1, "---", NULL, NULL }, }; if ( !vfu_toggle_box( 50, 5, "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; } /*---------------------------------------------------------------------------*/ #ifdef _TARGET_GO32_ #include #include int file_get_sfn( const char *in, char *out ) { fname_t src; fname_t dst; strcpy( src, in ); __dpmi_regs r; dosmemput(src, strlen (src)+1, __tb); r.x.ax = 0x7160; /* Truename */ r.x.cx = 1; /* Get short name */ r.x.ds = r.x.es = __tb / 16; r.x.si = r.x.di = __tb & 15; __dpmi_int(0x21, &r); if (r.x.flags & 1 || r.x.ax == 0x7100) { strcpy( out, in ); return -1; } dosmemget (__tb, MAX_PATH, dst); strcpy( out, dst ); return 0; } int file_get_lfn( const char *in, char *out ) { fname_t src; fname_t dst; strcpy( src, in ); __dpmi_regs r; dosmemput(src, strlen (src)+1, __tb); r.x.ax = 0x7160; /* Truename */ r.x.cx = 2; /* Get long name */ r.x.ds = r.x.es = __tb / 16; r.x.si = r.x.di = __tb & 15; __dpmi_int(0x21, &r); if (r.x.flags & 1 || r.x.ax == 0x7100) { strcpy( out, in ); return -1; } dosmemget (__tb, MAX_PATH, dst); strcpy( out, dst ); return 0; } #endif /* _TARGET_GO32_ */ /*###########################################################################*/ /* eof vfusys.cpp */ vfu-4.22/vfu/vfu.vpj0000644000175000017500000000240614145574042012744 0ustar cadecade[CONFIGURATIONS] activeconfig=,Release config=,Release [GLOBAL] workingdir=/home/cade/pro/vfu vcsproject=VCS:vfu macro=\n version=6.0 [ASSOCIATION] [FILES] see.cpp vfu.cpp vfu.h vfuarc.cpp vfuarc.h vfucopy.cpp vfucopy.h vfudir.cpp vfudir.h vfufiles.cpp vfufiles.h vfumenu.cpp vfumenu.h vfuopt.cpp vfuopt.h vfusetup.h vfusys.cpp vfusys.h vfutools.cpp vfutools.h vfuuti.cpp vfuuti.h vfuview.cpp vfuview.h [STATE] SCREEN: 800 600 -4 -4 800 549 0 0 N 0 0 0 0 651 494 CWD: /home/cade/pro/vfu FILEHIST: 9 /home/cade/pro/vfu/vfuopt.cpp /home/cade/pro/vfu/vfuopt.h /home/cade/pro/vfu/vfuview.cpp /home/cade/pro/vfu/vfudir.h /home/cade/pro/vfu/vfumenu.cpp /home/cade/pro/vfu/vfufiles.cpp /home/cade/pro/vfu/vfu.h /home/cade/pro/vfu/vfu.cpp /home/cade/get [COMPILER.Release] FILTEREXPANSION=1 1 0 1 1 includedirs=%(INCLUDE) tagfiles= reffile= compile=copts: concur|capture|menu: Compile:&Compilecmd: make %n.o make=copts: concur|capture|clear|saveall|menu: Build:&Buildcmd: make rebuild=copts: concur|capture|menu: Rebuild:&Rebuildcmd: debug=copts: menu: Debug:&Debugcmd: xxgdb [exename-here] execute=copts: concur|capture|clear|saveall|menu: Execute:E&xecutecmd: make && rxvt -ls -e vfu user1=copts: hide|menu: User 1:User 1cmd: user2=copts: hide|menu: User 2:User 2cmd: vfu-4.22/vfu/see.h0000644000175000017500000001325714145574042012356 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * SEE `README',`LICENSE' OR `COPYING' FILE FOR LICENSE AND OTHER DETAILS! * ****************************************************************************/ #ifndef _SEE_H_ #define _SEE_H_ #include #define SEE_VERSION "4.10" #define MAX_SEARCH_LEN 128 #define MAX_PIPE_LEN 128 #define MAX_ESCAPE_KEYS 64 #define BUFF_SIZE (16*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 = BUFF_SIZE; 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 */ char 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 */ VArray 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( int ch ); 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-4.22/vfu/vfuuti.h0000644000175000017500000000364314145574042013122 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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 */ int vfu_ask( const char *prompt, const char *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 ); /*###########################################################################*/ 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 char* 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(); /*###########################################################################*/ #endif//_VFUUTI_H_ vfu-4.22/vfu/vfuarc.cpp0000644000175000017500000001474314145574042013424 0ustar cadecade/**************************************************************************** * * Copyright (c) 1996-2021 Vladi Belperchinov-Shabanski "Cade" * http://cade.noxrun.com/ * * 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" /*---------------------------------------------------------------------------*/ 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 s; s = "/usr/libexec/vfu/rx_auto "; s += ( a_recursive ) ? "v" : "l"; s += " '" + archive_name + "' "; s += " '" + archive_path + "' "; s += " 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; } /* FIXME: my man page for stat() says S_IFDIR is not POSIX?! */ } else if ( strncmp( line, "MODE:", 5 ) == 0 ) { VString ms = line + 5; if( ms[0] == 'd' ) st.st_mode |= S_IFDIR; // FIXME: move this modeline parse to function and port back to vfufiles? 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; #ifndef _TARGET_GO32_ 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; #endif } 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(); if(mkdir( tmpdir, S_IRUSR|S_IWUSR|S_IXUSR /*|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH*/ ) || chdir( tmpdir ) ) { say1( "error: cannot create temp directory" ); say2( tmpdir ); return; } VString fn = files_list_get(FLI)->full_name(); VString s; s = "/usr/libexec/vfu/rx_auto x \""; s += work_path; s += archive_name; s += "\" "; s += fn; s += " 2> /dev/null"; vfu_shell( s, "" ); chdir( tmpdir ); /* FIXME: a little hack -- vfu_shell() changes current path */ vfu_browse( fn ); chdir( work_path ); __vfu_dir_erase( tmpdir ); say1( "" ); } /*---------------------------------------------------------------------------*/ void vfu_user_external_archive_exec( VString &shell_line ) { VString tmpdir = vfu_temp(); if(mkdir( tmpdir, S_IRUSR|S_IWUSR|S_IXUSR /*|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH*/ ) || chdir( tmpdir )) { say1( "error: cannot create temp directory" ); say2( tmpdir ); return; } VString fn = files_list_get(FLI)->full_name(); VString s; s = "/usr/libexec/vfu/rx_auto x \""; s += work_path; s += archive_name; s += "\" "; s += fn; s += " 2> /dev/null"; vfu_shell( s, "" ); chdir( tmpdir ); /* FIXME: a little hack -- vfu_shell() changes current path */ str_replace( shell_line, "%f", fn ); str_replace( shell_line, "%F", fn ); vfu_shell( shell_line, "" ); chdir( work_path ); __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 s; s = "/usr/libexec/vfu/rx_auto x \""; s += work_path; s += archive_name; s += "\" @"; s += tmpfile; s += " 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-4.22/vfu/AUTHORS0000644000175000017500000000021414145574042012466 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-4.22/CONFIG0000644000175000017500000000366214145574023011517 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 10 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 http://cade.datamax.bg/vfu ------------------------------------------------------------------------ vfu-4.22/build.netbsd0000755000175000017500000000115514145574023013125 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-4.22/TODO0000644000175000017500000000072114145574023011310 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-4.22/sub.list0000644000175000017500000000003314145574204012303 0ustar cadecadeyascreen vstring vslib vfu vfu-4.22/COPYING0000644000175000017500000004307014145574023011657 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-4.22/vfu.lsm0000644000175000017500000000236614151762146012146 0ustar cadecadeBegin3 Title: VFU File Manager Version: 4.22 Entered-date: 01DEC2021 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@datamax.bg (Vladi Belperchinov-Shabanski) Maintained-by: cade@biscom.net (Vladi Belperchinov-Shabanski) Primary-site: http://cade.noxrun.com/projects/vfu/ 500kB vfu-4.22.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-4.22/configure.ac0000644000175000017500000000414714145574023013114 0ustar cadecade# The values here are used by the configure script and defined as # macros in the generated config.h when ./configure is run. Traditionally the # third field is the "bug report" address, but using a URL is acceptable AC_INIT([vfu], [4.18], [https://github.com/cade-vs/vfu/issues],,[http://cade.datamax.bg/vfu/]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([subdir-objects foreign no-dependencies]) AC_PREREQ(2.69) AC_CONFIG_HEADERS([config.h:config.in]) if test -f configure.ac; then AC_MSG_ERROR([configure should be run in a separate build directory], [1]) fi # Don't add -g and -O2 by default : ${CFLAGS=""} # RELEASE TODO: set this to "no" # This will add debug and profiling options to ./configure AX_CHECK_ENABLE_DEBUG([yes]) AC_PROG_CC # yascreen use C, not CXX AC_PROG_CXX AC_CONFIG_MACRO_DIRS([build-aux/m4]) AC_PROG_INSTALL AC_SYS_LARGEFILE # Allows for building static libraries AC_PROG_RANLIB # This prevents problems such as those shown in # https://github.com/theimpossibleastronaut/rmw/commit/90200c2df06b16f16b5d21d25c51966c0ee65b23#commitcomment-30291627 # and https://github.com/theimpossibleastronaut/rmw/issues/21 AM_MAINTAINER_MODE AX_PATH_LIB_PCRE AX_WITH_CURSES AC_ARG_ENABLE( yascreen, AC_HELP_STRING(--enable-yascreen, use yascreen instead of curses/ncurses) ) if test "x$enable_yascreen" = xyes || test "x$ax_cv_curses" = xno; then AC_MSG_NOTICE(-- vfu will build with yascreen support) want_yascreen="yes" AM_CONDITIONAL([WANT_YASCREEN], [test "x$want_yascreen" = xyes]) AC_DEFINE([USE_YASCREEN], [1], [Define to 1 if yascreen was requested]) CXXFLAGS="$CXXFLAGS -I$ac_abs_confdir/yascreen" LDFLAGS="-L../yas-libs" LIBS="-lrt -lyascreen" else AM_CONDITIONAL([WANT_YASCREEN], [test "x$want_yascreen" = xno]) CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" LIBS="$CURSES_LIBS" fi # Defining HAVE_CONFIG_H is needed because we are using different sets of Makefiles DEFS="$DEFS -DHAVE_CONFIG_H" AC_CONFIG_FILES([ Makefile yas-libs/Makefile vslib/Makefile vslib/t/Makefile vfu/Makefile]) AC_OUTPUT vfu-4.22/vfu.wiki0000644000175000017500000002364514145574023012317 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 == [[http://cade.datamax.bg/vfu/|vfu homepage]] CategoryDocumentation vfu-4.22/yas-libs/0000755000175000017500000000000014145574023012343 5ustar cadecadevfu-4.22/yas-libs/Makefile.in0000644000175000017500000003702314145574023014415 0ustar cadecade# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = yas-libs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = \ $(top_srcdir)/build-aux/m4/ax_check_enable_debug.m4 \ $(top_srcdir)/build-aux/m4/ax_path_lib_pcre.m4 \ $(top_srcdir)/build-aux/m4/ax_require_defined.m4 \ $(top_srcdir)/build-aux/m4/ax_with_curses.m4 \ $(top_srcdir)/build-aux/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AR = ar ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libyascreen_a_AR = $(AR) $(ARFLAGS) libyascreen_a_LIBADD = am__libyascreen_a_SOURCES_DIST = $(top_srcdir)/yascreen/yascreen.c am__dirstamp = $(am__leading_dot)dirstamp @WANT_YASCREEN_TRUE@am_libyascreen_a_OBJECTS = \ @WANT_YASCREEN_TRUE@ $(top_srcdir)/yascreen/yascreen.$(OBJEXT) libyascreen_a_OBJECTS = $(am_libyascreen_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__depfiles_maybe = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libyascreen_a_SOURCES) DIST_SOURCES = $(am__libyascreen_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CURSES_CFLAGS = @CURSES_CFLAGS@ CURSES_LIBS = @CURSES_LIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE_CFLAGS = @PCRE_CFLAGS@ PCRE_LIBS = @PCRE_LIBS@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__leading_dot = @am__leading_dot@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @WANT_YASCREEN_TRUE@noinst_LIBRARIES = libyascreen.a @WANT_YASCREEN_TRUE@AM_CFLAGS = -Wall -I $(top_srcdir)/yascreen --std=gnu89 @WANT_YASCREEN_TRUE@libyascreen_a_SOURCES = $(top_srcdir)/yascreen/yascreen.c all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps yas-libs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign --ignore-deps yas-libs/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) $(top_srcdir)/yascreen/$(am__dirstamp): @$(MKDIR_P) $(top_srcdir)/yascreen @: > $(top_srcdir)/yascreen/$(am__dirstamp) $(top_srcdir)/yascreen/yascreen.$(OBJEXT): \ $(top_srcdir)/yascreen/$(am__dirstamp) libyascreen.a: $(libyascreen_a_OBJECTS) $(libyascreen_a_DEPENDENCIES) $(EXTRA_libyascreen_a_DEPENDENCIES) $(AM_V_at)-rm -f libyascreen.a $(AM_V_AR)$(libyascreen_a_AR) libyascreen.a $(libyascreen_a_OBJECTS) $(libyascreen_a_LIBADD) $(AM_V_at)$(RANLIB) libyascreen.a mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f $(top_srcdir)/yascreen/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(top_srcdir)/yascreen/$(am__dirstamp)" || rm -f $(top_srcdir)/yascreen/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: vfu-4.22/yas-libs/Makefile.am0000644000175000017500000000026614145574023014403 0ustar cadecadeif WANT_YASCREEN noinst_LIBRARIES = libyascreen.a AM_CFLAGS = -Wall -I $(top_srcdir)/yascreen --std=gnu89 libyascreen_a_SOURCES = $(top_srcdir)/yascreen/yascreen.c endif vfu-4.22/build.sh0000755000175000017500000000147614145574023012266 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-4.22/vfu.html0000644000175000017500000001231214145574023012305 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@biscom.net> <cade@datamax.bg>
 http://soul.datamax.bg/~cade/
 http://soul.datamax.bg/~cade/vfu
vfu-4.22/yascreen/0000755000175000017500000000000014145574104012431 5ustar cadecadevfu-4.22/yascreen/yascreen.c0000644000175000017500000015011414145574104014410 0ustar cadecade// $Id: yascreen.c,v 1.86 2021/09/07 04:13:16 bbonev Exp $ // // Copyright © 2015-2020 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 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 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 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 }; // }}} 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.86 $\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; s->state=ST_NORM; s->tstate=T_NORM; s->ustate=U_NORM; s->escto=YAS_DEFAULT_ESCTO; s->cursor=1; s->cursorx=0; s->cursory=0; 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->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); // be compatible with earlier versions that worked without output buffering // normally a recent client will set this to 0 and use explicit flush s->lineflush=1; // leave scr empty, so that on first refresh everything is redrawn s->redraw=1; 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_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; // cycle 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); } } // }}} 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; } // }}} inline void yascreen_feed(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; 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]=='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==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 'R': { // \e[n;mR - cursor position report 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); } break; } case 'A': // ^up - \e[1;5A if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') yascreen_pushch(s,YAS_K_C_UP); break; case 'B': // ^down - \e[1;5B if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') yascreen_pushch(s,YAS_K_C_DOWN); break; case 'C': // ^right - \e[1;5C if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') yascreen_pushch(s,YAS_K_C_RIGHT); break; case 'D': // ^left - \e[1;5D if (s->ansipos==6&&s->ansibuf[2]=='1'&&s->ansibuf[3]==';'&&s->ansibuf[4]=='5') yascreen_pushch(s,YAS_K_C_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 '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; } } // }}} inline int yascreen_getch_to(yascreen *s,int timeout) { // {{{ int64_t toms=timeout*1000,tto; struct timeval to,*pto=&to; fd_set r; memset(&r,0,sizeof r); // make clang static analyzer happier (llvm bug #8920) if (!s) return -1; 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 -1; if (STDOUT_FILENO<0) return -1; 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)) { yascreen_feed(s,c); 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 -1; if (!toms&&timeout>0) // timeout is finished return -1; 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; } } } // }}} 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; } // }}} inline int yascreen_peekch(yascreen *s) { // {{{ int ch=yascreen_getch_nowait(s); if (ch!=-1) 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; } // }}} vfu-4.22/yascreen/yascreen.vers0000644000175000017500000000013614145574104015143 0ustar cadecadeYASCREEN_1.79 { global: *; }; YASCREEN_1.83 { global: yascreen_line_flush; } YASCREEN_1.79; vfu-4.22/yascreen/yascreen.h0000644000175000017500000003071014145574104014414 0ustar cadecade// $Id: yascreen.h,v 1.41 2020/09/30 21:34:45 bbonev Exp $ // // Copyright © 2015-2020 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 #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)|0x200) // key codes typedef enum { YAS_K_NONE=-1, // 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=0x100, YAS_K_F2=0x101, YAS_K_F3=0x102, YAS_K_F4=0x103, YAS_K_F5=0x104, YAS_K_F6=0x105, YAS_K_F7=0x106, YAS_K_F8=0x107, YAS_K_F9=0x108, YAS_K_F10=0x109, YAS_K_F11=0x10a, YAS_K_F12=0x10b, YAS_K_S_F1=0x10c, YAS_K_S_F2=0x10d, YAS_K_S_F3=0x10e, YAS_K_S_F4=0x10f, YAS_K_S_F5=0x110, YAS_K_S_F6=0x111, YAS_K_S_F7=0x112, YAS_K_S_F8=0x113, YAS_K_S_F9=0x114, YAS_K_S_F10=0x115, YAS_K_S_F11=0x116, YAS_K_S_F12=0x117, YAS_K_C_F1=0x118, YAS_K_C_F2=0x119, YAS_K_C_F3=0x11a, YAS_K_C_F4=0x11b, YAS_K_C_F5=0x11c, YAS_K_C_F6=0x11d, YAS_K_C_F7=0x11e, YAS_K_C_F8=0x11f, YAS_K_C_F9=0x120, YAS_K_C_F10=0x121, YAS_K_C_F11=0x122, YAS_K_C_F12=0x123, YAS_K_A_F1=0x124, YAS_K_A_F2=0x125, YAS_K_A_F3=0x126, YAS_K_A_F4=0x127, YAS_K_A_F5=0x128, YAS_K_A_F6=0x129, YAS_K_A_F7=0x12a, YAS_K_A_F8=0x12b, YAS_K_A_F9=0x12c, YAS_K_A_F10=0x12d, YAS_K_A_F11=0x12e, YAS_K_A_F12=0x12f, YAS_K_LEFT=0x130, YAS_K_UP=0x131, YAS_K_DOWN=0x132, YAS_K_RIGHT=0x133, YAS_K_HOME=0x134, YAS_K_END=0x135, YAS_K_PGUP=0x136, YAS_K_PGDN=0x137, YAS_K_INS=0x138, YAS_K_DEL=0x139, YAS_K_C_LEFT=0x13a, YAS_K_C_UP=0x13b, YAS_K_C_DOWN=0x13c, YAS_K_C_RIGHT=0x13d, // 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=0x800, YAS_TELNET_SIZE=0x801, } 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 handling of telnet protocol 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 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); // 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); // 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-4.22/yascreen/.gitignore0000644000175000017500000000011514145574104014416 0ustar cadecade*.o CVS lib*.a yastest yastest.shared libyascreen.so* yascreen.pc yascreen.3 vfu-4.22/yascreen/yascreen.pc.in0000644000175000017500000000036414145574104015176 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-4.22/yascreen/.cvsignore0000644000175000017500000000010714145574104014427 0ustar cadecade*.o .git yastest yastest.shared libyascreen.so* yascreen.pc yascreen.3 vfu-4.22/yascreen/yastest.c0000644000175000017500000000713114145574104014273 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,"zÌ;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,"zÌ;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,"zÌ;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 -f GNUmakefile $(.TARGETS) vfu-4.22/yascreen/LICENSE0000644000175000017500000001674414145574104013452 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-4.22/yascreen/Makefile.main0000644000175000017500000000736314145574104015025 0ustar cadecade############################# configurable section ############################# # # Copyright © 2015-2020 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 # change options based on flto configuration ifndef NO_FLTO CCOPT:=-Wall -Wextra -I. --std=gnu89 -fPIC -flto else CCOPT:=-Wall -Wextra -I. --std=gnu89 -fPIC endif LDOPT:=-Wl,--version-script,yascreen.vers SONAME:=-Wl,-soname,libyascreen.so.$(SOVERM) ifeq ($(shell uname -s),Darwin) AR=ar RANLIB=ranlib LDOPT:= SONAME:= endif ifeq ($(shell uname -s),OpenBSD) ifeq ($(CC),cc) CC:=egcc endif AR=ar RANLIB=ranlib CCOPT:=-Wall -Wextra -I. --std=gnu89 LDOPT:= 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 $(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 $(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 644 -t $(DESTDIR)$(PREFIX)$(LIBDIR) libyascreen.a $(INSTALL) -D -m 644 -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 644 -t $(DESTDIR)$(PREFIX)$(LIBDIR) libyascreen.so.$(SOVERF) $(INSTALL) -D -m 644 -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 rebuild: $(MAKE) clean $(MAKE) -j all mkotar: $(MAKE) clean dh_clean $(MAKE) yascreen.3 tar \ --xform 's,^[.],yascreen-$(VER),' \ --exclude ./.git \ --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 all vfu-4.22/yascreen/README.md0000644000175000017500000006244014145574104013716 0ustar cadecadeYASCREEN 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 - 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 | Name | Value | Description | |------------------|------:|-----------------------| |`YAS_K_NONE` | -1 | no key is available; in time limited mode means that the time limit expired | |`YAS_SCREEN_SIZE` | 0x800 | notification for screen size change (may come because of telnet or ANSI sequence) | |`YAS_TELNET_SIZE` | 0x801 | 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`, `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 return after a timeout or double Esc | |`YAS_K_C_3` | 0x1b | Esc, Ctrl-3; see above; All ANSI sequences start with Esc, this is return 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 | `=` | |`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 | 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` | 0x100 | F1 | | `YAS_K_F2` | 0x101 | F2 | | `YAS_K_F3` | 0x102 | F3 | | `YAS_K_F4` | 0x103 | F4 | | `YAS_K_F5` | 0x104 | F5 | | `YAS_K_F6` | 0x105 | F6 | | `YAS_K_F7` | 0x106 | F7 | | `YAS_K_F8` | 0x107 | F8 | | `YAS_K_F9` | 0x108 | F9 | | `YAS_K_F10` | 0x109 | F10 | | `YAS_K_F11` | 0x10a | F11 | | `YAS_K_F12` | 0x10b | F12 | | `YAS_K_S_F1` | 0x10c | Shift-F1 | | `YAS_K_S_F2` | 0x10d | Shift-F2 | | `YAS_K_S_F3` | 0x10e | Shift-F3 | | `YAS_K_S_F4` | 0x10f | Shift-F4 | | `YAS_K_S_F5` | 0x110 | Shift-F5 | | `YAS_K_S_F6` | 0x111 | Shift-F6 | | `YAS_K_S_F7` | 0x112 | Shift-F7 | | `YAS_K_S_F8` | 0x113 | Shift-F8 | | `YAS_K_S_F9` | 0x114 | Shift-F9 | | `YAS_K_S_F10` | 0x115 | Shift-F10 | | `YAS_K_S_F11` | 0x116 | Shift-F11 | | `YAS_K_S_F12` | 0x117 | Shift-F12 | | `YAS_K_C_F1` | 0x118 | Ctrl-F1 | | `YAS_K_C_F2` | 0x119 | Ctrl-F2 | | `YAS_K_C_F3` | 0x11a | Ctrl-F3 | | `YAS_K_C_F4` | 0x11b | Ctrl-F4 | | `YAS_K_C_F5` | 0x11c | Ctrl-F5 | | `YAS_K_C_F6` | 0x11d | Ctrl-F6 | | `YAS_K_C_F7` | 0x11e | Ctrl-F7 | | `YAS_K_C_F8` | 0x11f | Ctrl-F8 | | `YAS_K_C_F9` | 0x120 | Ctrl-F9 | | `YAS_K_C_F10` | 0x121 | Ctrl-F10 | | `YAS_K_C_F11` | 0x122 | Ctrl-F11 | | `YAS_K_C_F12` | 0x123 | Ctrl-F12 | | `YAS_K_A_F1` | 0x124 | Alt-F1 | | `YAS_K_A_F2` | 0x125 | Alt-F2 | | `YAS_K_A_F3` | 0x126 | Alt-F3 | | `YAS_K_A_F4` | 0x127 | Alt-F4 | | `YAS_K_A_F5` | 0x128 | Alt-F5 | | `YAS_K_A_F6` | 0x129 | Alt-F6 | | `YAS_K_A_F7` | 0x12a | Alt-F7 | | `YAS_K_A_F8` | 0x12b | Alt-F8 | | `YAS_K_A_F9` | 0x12c | Alt-F9 | | `YAS_K_A_F10` | 0x12d | Alt-F10 | | `YAS_K_A_F11` | 0x12e | Alt-F11 | | `YAS_K_A_F12` | 0x12f | Alt-F12 | | `YAS_K_LEFT` | 0x130 | Left | | `YAS_K_UP` | 0x131 | Up | | `YAS_K_DOWN` | 0x132 | Down | | `YAS_K_RIGHT` | 0x133 | Right | | `YAS_K_HOME` | 0x134 | Home | | `YAS_K_END` | 0x135 | End | | `YAS_K_PGUP` | 0x136 | PageUp | | `YAS_K_PGDN` | 0x137 | PageDown | | `YAS_K_INS` | 0x138 | Insert | | `YAS_K_DEL` | 0x139 | Delete | | `YAS_K_C_LEFT` | 0x13a | Ctrl-Left | | `YAS_K_C_UP` | 0x13b | Ctrl-Up | | `YAS_K_C_DOWN` | 0x13c | Ctrl-Down | | `YAS_K_C_RIGHT` | 0x13d | Ctrl-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-\* | | `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\_telnet ```c inline void yascreen_set_telnet(yascreen *s,int on); ``` enable (on is non-zero) or disable (on=0) telnet sequence processing ### 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`\|`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); ``` vfu-4.22/THANKS.TO0000644000175000017500000000351114145574023012054 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 since Nov.2002 William Vera for Debian support & packaging Larry De Coste for keeping in touch after so many years :) Ubuntu Community Documentation for VFU wiki docs: https://help.ubuntu.com/community/Vfu/ (current release copied as vfu.wiki here) All VFU users! P! Vladi Belperchinov-Shabanski http://cade.datamax.bg ---eof---------------------------------------------------------------------- vfu-4.22/COMPILE.NOTES0000644000175000017500000000244514145574023012527 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 http://soul.datamax.bg/~cade/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-4.22/aclocal.m40000644000175000017500000007734514145574023012500 0ustar cadecade# generated automatically by aclocal 1.15 -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. # Default is to disable them, unless 'enable' is passed literally. # For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), [enable], [m4_define([am_maintainer_other], [disable])], [disable], [m4_define([am_maintainer_other], [enable])], [m4_define([am_maintainer_other], [enable]) m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], am_maintainer_other[ make rules and dependencies not useful (and sometimes confusing) to the casual installer])], [USE_MAINTAINER_MODE=$enableval], [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST([MAINT])dnl ] ) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([build-aux/m4/ax_check_enable_debug.m4]) m4_include([build-aux/m4/ax_path_lib_pcre.m4]) m4_include([build-aux/m4/ax_require_defined.m4]) m4_include([build-aux/m4/ax_with_curses.m4]) m4_include([build-aux/m4/pkg.m4]) vfu-4.22/mm.conf0000644000175000017500000000012314145574023012074 0ustar cadecadeCC = ! LD = ! AR = ? ar RANLIB = ? ranlib LD = $(CXX) MODULES = vstring vslib vfu vfu-4.22/config.in0000644000175000017500000000372414145574023012423 0ustar cadecade/* config.in. Generated from configure.ac by autoheader. */ /* Define to 1 if a SysV or X/Open compatible Curses library is present */ #undef HAVE_CURSES /* Define to 1 if library supports color (enhanced functions) */ #undef HAVE_CURSES_COLOR /* Define to 1 if library supports X/Open Enhanced functions */ #undef HAVE_CURSES_ENHANCED /* Define to 1 if is present */ #undef HAVE_CURSES_H /* Define to 1 if library supports certain obsolete features */ #undef HAVE_CURSES_OBSOLETE /* Define to 1 if you have the `pcre' library (-lpcre). */ #undef HAVE_LIBPCRE /* Define to 1 if the Ncurses library is present */ #undef HAVE_NCURSES /* Define to 1 if the NcursesW library is present */ #undef HAVE_NCURSESW /* Define to 1 if is present */ #undef HAVE_NCURSESW_CURSES_H /* Define to 1 if is present */ #undef HAVE_NCURSESW_H /* Define to 1 if is present */ #undef HAVE_NCURSES_CURSES_H /* Define to 1 if is present */ #undef HAVE_NCURSES_H /* Define if debugging is disabled */ #undef NDEBUG /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if yascreen was requested */ #undef USE_YASCREEN /* Version number of package */ #undef VERSION /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES vfu-4.22/.tar_exclude0000644000175000017500000000004014151772750013116 0ustar cadecadeyascreen/debian yascreen/fedora vfu-4.22/build-aux/0000755000175000017500000000000014145574023012512 5ustar cadecadevfu-4.22/build-aux/compile0000755000175000017500000001624514145574023014100 0ustar cadecade#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: vfu-4.22/build-aux/test-driver0000755000175000017500000001104014145574023014704 0ustar cadecade#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2013-07-13.22; # UTC # Copyright (C) 2011-2014 Free Software Foundation, Inc. # # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>$log_file # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: vfu-4.22/build-aux/m4/0000755000175000017500000000000014145574023013032 5ustar cadecadevfu-4.22/build-aux/m4/ax_check_enable_debug.m40000644000175000017500000001073014145574023017516 0ustar cadecade# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) # # DESCRIPTION # # Check for the presence of an --enable-debug option to configure, with # the specified default value used when the option is not present. Return # the value in the variable $ax_enable_debug. # # Specifying 'yes' adds '-g -O0' to the compilation flags for all # languages. Specifying 'info' adds '-g' to the compilation flags. # Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to # the linking flags. Otherwise, nothing is added. # # Define the variables listed in the second argument if debug is enabled, # defaulting to no variables. Defines the variables listed in the third # argument if debug is disabled, defaulting to NDEBUG. All lists of # variables should be space-separated. # # If debug is not enabled, ensure AC_PROG_* will not add debugging flags. # Should be invoked prior to any AC_PROG_* compiler checks. # # IS-RELEASE can be used to change the default to 'no' when making a # release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it # uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE # macro, there is no need to pass this parameter. # # AX_IS_RELEASE([git-directory]) # AX_CHECK_ENABLE_DEBUG() # # LICENSE # # Copyright (c) 2011 Rhys Ulerich # Copyright (c) 2014, 2015 Philip Withnall # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. #serial 5 AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ AC_BEFORE([$0],[AC_PROG_CC])dnl AC_BEFORE([$0],[AC_PROG_CXX])dnl AC_BEFORE([$0],[AC_PROG_F77])dnl AC_BEFORE([$0],[AC_PROG_FC])dnl AC_MSG_CHECKING(whether to enable debugging) ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, [$ax_is_release], [$4]))) # If this is a release, override the default. AS_IF([test "$ax_enable_debug_is_release" = "yes"], [ax_enable_debug_default="no"]) m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], [],enable_debug=$ax_enable_debug_default) # empty mean debug yes AS_IF([test "x$enable_debug" = "x"], [enable_debug="yes"]) # case of debug AS_CASE([$enable_debug], [yes],[ AC_MSG_RESULT(yes) CFLAGS="${CFLAGS} -g -O0" CXXFLAGS="${CXXFLAGS} -g -O0" FFLAGS="${FFLAGS} -g -O0" FCFLAGS="${FCFLAGS} -g -O0" OBJCFLAGS="${OBJCFLAGS} -g -O0" ], [info],[ AC_MSG_RESULT(info) CFLAGS="${CFLAGS} -g" CXXFLAGS="${CXXFLAGS} -g" FFLAGS="${FFLAGS} -g" FCFLAGS="${FCFLAGS} -g" OBJCFLAGS="${OBJCFLAGS} -g" ], [profile],[ AC_MSG_RESULT(profile) CFLAGS="${CFLAGS} -g -pg" CXXFLAGS="${CXXFLAGS} -g -pg" FFLAGS="${FFLAGS} -g -pg" FCFLAGS="${FCFLAGS} -g -pg" OBJCFLAGS="${OBJCFLAGS} -g -pg" LDFLAGS="${LDFLAGS} -pg" ], [ AC_MSG_RESULT(no) dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags dnl by setting any unset environment flag variables AS_IF([test "x${CFLAGS+set}" != "xset"], [CFLAGS=""]) AS_IF([test "x${CXXFLAGS+set}" != "xset"], [CXXFLAGS=""]) AS_IF([test "x${FFLAGS+set}" != "xset"], [FFLAGS=""]) AS_IF([test "x${FCFLAGS+set}" != "xset"], [FCFLAGS=""]) AS_IF([test "x${OBJCFLAGS+set}" != "xset"], [OBJCFLAGS=""]) ]) dnl Define various variables if debugging is disabled. dnl assert.h is a NOP if NDEBUG is defined, so define it by default. AS_IF([test "x$enable_debug" = "xyes"], [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) ax_enable_debug=$enable_debug ]) vfu-4.22/build-aux/m4/ax_path_lib_pcre.m40000644000175000017500000000662114145574023016564 0ustar cadecade# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_path_lib_pcre.html # =========================================================================== # # SYNOPSIS # # AX_PATH_LIB_PCRE [(A/NA)] # # DESCRIPTION # # check for pcre lib and set PCRE_LIBS and PCRE_CFLAGS accordingly. # # also provide --with-pcre option that may point to the $prefix of the # pcre installation - the macro will check $pcre/include and $pcre/lib to # contain the necessary files. # # the usual two ACTION-IF-FOUND / ACTION-IF-NOT-FOUND are supported and # they can take advantage of the LIBS/CFLAGS additions. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # # 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 3 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, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 7 AC_DEFUN([AX_PATH_LIB_PCRE],[dnl AC_MSG_CHECKING([lib pcre]) AC_ARG_WITH(pcre, [ --with-pcre[[=prefix]] compile xmlpcre part (via libpcre check)],, with_pcre="yes") if test ".$with_pcre" = ".no" ; then AC_MSG_RESULT([disabled]) m4_ifval($2,$2) else AC_MSG_RESULT([(testing)]) AC_CHECK_LIB(pcre, pcre_study) if test "$ac_cv_lib_pcre_pcre_study" = "yes" ; then PCRE_LIBS="-lpcre" AC_MSG_CHECKING([lib pcre]) AC_MSG_RESULT([$PCRE_LIBS]) m4_ifval($1,$1) else OLDLDFLAGS="$LDFLAGS" ; LDFLAGS="$LDFLAGS -L$with_pcre/lib" OLDCPPFLAGS="$CPPFLAGS" ; CPPFLAGS="$CPPFLAGS -I$with_pcre/include" AC_CHECK_LIB(pcre, pcre_compile) CPPFLAGS="$OLDCPPFLAGS" LDFLAGS="$OLDLDFLAGS" if test "$ac_cv_lib_pcre_pcre_compile" = "yes" ; then AC_MSG_RESULT(.setting PCRE_LIBS -L$with_pcre/lib -lpcre) PCRE_LIBS="-L$with_pcre/lib -lpcre" test -d "$with_pcre/include" && PCRE_CFLAGS="-I$with_pcre/include" AC_MSG_CHECKING([lib pcre]) AC_MSG_RESULT([$PCRE_LIBS]) m4_ifval($1,$1) else AC_MSG_CHECKING([lib pcre]) AC_MSG_RESULT([no, (WARNING)]) m4_ifval($2,$2) fi fi fi AC_SUBST([PCRE_LIBS]) AC_SUBST([PCRE_CFLAGS]) ]) vfu-4.22/build-aux/m4/pkg.m40000644000175000017500000002400514145574023014056 0ustar cadecadednl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- dnl serial 11 (pkg-config-0.29) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR vfu-4.22/build-aux/m4/ax_with_curses.m40000644000175000017500000006642314145574023016336 0ustar cadecade# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_with_curses.html # =========================================================================== # # SYNOPSIS # # AX_WITH_CURSES # # DESCRIPTION # # This macro checks whether a SysV or X/Open-compatible Curses library is # present, along with the associated header file. The NcursesW # (wide-character) library is searched for first, followed by Ncurses, # then the system-default plain Curses. The first library found is the # one returned. Finding libraries will first be attempted by using # pkg-config, and should the pkg-config files not be available, will # fallback to combinations of known flags itself. # # The following options are understood: --with-ncursesw, --with-ncurses, # --without-ncursesw, --without-ncurses. The "--with" options force the # macro to use that particular library, terminating with an error if not # found. The "--without" options simply skip the check for that library. # The effect on the search pattern is: # # (no options) - NcursesW, Ncurses, Curses # --with-ncurses --with-ncursesw - NcursesW only [*] # --without-ncurses --with-ncursesw - NcursesW only [*] # --with-ncursesw - NcursesW only [*] # --with-ncurses --without-ncursesw - Ncurses only [*] # --with-ncurses - NcursesW, Ncurses [**] # --without-ncurses --without-ncursesw - Curses only # --without-ncursesw - Ncurses, Curses # --without-ncurses - NcursesW, Curses # # [*] If the library is not found, abort the configure script. # # [**] If the second library (Ncurses) is not found, abort configure. # # The following preprocessor symbols may be defined by this macro if the # appropriate conditions are met: # # HAVE_CURSES - if any SysV or X/Open Curses library found # HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions # HAVE_CURSES_COLOR - if library supports color (enhanced functions) # HAVE_CURSES_OBSOLETE - if library supports certain obsolete features # HAVE_NCURSESW - if NcursesW (wide char) library is to be used # HAVE_NCURSES - if the Ncurses library is to be used # # HAVE_CURSES_H - if is present and should be used # HAVE_NCURSESW_H - if should be used # HAVE_NCURSES_H - if should be used # HAVE_NCURSESW_CURSES_H - if should be used # HAVE_NCURSES_CURSES_H - if should be used # # (These preprocessor symbols are discussed later in this document.) # # The following output variables are defined by this macro; they are # precious and may be overridden on the ./configure command line: # # CURSES_LIBS - library to add to xxx_LDADD # CURSES_CFLAGS - include paths to add to xxx_CPPFLAGS # # In previous versions of this macro, the flags CURSES_LIB and # CURSES_CPPFLAGS were defined. These have been renamed, in keeping with # AX_WITH_CURSES's close bigger brother, PKG_CHECK_MODULES, which should # eventually supersede the use of AX_WITH_CURSES. Neither the library # listed in CURSES_LIBS, nor the flags in CURSES_CFLAGS are added to LIBS, # respectively CPPFLAGS, by default. You need to add both to the # appropriate xxx_LDADD/xxx_CPPFLAGS line in your Makefile.am. For # example: # # prog_LDADD = @CURSES_LIBS@ # prog_CPPFLAGS = @CURSES_CFLAGS@ # # If CURSES_LIBS is set on the configure command line (such as by running # "./configure CURSES_LIBS=-lmycurses"), then the only header searched for # is . If the user needs to specify an alternative path for a # library (such as for a non-standard NcurseW), the user should use the # LDFLAGS variable. # # The following shell variables may be defined by this macro: # # ax_cv_curses - set to "yes" if any Curses library found # ax_cv_curses_enhanced - set to "yes" if Enhanced functions present # ax_cv_curses_color - set to "yes" if color functions present # ax_cv_curses_obsolete - set to "yes" if obsolete features present # # ax_cv_ncursesw - set to "yes" if NcursesW library found # ax_cv_ncurses - set to "yes" if Ncurses library found # ax_cv_plaincurses - set to "yes" if plain Curses library found # ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no" # # These variables can be used in your configure.ac to determine the level # of support you need from the Curses library. For example, if you must # have either Ncurses or NcursesW, you could include: # # AX_WITH_CURSES # if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then # AC_MSG_ERROR([requires either NcursesW or Ncurses library]) # fi # # If any Curses library will do (but one must be present and must support # color), you could use: # # AX_WITH_CURSES # if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then # AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) # fi # # Certain preprocessor symbols and shell variables defined by this macro # can be used to determine various features of the Curses library. In # particular, HAVE_CURSES and ax_cv_curses are defined if the Curses # library found conforms to the traditional SysV and/or X/Open Base Curses # definition. Any working Curses library conforms to this level. # # HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the # library supports the X/Open Enhanced Curses definition. In particular, # the wide-character types attr_t, cchar_t and wint_t, the functions # wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES # are checked. The Ncurses library does NOT conform to this definition, # although NcursesW does. # # HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library # supports color functions and macros such as COLOR_PAIR, A_COLOR, # COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the # X/Open Base Curses definition, but are part of the Enhanced set of # functions. The Ncurses library DOES support these functions, as does # NcursesW. # # HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the # library supports certain features present in SysV and BSD Curses but not # defined in the X/Open definition. In particular, the functions # getattrs(), getcurx() and getmaxx() are checked. # # To use the HAVE_xxx_H preprocessor symbols, insert the following into # your system.h (or equivalent) header file: # # #if defined HAVE_NCURSESW_CURSES_H # # include # #elif defined HAVE_NCURSESW_H # # include # #elif defined HAVE_NCURSES_CURSES_H # # include # #elif defined HAVE_NCURSES_H # # include # #elif defined HAVE_CURSES_H # # include # #else # # error "SysV or X/Open-compatible Curses header file required" # #endif # # For previous users of this macro: you should not need to change anything # in your configure.ac or Makefile.am, as the previous (serial 10) # semantics are still valid. However, you should update your system.h (or # equivalent) header file to the fragment shown above. You are encouraged # also to make use of the extended functionality provided by this version # of AX_WITH_CURSES, as well as in the additional macros # AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM. # # LICENSE # # Copyright (c) 2009 Mark Pulford # Copyright (c) 2009 Damian Pietras # Copyright (c) 2012 Reuben Thomas # Copyright (c) 2011 John Zaitseff # # 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 3 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, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 17 # internal function to factorize common code that is used by both ncurses # and ncursesw AC_DEFUN([_FIND_CURSES_FLAGS], [ AC_MSG_CHECKING([for $1 via pkg-config]) AX_REQUIRE_DEFINED([PKG_CHECK_EXISTS]) _PKG_CONFIG([_ax_cv_$1_libs], [libs], [$1]) _PKG_CONFIG([_ax_cv_$1_cppflags], [cflags], [$1]) AS_IF([test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"],[ AC_MSG_RESULT([no]) # No suitable .pc file found, have to find flags via fallback AC_CACHE_CHECK([for $1 via fallback], [ax_cv_$1], [ AS_ECHO() pkg_cv__ax_cv_$1_libs="-l$1" pkg_cv__ax_cv_$1_cppflags="-D_GNU_SOURCE $CURSES_CFLAGS" LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags" AC_MSG_CHECKING([for initscr() with $pkg_cv__ax_cv_$1_libs]) AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], [ AC_MSG_RESULT([yes]) AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs]) AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[ ax_cv_$1=yes ],[ AC_MSG_RESULT([no]) m4_if( [$1],[ncursesw],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfow"], [$1],[ncurses],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfo"] ) LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs" AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs]) AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[ ax_cv_$1=yes ],[ ax_cv_$1=no ]) ]) ],[ ax_cv_$1=no ]) ]) ],[ AC_MSG_RESULT([yes]) # Found .pc file, using its information LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags" ax_cv_$1=yes ]) ]) AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES]) AC_DEFUN([AX_WITH_CURSES], [ AC_ARG_VAR([CURSES_LIBS], [linker library for Curses, e.g. -lcurses]) AC_ARG_VAR([CURSES_CFLAGS], [preprocessor flags for Curses, e.g. -I/usr/include/ncursesw]) AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses], [force the use of Ncurses or NcursesW])], [], [with_ncurses=check]) AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw], [do not use NcursesW (wide character support)])], [], [with_ncursesw=check]) ax_saved_LIBS=$LIBS ax_saved_CPPFLAGS=$CPPFLAGS AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes], [ax_with_plaincurses=no], [ax_with_plaincurses=check]) ax_cv_curses_which=no # Test for NcursesW AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncursesw" != xno], [ _FIND_CURSES_FLAGS([ncursesw]) AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [ AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library]) ]) AS_IF([test "x$ax_cv_ncursesw" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=ncursesw CURSES_LIBS="$pkg_cv__ax_cv_ncursesw_libs" CURSES_CFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags" AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncursesw_curses_h=yes], [ax_cv_header_ncursesw_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncursesw_h=yes], [ax_cv_header_ncursesw_h=no]) ]) AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_header_ncurses_h_with_ncursesw=yes], [ax_cv_header_ncurses_h_with_ncursesw=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) ]) AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [ AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h]) ]) ]) ]) unset pkg_cv__ax_cv_ncursesw_libs unset pkg_cv__ax_cv_ncursesw_cppflags # Test for Ncurses AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [ _FIND_CURSES_FLAGS([ncurses]) AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [ AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library]) ]) AS_IF([test "x$ax_cv_ncurses" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=ncurses CURSES_LIBS="$pkg_cv__ax_cv_ncurses_libs" CURSES_CFLAGS="$pkg_cv__ax_cv_ncurses_cppflags" AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_header_ncurses_curses_h=yes], [ax_cv_header_ncurses_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [ ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if is present]) ]) AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_header_ncurses_h=yes], [ax_cv_header_ncurses_h=no]) ]) AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [ ax_cv_curses_color=yes ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) ]) AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [ AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h]) ]) ]) ]) unset pkg_cv__ax_cv_ncurses_libs unset pkg_cv__ax_cv_ncurses_cppflags # Test for plain Curses (or if CURSES_LIBS was set by user) AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [ AS_IF([test "x$CURSES_LIBS" != x], [ LIBS="$ax_saved_LIBS $CURSES_LIBS" ], [ LIBS="$ax_saved_LIBS -lcurses" ]) AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [ AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no]) ]) AS_IF([test "x$ax_cv_plaincurses" = xyes], [ ax_cv_curses=yes ax_cv_curses_which=plaincurses AS_IF([test "x$CURSES_LIBS" = x], [ CURSES_LIBS="-lcurses" ]) AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) # Check for base conformance (and header file) AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; initscr(); ]])], [ax_cv_header_curses_h=yes], [ax_cv_header_curses_h=no]) ]) AS_IF([test "x$ax_cv_header_curses_h" = xyes], [ AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present]) # Check for X/Open Enhanced conformance AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include @%:@ifndef _XOPEN_CURSES @%:@error "this Curses library is not enhanced" "this Curses library is not enhanced" @%:@endif ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ]])], [ax_cv_plaincurses_enhanced=yes], [ax_cv_plaincurses_enhanced=no]) ]) AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [ ax_cv_curses_enhanced=yes ax_cv_curses_color=yes AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) ]) # Check for color functions AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@define _XOPEN_SOURCE_EXTENDED 1 @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ]])], [ax_cv_plaincurses_color=yes], [ax_cv_plaincurses_color=no]) ]) AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [ ax_cv_curses_color=yes AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) ]) # Check for obsolete functions AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ chtype a = A_BOLD; int b = KEY_LEFT; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); ]])], [ax_cv_plaincurses_obsolete=yes], [ax_cv_plaincurses_obsolete=no]) ]) AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [ ax_cv_curses_obsolete=yes AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) ]) ]) AS_IF([test "x$ax_cv_header_curses_h" = xno], [ AC_MSG_WARN([could not find a working curses.h]) ]) ]) ]) AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no]) AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no]) AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no]) AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no]) LIBS=$ax_saved_LIBS CPPFLAGS=$ax_saved_CPPFLAGS unset ax_saved_LIBS unset ax_saved_CPPFLAGS ])dnl vfu-4.22/build-aux/m4/ax_require_defined.m40000644000175000017500000000230114145574023017112 0ustar cadecade# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_require_defined.html # =========================================================================== # # SYNOPSIS # # AX_REQUIRE_DEFINED(MACRO) # # DESCRIPTION # # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have # been defined and thus are available for use. This avoids random issues # where a macro isn't expanded. Instead the configure script emits a # non-fatal: # # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found # # It's like AC_REQUIRE except it doesn't expand the required macro. # # Here's an example: # # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) # # LICENSE # # Copyright (c) 2014 Mike Frysinger # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 1 AC_DEFUN([AX_REQUIRE_DEFINED], [dnl m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) ])dnl AX_REQUIRE_DEFINED vfu-4.22/build-aux/missing0000755000175000017500000001533014145574023014113 0ustar cadecade#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: vfu-4.22/build-aux/install-sh0000755000175000017500000003546314145574023014531 0ustar cadecade#!/bin/sh # install - install a program, script, or datafile scriptversion=2014-09-12.12; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # $RANDOM is not portable (e.g. dash); use it when possible to # lower collision chance tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # As "mkdir -p" follows symlinks and we work in /tmp possibly; so # create the $tmpdir first (and fail if unsuccessful) to make sure # that nobody tries to guess the $tmpdir name. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: vfu-4.22/INSTALL0000644000175000017500000000653714145574023011664 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 http://cade.datamax.bg/vfu vfu-4.22/build.docs0000755000175000017500000000032314145574023012572 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-4.22/extra/0000755000175000017500000000000014145574023011743 5ustar cadecadevfu-4.22/extra/vfu-bash0000644000175000017500000000076414145574023013410 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-4.22/extra/Xmodmap0000644000175000017500000000057414145574023013301 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-4.22/extra/terminfo/0000755000175000017500000000000014145574023013566 5ustar cadecadevfu-4.22/extra/terminfo/linux-c20000644000175000017500000000375714145574023015166 0ustar cadecadeS}Ylinux-c2|linux console 1.3.6+ (rev2) with private palette for each virtual consoleÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ÿÿ!%)ÿÿ4EGKRÿÿT[ÿÿ_cjnÿÿÿÿrx}ÿÿÿÿ‚‡Œÿÿ‘–› ©¯ÿÿÿÿ·¼ÂÈÿÿÿÿÿÿÿÿÿÿÿÿÚÞÿÿâÿÿÿÿÿÿäÿÿéÿÿÿÿÿÿÿÿíòøý $)ÿÿ.ÿÿ27<ÿÿÿÿÿÿ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDÿÿGPÿÿYÿÿbÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkÿÿÿÿÿÿqt‚„‡èÿÿëÿÿÿÿÿÿÿÿÿÿÿÿíÿÿÿÿÿÿÿÿñÿÿ2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=CIOU[agmsÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~‰Ž”˜¡¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ9CÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMS [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?25h[?25h[%p1%dX[?5h[?5l$<200/>[@[3~[[A[21~[[B[[C[[D[[E[17~[18~[19~[20~[1~[2~[6~[5~ [%p1%dP[%p1%dM[%p1%d@[%p1%dLc]R8[%i%p1%dd7 M[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;mH +,-.0Û`a±føgñh°iÎjÙk¿lÚmÀnÅo~pÄqÄrÄs_tÃu´vÁwÂx³yózò{ã|Ø}œ~þ[4~[23~[24~[25~[26~[28~[29~[31~[32~[33~[34~[%i%d;%dR[?6c]R]P%?%p1%{9}%>%t%p1%{10}%-%'a'%+%c%e%p1%d%;%p2%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%p3%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%p4%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;[3%p1%dm[4%p1%dmvfu-4.22/extra/terminfo/linux-c2.terminfo.src0000644000175000017500000000432714145574023017570 0ustar cadecade# Reconstructed via infocmp from file: /usr/share/terminfo/l/linux-c # Changed by to avoid all `ESC[0c' and similar cursor state # sequences to be used with rxvt and most xterm-compatible terminal-emulators linux-c2|linux console 1.3.6+ (rev2) with private palette for each virtual console, am, bce, ccc, eo, mir, msgr, xenl, xon, colors#8, it#8, ncv#2, pairs#64, acsc=+\020\,\021-\030.^Y0\333`\004a\261f\370g\361h\260i\316j\331k\277l\332m\300n\305o~p\304q\304r\304s_t\303u\264v\301w\302x\263y\363z\362{\343|\330}\234~\376, bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, clear=\E[H\E[J, cnorm=\E[?25h, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub1=^H, cud1=^J, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu1=\E[A, cvvis=\E[?25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h\E[?5l$<200/>, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=^J, initc=\E]P%?%p1%{9}%>%t%p1%{10}%-%'a'%+%c%e%p1%d%;%p2%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%p3%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%p4%{255}%&%Pr%gr%{16}%/%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;%gr%{15}%&%Px%?%gx%{9}%>%t%gx%{10}%-%'A'%+%c%e%gx%d%;, invis=\E[8m, kb2=\E[G, kbs=\177, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kend=\E[4~, kf1=\E[[A, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~, kf19=\E[33~, kf2=\E[[B, kf20=\E[34~, kf3=\E[[C, kf4=\E[[D, kf5=\E[[E, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khome=\E[1~, kich1=\E[2~, knp=\E[6~, kpp=\E[5~, kspd=^Z, nel=^M^J, oc=\E]R, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=\E[10m, rmir=\E[4l, rmpch=\E[10m, rmso=\E[27m, rmul=\E[24m, rs1=\Ec\E]R, sc=\E7, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, sgr0=\E[0;10m, smacs=\E[11m, smir=\E[4h, smpch=\E[11m, smso=\E[7m, smul=\E[4m, tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?6c, u9=\E[c, vpa=\E[%i%p1%dd, vfu-4.22/extra/terminfo/README0000644000175000017500000000136414145574023014452 0ustar cadecade NOTE: THIS IS OPTIONAL, YOU SHOULD USE THIS ONLY IF YOU HAVE PROBLEMS WITH STANDARD TERMINFO ENTRIES (linux,linux*,rxvt...) If you want to use this terminfo/termcap definition: 1. copy `linux-c2' file to /usr/share/terminof/l/ 2. export TERM=linux-c2 (for bash) 3. run vfu or 1. compile linux-c2.terminfo.src: tic linux-c2.terminfo.src -o . 2. you should got linux-c2 file in ./l/ directory 3. proceed as above This works fine with linux console and rxvt (xterm works also). If you have found bugs in linux-c2 or you have any notes about this file, feel free to contact me I'll be glad to help you or to fix/update linux-c2. vfu home page is: http://cade.datamax.bg/vfu vfu-4.22/extra/Rxvt0000644000175000017500000000025114145574023012627 0ustar cadecade*font: vga *boldFont: vga *saveLines: 500 *foreground: #cccccc *background: #444444 *cursorColor: green *scrollBar: False *title: Term *keysym.16: "\b" *keysym.6f: "^?" vfu-4.22/extra/vga.pcf0000644000175000017500000006007014145574023013215 0ustar cadecadefcp ¨˜d@¤D¬ÄJ ÌO@ÜQ€ äUdð_   g&K3K@ÿÿÿÿITPOINT_SIZEFONTvgaWEIGHTRESOLUTIONRESOLUTION_XRESOLUTION_YX_HEIGHTQUAD_WIDTH   ÿÿ÷ÿ €ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„€ˆˆŒ„@€À@€À@€À@€À@€À@€À@€À@€À@€À @ € À @ € À @ € À @ € À @ € À @€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À@€À @ € À !@!€!À!"@"€"À"#@#€#À#$@$€$À$%@%€%À%&@&€&À&'@'€'À'(@(€(À()@)€)À)*@*€*À*+@+€+À+,@,€,À,-@-€-À-.@.€.À./@/€/À/0@0€0À01@1€1À12@2€2À23@3€3À34@4€4À45@5€5À56@6€6À67@7€7À78@8€8À89@9€9À9:@:€:À:;@;€;À;<@<€<À<=@=€=À=>@>€>À>?@?€?À? @€>cc{{{;>~¥¥™~~ÿÛÿÿÛçÿÿ~6>>><<ççç<<~ÿÿ~<<<ÿÿÿÿÿÿçÃÃçÿÿÿÿÿÿ<fBBf<ÿÿÿÿÿÙ½½™Ãÿÿÿÿÿx`pX3333<ffff<~üÌü þÆþÆÆÆÆæçgÛ<ç<Û@`px||xp`@<~~<fffffffffþÛÛÛÞØØØØØ>c6cc60c><~~<~<~~<00  66>>>><<<fff$6666666>cC>``ac>Cc0 ca66n;333n 0 0 000000 f<ÿ<f~ @`0 6cckkcc6~>c`0 c>c``<```c>08<63000x?```c>?cccc>c``0 >ccc>cccc>>ccc~```0 `0  0`~~ 0`0 >cc0>cc{{{;>6cccccc?fff>ffff?<fCCf<6ffffff6fFFffF<fC{ccf\ccccccccc<<x00000333gff66ffgFfcwkccccccgo{scccc>cccccccc>?fff>>cccccck{>0p?fff>6fffg>cc0`cc>~~Z<ccccccccc>ccccccc6cccckkkw6cc6>>6ccffff<<ca0 Cc< <8p`@<00000000<6cÿ 0>333n6ffff>>cc>800<63333n>cc>6&n33333>036nffffg<``p``````ff<f66fg<7kkkkc;ffffff>ccccc>;fffff>n33333>00x;nf>c0c> ? l8333333nfffff<cckkk6c66ccccccc~`03 cpppn;6ccc<fCCf<0`>3333333n0 >cc>60>333n30>333n 0>333n60>333n<ff<0`<6>cc>c>cc> >cc>f<<f< <c6ccccc66ccccc f>f3nl~v|6333333s6>ccccc>c>ccccc> >ccccc> 3333333n 333333nccccccc~`0c>ccccccc>ccccccccc><ff<6&g?ff<~~33#3{333cpØ~ 0>333n0 < >ccccc> 333333nn;;ffffffn;cgo{sccc<66|~66> cc>````Cc3 ;a0|Cc3 fsy|``<<<l66l6l6ˆ"ˆ"ˆ"ˆ"ˆ"ˆ"ˆ"ˆ"ªUªUªUªUªUªUªUªU»î»î»î»î»î»î»î»îlllllllolllllllllllllllllllllo`ollllllllllllllllllllllll`olllllllllllllo`llllllløÿÿøÿÿøølllllllìlllllllllllllì üü ìlllllllllllllïÿÿïlllllllllllllì ìllllllllÿÿlllllïïllllllllÿÿlllllllÿÿÿÿlllllllllllllllüøøøøülllllllllllllllÿllllllllÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿððððððððððððððððÿÿÿÿÿÿÿn;;n3333ccc3cc6666666c  c~fffff>n;~<fff<~6cccc66ccc6666wx 0|ffff<~ÛÛÛ~À`~ÛÛÏ~8 > 8>cccccccc~ÿ 0`0 ~0  0~pØØ~n;n;66ð00000766<866666 >>>>>>>€‡ˆ‰€€ˆˆŠ€€ˆˆŠ€€‡ˆˆ€€‡ˆˆ€ˆˆ‰€€ˆˆ‰€‚†ˆ†~€ˆˆŒ„‡ˆ‡€ˆˆŒ„€‡ˆŠ€‡ˆŠ€€ˆˆŠ€€ˆˆŠ€ˆˆ‰€€‡ˆ‹€€‡ˆ‹€‡ˆŠ‡ˆŠ€€ˆˆŠ€€‡ˆ‹€‡ˆ„€‡ˆŠ€‡ˆŠ€‡ˆŠ€€‡ˆ‡~€‡ˆ‡~€‡ˆ†~€‡ˆ‡~€‡ˆˆ€‡ˆˆ€€ˆ€€‚†ˆŠ€‡ˆ‹y€‡ˆ‰€€‡ˆŒ‚€‡ˆˆ€€‡ˆŠ€„ˆ‹y‚†ˆŠ€‚†ˆŠ€€ˆˆ‡~‡ˆ‡~‚…ˆƒ€‡ˆ…|ƒ…ˆ‚€€‡ˆˆ€€‡ˆŠ€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€ƒ…ˆˆ‚…ˆˆ€‡ˆ‰€‡ˆ‡}‡ˆ‰€€‡ˆŠ€€‡ˆ‰€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€‚†ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ‚€‡ˆŠ€€‡ˆŠ€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€€‡ˆŠ€‡ˆŠ€€‡ˆŠ€‚†ˆŠ€€‡ˆ‰€‚†ˆŠ€€‡ˆŒx€ˆˆ‚‚…ˆŒw€‡ˆ‡€€‡ˆŠ€€‡ˆ‡€€‡ˆŠ€€‡ˆ‡€€†ˆŠ€€‡ˆ‡ƒ€‡ˆŠ€‚†ˆŠ€‡ˆŠƒ€‡ˆŠ€‚†ˆŠ€€‡ˆ‡€€‡ˆ‡€€‡ˆ‡€€‡ˆ‡ƒ€‡ˆ‡ƒ€‡ˆ‡€€‡ˆ‡€€‡ˆŠ€€‡ˆ‡€‡ˆ‡€€‡ˆ‡€€‡ˆ‡€€‡ˆ‡ƒ€‡ˆ‡€‡ˆŠ€ƒ…ˆŠ€‡ˆŠ€€‡ˆŠx€‡ˆˆ€‡ˆŠ‚€‡ˆŠ€€‡ˆ‹€€‡ˆ‹€€‡ˆŠ€€‡ˆ‹€€‡ˆ‹€‡ˆˆ€‡ˆ‹€€‡ˆŠ€€‡ˆ‹€‡ˆŠ€‡ˆ‹€†ˆ‹€€‡ˆ‹€€‡ˆŒ€€‡ˆŒ€€‡ˆ‡€€‡ˆŠ€€‡ˆ‹€€‡ˆŠ€€‡ˆ‹€€‡ˆ‹€€‡ˆ‹€€‡ˆŠƒ€‡ˆ‹€€‡ˆ‹€‡ˆ‹€€‡ˆ‹€‡ˆŠ€€‡ˆ‹€€ˆˆ‹‚€‡ˆ‹€‚†ˆ‹€€‡ˆ‹€€‡ˆ‹€€‡ˆŠ€€‡ˆŒ€‡ˆ‹{†ˆ‹{€‡ˆŠ€€‡ˆ†€‡ˆ†€‡ˆ‹‚€‡ˆ‹‚‚†ˆŠ€€‡ˆ‡~€‡ˆ‡~ˆˆŒ„€ˆˆŒ„€ˆˆŒ„ƒ…ˆŒ„€…ˆŒ„€…ˆŒ„€‡ˆŒ„€‡ˆ…„€…ˆ‡„€‡ˆŒ„‚‡ˆŒ„€‡ˆ‡„€‡ˆŒ|€‡ˆŒ|€…ˆŒ|€…ˆ…„ƒˆˆŒ|€ˆˆŒ|€ˆˆ…„ƒˆˆŒ„€ˆˆ…|€ˆˆŒ„ƒˆˆŒ„‚ˆˆŒ„‚ˆˆŒ|‚ˆˆ‡„€ˆˆŒ|€ˆˆ‡„‚ˆˆŒ„€ˆˆ‡|€ˆˆŒ„€ˆˆŒ|€ˆˆŒ|€ˆˆ‡„€ˆˆ…„‚ˆˆŒ|ƒˆˆŒ|ƒˆˆ‡„‚ˆˆ…„€ˆˆŒ„€ˆˆŒ„€…ˆŒ|ƒˆˆ…„€ˆˆŒ„€ˆˆ…„€„ˆŒ„„ˆˆŒ„€ˆˆŒ{€‡ˆ‡€€‡ˆŠ€€‡ˆŠ€€‡ˆˆ€€‡ˆ‰€€‡ˆ‡€€‡ˆˆ€‡ˆˆ€‡ˆ‰€€‡ˆ‰€€‡ˆŠ€‡ˆŠ€€ˆˆ‡~€ˆˆ‰€†ˆŠ€€‡ˆ‰€€‡ˆˆ€ˆˆˆ€‡ˆ‰€‡ˆ‰€ƒˆˆŠ„€…ˆŒ€‡ˆˆ€‡ˆ‡~†ˆ‹yƒ…ˆ…}ƒ…ˆ„}€ˆˆ‹€€†ˆ‹{€…ˆ‹{†ˆˆ€€ˆ€€ÿ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿšššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššššš $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôú $*06<BHNTZ`flrx~„Š–œ¢¨®´ºÀÆÌÒØÞäêðöü &,28>DJPV\bhntz€†Œ’˜ž¤ª°¶¼ÂÈÎÔÚàæìòøþ "(.4:@FLRX^djpv|‚ˆŽ”𠦬²¸¾ÄÊÐÖÜâèîôúC0000C0001C0002C0003C0004C0005C0006C0007C0008C0009C000aC000bC000cC000dC000eC000fC0010C0011C0012C0013C0014C0015C0016C0017C0018C0019C001aC001bC001cC001dC001eC001fC0020C0021C0022C0023C0024C0025C0026C0027C0028C0029C002aC002bC002cC002dC002eC002fC0030C0031C0032C0033C0034C0035C0036C0037C0038C0039C003aC003bC003cC003dC003eC003fC0040C0041C0042C0043C0044C0045C0046C0047C0048C0049C004aC004bC004cC004dC004eC004fC0050C0051C0052C0053C0054C0055C0056C0057C0058C0059C005aC005bC005cC005dC005eC005fC0060C0061C0062C0063C0064C0065C0066C0067C0068C0069C006aC006bC006cC006dC006eC006fC0070C0071C0072C0073C0074C0075C0076C0077C0078C0079C007aC007bC007cC007dC007eC007fC0080C0081C0082C0083C0084C0085C0086C0087C0088C0089C008aC008bC008cC008dC008eC008fC0090C0091C0092C0093C0094C0095C0096C0097C0098C0099C009aC009bC009cC009dC009eC009fC00a0C00a1C00a2C00a3C00a4C00a5C00a6C00a7C00a8C00a9C00aaC00abC00acC00adC00aeC00afC00b0C00b1C00b2C00b3C00b4C00b5C00b6C00b7C00b8C00b9C00baC00bbC00bcC00bdC00beC00bfC00c0C00c1C00c2C00c3C00c4C00c5C00c6C00c7C00c8C00c9C00caC00cbC00ccC00cdC00ceC00cfC00d0C00d1C00d2C00d3C00d4C00d5C00d6C00d7C00d8C00d9C00daC00dbC00dcC00ddC00deC00dfC00e0C00e1C00e2C00e3C00e4C00e5C00e6C00e7C00e8C00e9C00eaC00ebC00ecC00edC00eeC00efC00f0C00f1C00f2C00f3C00f4C00f5C00f6C00f7C00f8C00f9C00faC00fbC00fcC00fdC00feC00ff   ÿÿ÷ÿ vfu-4.22/extra/Xdefaults0000644000175000017500000000037414145574023013631 0ustar cadecadeRxvt*font: vga Rxvt*boldFont: vga Rxvt*saveLines: 500 Rxvt*foreground: #cccccc Rxvt*background: #444444 Rxvt*cursorColor: green Rxvt*scrollBar: False Rxvt*title: Term Rxvt*keysym.16: "\b" Rxvt*keysym.6f: "^?" Rxvt*backspacekey: ^H Rxvt*modifier: mod1 vfu-4.22/extra/README0000644000175000017500000000137614145574023012632 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... vga.pcf is my favorite font for rxvt (xterm) Rxvt is my configuration for rxvt (/var/X11R6/lib/app-defaults/Rxvt) OBSOLETE: see Xdefaults 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 contains some fixes to allow easier VFU usage under X (mostly rxvt issues) Vladi. vfu-4.22/AUTHORS0000644000175000017500000000021414145574023011665 0ustar cadecade Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg vfu-4.22/configure0000755000175000017500000057766614145574023012562 0ustar cadecade#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for vfu 4.18. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/cade-vs/vfu/issues about your $0: system, including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='vfu' PACKAGE_TARNAME='vfu' PACKAGE_VERSION='4.18' PACKAGE_STRING='vfu 4.18' PACKAGE_BUGREPORT='https://github.com/cade-vs/vfu/issues' PACKAGE_URL='http://cade.datamax.bg/vfu/' ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS WANT_YASCREEN_FALSE WANT_YASCREEN_TRUE PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG CURSES_CFLAGS CURSES_LIBS PCRE_CFLAGS PCRE_LIBS MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE RANLIB ac_ct_CXX CXXFLAGS CXX OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_debug enable_largefile enable_maintainer_mode with_pcre with_ncurses with_ncursesw enable_yascreen ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CXX CXXFLAGS CCC CURSES_LIBS CURSES_CFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures vfu 4.18 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/vfu] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of vfu 4.18:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-debug=[yes/info/profile/no] compile with debugging --disable-largefile omit support for large files --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-yascreen use yascreen instead of curses/ncurses Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pcre[=prefix] compile xmlpcre part (via libpcre check) --with-ncurses force the use of Ncurses or NcursesW --without-ncursesw do not use NcursesW (wide character support) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags CURSES_LIBS linker library for Curses, e.g. -lcurses CURSES_CFLAGS preprocessor flags for Curses, e.g. -I/usr/include/ncursesw PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . vfu home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF vfu configure 4.18 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by vfu $as_me 4.18, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in build-aux "$srcdir"/build-aux; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='vfu' VERSION='4.18' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi ac_config_headers="$ac_config_headers config.h:config.in" if test -f configure.ac; then as_fn_error 1 "configure should be run in a separate build directory" "$LINENO" 5 fi # Don't add -g and -O2 by default : ${CFLAGS=""} # RELEASE TODO: set this to "no" # This will add debug and profiling options to ./configure { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable debugging" >&5 $as_echo_n "checking whether to enable debugging... " >&6; } ax_enable_debug_default=yes ax_enable_debug_is_release=$ax_is_release # If this is a release, override the default. if test "$ax_enable_debug_is_release" = "yes"; then : ax_enable_debug_default="no" fi # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; else enable_debug=$ax_enable_debug_default fi # empty mean debug yes if test "x$enable_debug" = "x"; then : enable_debug="yes" fi # case of debug case $enable_debug in #( yes) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } CFLAGS="${CFLAGS} -g -O0" CXXFLAGS="${CXXFLAGS} -g -O0" FFLAGS="${FFLAGS} -g -O0" FCFLAGS="${FCFLAGS} -g -O0" OBJCFLAGS="${OBJCFLAGS} -g -O0" ;; #( info) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: info" >&5 $as_echo "info" >&6; } CFLAGS="${CFLAGS} -g" CXXFLAGS="${CXXFLAGS} -g" FFLAGS="${FFLAGS} -g" FCFLAGS="${FCFLAGS} -g" OBJCFLAGS="${OBJCFLAGS} -g" ;; #( profile) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: profile" >&5 $as_echo "profile" >&6; } CFLAGS="${CFLAGS} -g -pg" CXXFLAGS="${CXXFLAGS} -g -pg" FFLAGS="${FFLAGS} -g -pg" FCFLAGS="${FCFLAGS} -g -pg" OBJCFLAGS="${OBJCFLAGS} -g -pg" LDFLAGS="${LDFLAGS} -pg" ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if test "x${CFLAGS+set}" != "xset"; then : CFLAGS="" fi if test "x${CXXFLAGS+set}" != "xset"; then : CXXFLAGS="" fi if test "x${FFLAGS+set}" != "xset"; then : FFLAGS="" fi if test "x${FCFLAGS+set}" != "xset"; then : FCFLAGS="" fi if test "x${OBJCFLAGS+set}" != "xset"; then : OBJCFLAGS="" fi ;; esac if test "x$enable_debug" = "xyes"; then : else $as_echo "#define NDEBUG /**/" >>confdefs.h fi ax_enable_debug=$enable_debug ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # yascreen use C, not CXX ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi # Allows for building static libraries if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # This prevents problems such as those shown in # https://github.com/theimpossibleastronaut/rmw/commit/90200c2df06b16f16b5d21d25c51966c0ee65b23#commitcomment-30291627 # and https://github.com/theimpossibleastronaut/rmw/issues/21 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then : enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval else USE_MAINTAINER_MODE=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 $as_echo "$USE_MAINTAINER_MODE" >&6; } if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE { $as_echo "$as_me:${as_lineno-$LINENO}: checking lib pcre" >&5 $as_echo_n "checking lib pcre... " >&6; } # Check whether --with-pcre was given. if test "${with_pcre+set}" = set; then : withval=$with_pcre; else with_pcre="yes" fi if test ".$with_pcre" = ".no" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 $as_echo "disabled" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: (testing)" >&5 $as_echo "(testing)" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcre_study in -lpcre" >&5 $as_echo_n "checking for pcre_study in -lpcre... " >&6; } if ${ac_cv_lib_pcre_pcre_study+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpcre $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pcre_study (); int main () { return pcre_study (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pcre_pcre_study=yes else ac_cv_lib_pcre_pcre_study=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcre_pcre_study" >&5 $as_echo "$ac_cv_lib_pcre_pcre_study" >&6; } if test "x$ac_cv_lib_pcre_pcre_study" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPCRE 1 _ACEOF LIBS="-lpcre $LIBS" fi if test "$ac_cv_lib_pcre_pcre_study" = "yes" ; then PCRE_LIBS="-lpcre" { $as_echo "$as_me:${as_lineno-$LINENO}: checking lib pcre" >&5 $as_echo_n "checking lib pcre... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PCRE_LIBS" >&5 $as_echo "$PCRE_LIBS" >&6; } else OLDLDFLAGS="$LDFLAGS" ; LDFLAGS="$LDFLAGS -L$with_pcre/lib" OLDCPPFLAGS="$CPPFLAGS" ; CPPFLAGS="$CPPFLAGS -I$with_pcre/include" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcre_compile in -lpcre" >&5 $as_echo_n "checking for pcre_compile in -lpcre... " >&6; } if ${ac_cv_lib_pcre_pcre_compile+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpcre $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pcre_compile (); int main () { return pcre_compile (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pcre_pcre_compile=yes else ac_cv_lib_pcre_pcre_compile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcre_pcre_compile" >&5 $as_echo "$ac_cv_lib_pcre_pcre_compile" >&6; } if test "x$ac_cv_lib_pcre_pcre_compile" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPCRE 1 _ACEOF LIBS="-lpcre $LIBS" fi CPPFLAGS="$OLDCPPFLAGS" LDFLAGS="$OLDLDFLAGS" if test "$ac_cv_lib_pcre_pcre_compile" = "yes" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: .setting PCRE_LIBS -L$with_pcre/lib -lpcre" >&5 $as_echo ".setting PCRE_LIBS -L$with_pcre/lib -lpcre" >&6; } PCRE_LIBS="-L$with_pcre/lib -lpcre" test -d "$with_pcre/include" && PCRE_CFLAGS="-I$with_pcre/include" { $as_echo "$as_me:${as_lineno-$LINENO}: checking lib pcre" >&5 $as_echo_n "checking lib pcre... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PCRE_LIBS" >&5 $as_echo "$PCRE_LIBS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking lib pcre" >&5 $as_echo_n "checking lib pcre... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, (WARNING)" >&5 $as_echo "no, (WARNING)" >&6; } fi fi fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi # Check whether --with-ncurses was given. if test "${with_ncurses+set}" = set; then : withval=$with_ncurses; else with_ncurses=check fi # Check whether --with-ncursesw was given. if test "${with_ncursesw+set}" = set; then : withval=$with_ncursesw; else with_ncursesw=check fi ax_saved_LIBS=$LIBS ax_saved_CPPFLAGS=$CPPFLAGS if test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes; then : ax_with_plaincurses=no else ax_with_plaincurses=check fi ax_cv_curses_which=no # Test for NcursesW if test "x$CURSES_LIBS" = x && test "x$with_ncursesw" != xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ncursesw via pkg-config" >&5 $as_echo_n "checking for ncursesw via pkg-config... " >&6; } if test -n "$_ax_cv_ncursesw_libs"; then pkg_cv__ax_cv_ncursesw_libs="$_ax_cv_ncursesw_libs" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncursesw\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncursesw") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv__ax_cv_ncursesw_libs=`$PKG_CONFIG --libs "ncursesw" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$_ax_cv_ncursesw_cppflags"; then pkg_cv__ax_cv_ncursesw_cppflags="$_ax_cv_ncursesw_cppflags" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncursesw\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncursesw") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv__ax_cv_ncursesw_cppflags=`$PKG_CONFIG --cflags "ncursesw" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } # No suitable .pc file found, have to find flags via fallback { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ncursesw via fallback" >&5 $as_echo_n "checking for ncursesw via fallback... " >&6; } if ${ax_cv_ncursesw+:} false; then : $as_echo_n "(cached) " >&6 else $as_echo pkg_cv__ax_cv_ncursesw_libs="-lncursesw" pkg_cv__ax_cv_ncursesw_cppflags="-D_GNU_SOURCE $CURSES_CFLAGS" LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncursesw_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_ncursesw_cppflags" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr() with $pkg_cv__ax_cv_ncursesw_libs" >&5 $as_echo_n "checking for initscr() with $pkg_cv__ax_cv_ncursesw_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char initscr (); int main () { return initscr (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nodelay() with $pkg_cv__ax_cv_ncursesw_libs" >&5 $as_echo_n "checking for nodelay() with $pkg_cv__ax_cv_ncursesw_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char nodelay (); int main () { return nodelay (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_ncursesw=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } pkg_cv__ax_cv_ncursesw_libs="$pkg_cv__ax_cv_ncursesw_libs -ltinfow" LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncursesw_libs" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nodelay() with $pkg_cv__ax_cv_ncursesw_libs" >&5 $as_echo_n "checking for nodelay() with $pkg_cv__ax_cv_ncursesw_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char nodelay (); int main () { return nodelay (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_ncursesw=yes else ax_cv_ncursesw=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext else ax_cv_ncursesw=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_ncursesw" >&5 $as_echo "$ax_cv_ncursesw" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # Found .pc file, using its information LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncursesw_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_ncursesw_cppflags" ax_cv_ncursesw=yes fi if test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes; then : as_fn_error $? "--with-ncursesw specified but could not find NcursesW library" "$LINENO" 5 fi if test "x$ax_cv_ncursesw" = xyes; then : ax_cv_curses=yes ax_cv_curses_which=ncursesw CURSES_LIBS="$pkg_cv__ax_cv_ncursesw_libs" CURSES_CFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags" $as_echo "#define HAVE_NCURSESW 1" >>confdefs.h $as_echo "#define HAVE_CURSES 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ncursesw/curses.h" >&5 $as_echo_n "checking for working ncursesw/curses.h... " >&6; } if ${ax_cv_header_ncursesw_curses_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE_EXTENDED 1 #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_ncursesw_curses_h=yes else ax_cv_header_ncursesw_curses_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_ncursesw_curses_h" >&5 $as_echo "$ax_cv_header_ncursesw_curses_h" >&6; } if test "x$ax_cv_header_ncursesw_curses_h" = xyes; then : ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_ENHANCED 1" >>confdefs.h $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h $as_echo "#define HAVE_NCURSESW_CURSES_H 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ncursesw.h" >&5 $as_echo_n "checking for working ncursesw.h... " >&6; } if ${ax_cv_header_ncursesw_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE_EXTENDED 1 #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_ncursesw_h=yes else ax_cv_header_ncursesw_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_ncursesw_h" >&5 $as_echo "$ax_cv_header_ncursesw_h" >&6; } if test "x$ax_cv_header_ncursesw_h" = xyes; then : ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_ENHANCED 1" >>confdefs.h $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h $as_echo "#define HAVE_NCURSESW_H 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ncurses.h" >&5 $as_echo_n "checking for working ncurses.h... " >&6; } if ${ax_cv_header_ncurses_h_with_ncursesw+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE_EXTENDED 1 #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_ncurses_h_with_ncursesw=yes else ax_cv_header_ncurses_h_with_ncursesw=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_ncurses_h_with_ncursesw" >&5 $as_echo "$ax_cv_header_ncurses_h_with_ncursesw" >&6; } if test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes; then : ax_cv_curses_enhanced=yes ax_cv_curses_color=yes ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_ENHANCED 1" >>confdefs.h $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h $as_echo "#define HAVE_NCURSES_H 1" >>confdefs.h fi if test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h" >&5 $as_echo "$as_me: WARNING: could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h" >&2;} fi fi fi unset pkg_cv__ax_cv_ncursesw_libs unset pkg_cv__ax_cv_ncursesw_cppflags # Test for Ncurses if test "x$CURSES_LIBS" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ncurses via pkg-config" >&5 $as_echo_n "checking for ncurses via pkg-config... " >&6; } if test -n "$_ax_cv_ncurses_libs"; then pkg_cv__ax_cv_ncurses_libs="$_ax_cv_ncurses_libs" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncurses\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncurses") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv__ax_cv_ncurses_libs=`$PKG_CONFIG --libs "ncurses" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$_ax_cv_ncurses_cppflags"; then pkg_cv__ax_cv_ncurses_cppflags="$_ax_cv_ncurses_cppflags" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncurses\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncurses") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv__ax_cv_ncurses_cppflags=`$PKG_CONFIG --cflags "ncurses" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } # No suitable .pc file found, have to find flags via fallback { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ncurses via fallback" >&5 $as_echo_n "checking for ncurses via fallback... " >&6; } if ${ax_cv_ncurses+:} false; then : $as_echo_n "(cached) " >&6 else $as_echo pkg_cv__ax_cv_ncurses_libs="-lncurses" pkg_cv__ax_cv_ncurses_cppflags="-D_GNU_SOURCE $CURSES_CFLAGS" LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncurses_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_ncurses_cppflags" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr() with $pkg_cv__ax_cv_ncurses_libs" >&5 $as_echo_n "checking for initscr() with $pkg_cv__ax_cv_ncurses_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char initscr (); int main () { return initscr (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nodelay() with $pkg_cv__ax_cv_ncurses_libs" >&5 $as_echo_n "checking for nodelay() with $pkg_cv__ax_cv_ncurses_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char nodelay (); int main () { return nodelay (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_ncurses=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } pkg_cv__ax_cv_ncurses_libs="$pkg_cv__ax_cv_ncurses_libs -ltinfo" LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncurses_libs" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nodelay() with $pkg_cv__ax_cv_ncurses_libs" >&5 $as_echo_n "checking for nodelay() with $pkg_cv__ax_cv_ncurses_libs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char nodelay (); int main () { return nodelay (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_ncurses=yes else ax_cv_ncurses=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext else ax_cv_ncurses=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_ncurses" >&5 $as_echo "$ax_cv_ncurses" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # Found .pc file, using its information LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_ncurses_libs" CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_ncurses_cppflags" ax_cv_ncurses=yes fi if test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes; then : as_fn_error $? "--with-ncurses specified but could not find Ncurses library" "$LINENO" 5 fi if test "x$ax_cv_ncurses" = xyes; then : ax_cv_curses=yes ax_cv_curses_which=ncurses CURSES_LIBS="$pkg_cv__ax_cv_ncurses_libs" CURSES_CFLAGS="$pkg_cv__ax_cv_ncurses_cppflags" $as_echo "#define HAVE_NCURSES 1" >>confdefs.h $as_echo "#define HAVE_CURSES 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ncurses/curses.h" >&5 $as_echo_n "checking for working ncurses/curses.h... " >&6; } if ${ax_cv_header_ncurses_curses_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_ncurses_curses_h=yes else ax_cv_header_ncurses_curses_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_ncurses_curses_h" >&5 $as_echo "$ax_cv_header_ncurses_curses_h" >&6; } if test "x$ax_cv_header_ncurses_curses_h" = xyes; then : ax_cv_curses_color=yes ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h $as_echo "#define HAVE_NCURSES_CURSES_H 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ncurses.h" >&5 $as_echo_n "checking for working ncurses.h... " >&6; } if ${ax_cv_header_ncurses_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_ncurses_h=yes else ax_cv_header_ncurses_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_ncurses_h" >&5 $as_echo "$ax_cv_header_ncurses_h" >&6; } if test "x$ax_cv_header_ncurses_h" = xyes; then : ax_cv_curses_color=yes ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h $as_echo "#define HAVE_NCURSES_H 1" >>confdefs.h fi if test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find a working ncurses/curses.h or ncurses.h" >&5 $as_echo "$as_me: WARNING: could not find a working ncurses/curses.h or ncurses.h" >&2;} fi fi fi unset pkg_cv__ax_cv_ncurses_libs unset pkg_cv__ax_cv_ncurses_cppflags # Test for plain Curses (or if CURSES_LIBS was set by user) if test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno; then : if test "x$CURSES_LIBS" != x; then : LIBS="$ax_saved_LIBS $CURSES_LIBS" else LIBS="$ax_saved_LIBS -lcurses" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Curses library" >&5 $as_echo_n "checking for Curses library... " >&6; } if ${ax_cv_plaincurses+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char initscr (); int main () { return initscr (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_plaincurses=yes else ax_cv_plaincurses=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_plaincurses" >&5 $as_echo "$ax_cv_plaincurses" >&6; } if test "x$ax_cv_plaincurses" = xyes; then : ax_cv_curses=yes ax_cv_curses_which=plaincurses if test "x$CURSES_LIBS" = x; then : CURSES_LIBS="-lcurses" fi $as_echo "#define HAVE_CURSES 1" >>confdefs.h # Check for base conformance (and header file) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working curses.h" >&5 $as_echo_n "checking for working curses.h... " >&6; } if ${ax_cv_header_curses_h+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; initscr(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_header_curses_h=yes else ax_cv_header_curses_h=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_header_curses_h" >&5 $as_echo "$ax_cv_header_curses_h" >&6; } if test "x$ax_cv_header_curses_h" = xyes; then : $as_echo "#define HAVE_CURSES_H 1" >>confdefs.h # Check for X/Open Enhanced conformance { $as_echo "$as_me:${as_lineno-$LINENO}: checking for X/Open Enhanced Curses conformance" >&5 $as_echo_n "checking for X/Open Enhanced Curses conformance... " >&6; } if ${ax_cv_plaincurses_enhanced+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE_EXTENDED 1 #include #ifndef _XOPEN_CURSES #error "this Curses library is not enhanced" "this Curses library is not enhanced" #endif int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; attr_t d = WA_NORMAL; cchar_t e; wint_t f; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); wattr_set(stdscr, d, 0, NULL); wget_wch(stdscr, &f); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_plaincurses_enhanced=yes else ax_cv_plaincurses_enhanced=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_plaincurses_enhanced" >&5 $as_echo "$ax_cv_plaincurses_enhanced" >&6; } if test "x$ax_cv_plaincurses_enhanced" = xyes; then : ax_cv_curses_enhanced=yes ax_cv_curses_color=yes $as_echo "#define HAVE_CURSES_ENHANCED 1" >>confdefs.h $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h fi # Check for color functions { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Curses color functions" >&5 $as_echo_n "checking for Curses color functions... " >&6; } if ${ax_cv_plaincurses_color+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE_EXTENDED 1 #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; chtype c = COLOR_PAIR(1) & A_COLOR; initscr(); init_pair(1, COLOR_WHITE, COLOR_RED); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_plaincurses_color=yes else ax_cv_plaincurses_color=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_plaincurses_color" >&5 $as_echo "$ax_cv_plaincurses_color" >&6; } if test "x$ax_cv_plaincurses_color" = xyes; then : ax_cv_curses_color=yes $as_echo "#define HAVE_CURSES_COLOR 1" >>confdefs.h fi # Check for obsolete functions { $as_echo "$as_me:${as_lineno-$LINENO}: checking for obsolete Curses functions" >&5 $as_echo_n "checking for obsolete Curses functions... " >&6; } if ${ax_cv_plaincurses_obsolete+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { chtype a = A_BOLD; int b = KEY_LEFT; int g = getattrs(stdscr); int h = getcurx(stdscr) + getmaxx(stdscr); initscr(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ax_cv_plaincurses_obsolete=yes else ax_cv_plaincurses_obsolete=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_plaincurses_obsolete" >&5 $as_echo "$ax_cv_plaincurses_obsolete" >&6; } if test "x$ax_cv_plaincurses_obsolete" = xyes; then : ax_cv_curses_obsolete=yes $as_echo "#define HAVE_CURSES_OBSOLETE 1" >>confdefs.h fi fi if test "x$ax_cv_header_curses_h" = xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find a working curses.h" >&5 $as_echo "$as_me: WARNING: could not find a working curses.h" >&2;} fi fi fi if test "x$ax_cv_curses" != xyes; then : ax_cv_curses=no fi if test "x$ax_cv_curses_enhanced" != xyes; then : ax_cv_curses_enhanced=no fi if test "x$ax_cv_curses_color" != xyes; then : ax_cv_curses_color=no fi if test "x$ax_cv_curses_obsolete" != xyes; then : ax_cv_curses_obsolete=no fi LIBS=$ax_saved_LIBS CPPFLAGS=$ax_saved_CPPFLAGS unset ax_saved_LIBS unset ax_saved_CPPFLAGS # Check whether --enable-yascreen was given. if test "${enable_yascreen+set}" = set; then : enableval=$enable_yascreen; fi if test "x$enable_yascreen" = xyes || test "x$ax_cv_curses" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: -- vfu will build with yascreen support" >&5 $as_echo "$as_me: -- vfu will build with yascreen support" >&6;} want_yascreen="yes" if test "x$want_yascreen" = xyes; then WANT_YASCREEN_TRUE= WANT_YASCREEN_FALSE='#' else WANT_YASCREEN_TRUE='#' WANT_YASCREEN_FALSE= fi $as_echo "#define USE_YASCREEN 1" >>confdefs.h CXXFLAGS="$CXXFLAGS -I$ac_abs_confdir/yascreen" LDFLAGS="-L../yas-libs" LIBS="-lrt -lyascreen" else if test "x$want_yascreen" = xno; then WANT_YASCREEN_TRUE= WANT_YASCREEN_FALSE='#' else WANT_YASCREEN_TRUE='#' WANT_YASCREEN_FALSE= fi CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" LIBS="$CURSES_LIBS" fi # Defining HAVE_CONFIG_H is needed because we are using different sets of Makefiles DEFS="$DEFS -DHAVE_CONFIG_H" ac_config_files="$ac_config_files Makefile yas-libs/Makefile vslib/Makefile vslib/t/Makefile vfu/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WANT_YASCREEN_TRUE}" && test -z "${WANT_YASCREEN_FALSE}"; then as_fn_error $? "conditional \"WANT_YASCREEN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WANT_YASCREEN_TRUE}" && test -z "${WANT_YASCREEN_FALSE}"; then as_fn_error $? "conditional \"WANT_YASCREEN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by vfu $as_me 4.18, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to . vfu home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ vfu config.status 4.18 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.in" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "yas-libs/Makefile") CONFIG_FILES="$CONFIG_FILES yas-libs/Makefile" ;; "vslib/Makefile") CONFIG_FILES="$CONFIG_FILES vslib/Makefile" ;; "vslib/t/Makefile") CONFIG_FILES="$CONFIG_FILES vslib/t/Makefile" ;; "vfu/Makefile") CONFIG_FILES="$CONFIG_FILES vfu/Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi vfu-4.22/FAQ0000644000175000017500000002745414145574023011166 0ustar cadecade FREQUENTLY ASKED QUESTIONS ABOUT: VF File Manager for Unix/Linux ( VFU ) by Vladi Belperchinov-Shabanski "Cade" http://cade.datamax.bg/vfu 1. What is `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 2. Does VFU follows directory links when calculating directory(ies) size? No. :) 3. I cannot see entire filename? Try '>' or '0' -- it will show only filenames... or try `1'..`6' to toggle different info entries. 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! This behavior may be changed in the future but not for now. (in general `**' does NOT mean that you can 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? 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. (technical note: real parsing re is "^(.*)([0123456789]+)(\\.(.*))?$" if you know what this means:)) 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: 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' (key `O') 14. Can I browse/view more than one file at the same time? 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? 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 99. I don't/do like feature X, can I turn it OFF/ON? I tried to give option for every arguable behavior of most features. So you have to check `Options' menu (key O inside VFU) and vfu.conf file to find out how to solve the problem. If this doesn't help contact me... vfu-4.22/GIT_HISTORY0000644000175000017500000004351114151773033012412 0ustar cadecadecommit 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