# HG changeset patch # User John W. Eaton # Date 1562948083 14400 # Node ID 61701d1317a1139ae9a8a82f6c8dc76c06892cf8 # Parent 733431da9742f26208ecfc6809b27952248b5ee7# Parent 940c1b6e345359a68fc61b10d6e340fd3f9ba841 maint: Merge stable to default. diff -r 940c1b6e3453 -r 61701d1317a1 .dir-locals.el --- a/.dir-locals.el Wed Jul 10 20:02:44 2019 -0700 +++ b/.dir-locals.el Fri Jul 12 12:14:43 2019 -0400 @@ -1,11 +1,6 @@ ((nil . ((c-file-style . "gnu") (indent-tabs-mode . nil) - (fill-column . 72) - (eval . (when (and (buffer-file-name) - (string-match-p "\\.h\\'" (buffer-file-name)) - (not (string-match-p "/gnulib/" (buffer-file-name)))) - (c++-mode) - (c-set-style "gnu"))))) + (fill-column . 72))) (change-log-mode . ((indent-tabs-mode . t))) (makefile-mode . ((indent-tabs-mode . t)))) diff -r 940c1b6e3453 -r 61701d1317a1 .hgignore --- a/.hgignore Wed Jul 10 20:02:44 2019 -0700 +++ b/.hgignore Fri Jul 12 12:14:43 2019 -0400 @@ -28,6 +28,11 @@ ^Makefile\.in$ ^INSTALL$ +## CMake associated files +# Octave doesn't use CMake, but some IDEs use this index file to indicate +# what files are part of a project (e.g., CLion). Ignore it (bug #55901). +^CMakeLists.txt$ + ## Emacs associated files (^|/)TAGS$ (^|/)semantic.cache$ @@ -60,7 +65,6 @@ .*\.Plo$ .*\.Po$ - ## DLDFCN associated files ^libinterp/dldfcn/module\.mk$ (^|/)libinterp/dldfcn/PKG_ADD$ diff -r 940c1b6e3453 -r 61701d1317a1 CITATION --- a/CITATION Wed Jul 10 20:02:44 2019 -0700 +++ b/CITATION Fri Jul 12 12:14:43 2019 -0400 @@ -15,5 +15,5 @@ } We have invested a lot of time and effort in creating GNU Octave, please -cite it when using it. See also `citation pkgname' for citing Octave +cite it when using it. See also 'citation pkgname' for citing Octave packages. diff -r 940c1b6e3453 -r 61701d1317a1 NEWS --- a/NEWS Wed Jul 10 20:02:44 2019 -0700 +++ b/NEWS Fri Jul 12 12:14:43 2019 -0400 @@ -1,324 +1,151 @@ -Summary of important user-visible changes for version 5 (2019-02-23): --------------------------------------------------------------------- +Summary of important user-visible changes for version 6 (yyyy-mm-dd): +---------------------------------------------------------------------- ### General improvements -- The Octave plotting system now supports high resolution screens, - i.e., those with greater than 96 DPI which are referred to as - HiDPI/Retina monitors. - -- Unicode character support for files and folders in Windows. +- The `edit` function option `"editinplace"` now defaults to `true` and + the option `"home"` now defaults to the empty matrix `[]`. Files will + no longer be copied to the user's HOME directory for editing. The old + behavior can be restored by setting `"editinplace"` to `false` and + `"home"` to `"~/octave"`. -- A new core function `movfun` will apply a function to a sliding - window of arbitrary size on a dataset and accumulate the results. - Many common cases have been implemented using the naming - scheme `movXXX` where `XXX` is the function that will be applied. - For example, the moving average over a dataset is `movmean`. - New moving window functions: - - `movfun` `movslice` - `movmad` `movmax` `movmean` `movmedian` `movmin` `movprod` - `movstd` `movsum` `movvar` +#### Graphics backend -- The `fsolve` function has been tweaked to use larger step sizes when - calculating the Jacobian of a function with finite differences. - This leads to faster convergence. - -- The `ranks` function has been recoded for performance and is now 25X - faster. In addition, it now supports a third argument that specifies - how to resolve the ranking of tie values. +- Graphic primitives now accept a color property value of `"none"` + which is useful when a particular primitive needs to be hidden + (for example, the Y-axis of an axes object with `"ycolor" = "none"`) + without hiding the entire primitive `"visibility" = "off"`. -- The function `randi` has been recoded to produce an unbiased (all - results are equally likely) sample of integers. This may produce - different results in existing code. If it is necessary to reproduce - the exact random integer sequence as in previous versions use - - `ri = imin + floor ((imax - imin + 1) * rand ());` - -- The function `isdefinite` now returns `true` or `false` rather than - `-1`, `0`, or `1`. To test for a positive semi-definite matrix (old - output of `0`) check whether the following two conditions hold: +- A new property `"FontSmoothing"` has been added to text and axes + objects that controls whether anti-aliasing is used during the + rendering of characters. The default is "on" which produces smooth, + more visually appealing text. - `isdefinite (A) => 0` and `isdefinite (A + 5*TOL, TOL) => 1` - -- The `intmax`, `intmin`, and `flintmax` functions now accept a variable - as input. Existing code to query the range of an existing variable can - be simplified by removing the call to `class` that was previously - required. For example defining the variable `x = int8 (3)` in the - workspace, calls like +- The figure property `"windowscrollwheelfcn"`is now implemented. + This makes it possible to provide a callback function to be executed when + users manipulate the mouse wheel on a given figure. - `range = [ intmin(class(x)), intmax(class(x)) ]` - - can in Octave 5 be simplified to `range = [ intmin(x), intmax(x) ]`. - -- The path handling functions no longer perform variable or brace - expansion on path elements and Octave's load-path is no longer - subject to these expansions. +- The figure properties `"pointer"`, `"pointershapecdata"`, and + `"pointershapehotspot"` are now implemented. This makes it possible + to change the shape of the cursor (pointer in Matlab-speak) displayed + in a plot window. -- A new printing device is available, `"-ddumb"`, which produces ASCII - art for plots. This device is only available with the gnuplot toolkit. - -- The `msgbox` function has changed in two respects: the default WindowStyle - is now `"non-modal"`, and the default interpreter for the message is now - `"tex"`. Both WindowStyle and Interpreter can be controlled by passing an - option struct argument. - -### Dependencies +- The appearance of patterned lines `"LineStyle" = ":"|"--"|"-."` has + been improved for small widths (`"LineWidth"` less than 1.5 pixels) + which is a common scenario. -- The GUI requires Qt libraries. The minimum Qt4 version supported is - Qt4.8. Qt5 of any version is preferred. +- Printing to EPS files now uses a tight bounding box (`"-tight"` + argument to print) by default. This makes more sense for EPS + files which are normally embedded within other documents, and is + Matlab compatible. If necessary use the `"-loose"` option to + reproduce figures as they appeared in previous versions of Octave. -- The OSMesa library is no longer used. To print invisible figures - when using OpenGL graphics, the Qt `QOFFSCREENSURFACE` feature must be - available and you must use the qt graphics toolkit. - -- The FFTW library is now required to perform FFT calculations. - The FFTPACK sources have been removed from Octave. - +- The following print devices are no longer officially supported: cdr, + corel, aifm, ill, cgm, hpgl, mf and dxf. A warning will be thrown + when using those devices, and the code for supporting those formats + will eventually be removed from a future version of Octave. ### Matlab compatibility -- The determination of an object's dimensions, size, and shape by the - functions `ndims`, `rows`, `columns`, `isscalar`, `isvector`, - `isrow`, `iscolumn`, `ismatrix`, and `issquare` now fully depends - on the function size. Thus, any user-defined object can ensure correct - treatment by the aforementioned functions by properly overloading the - `size` function. +- The function `unique` now returns column index vectors for the second + and third outputs. When duplicate values are present, the default + index to return is now the `"first"` occurrence. The previous Octave + behavior, or Matlab behavior from releases prior to R2012b, can be + obtained by using the `"legacy"` flag. -- The functions `issymmetric` and `ishermitian` accept an option - `"nonskew"` or `"skew"` to calculate the symmetric or skew-symmetric - property of a matrix. Performance has also been increased. - -- The `issorted` function now uses a direction option of `"ascend"` - or `"descend"`. Change all uses of `"ascending"` and `"descending"` - in existing code to the new options. +- The function `setdiff` with the `"rows"` argument now returns Matlab + compatible results. The previous Octave behavior, or Matlab behavior + from releases prior to R2012b, can be obtained by using the `"legacy"` + flag. -- The `strncmp` and `strncmpi` functions now return `true` if the two - input strings match, even though the number of characters specified - by `n` exceeds the string length. For Example: - - `strncmp ("abc", "abc", 100)` - - returns `true` in Octave 5 and `false` in older versions of Octave. +- The functions `intersect`, `setxor`, `union`, now accept a `"legacy"` + flag which changes the index values (second and third outputs) as well + as the orientation of all outputs to match Matlab releases prior to + R2012b. -- The `str2func` function no longer accepts a second `"global"` argument. - This argument was typically used to allow functions that accept - function names as arguments to avoid conflicts with subfunctions or - nested functions. Instead, it's best to avoid this situation - entirely and require users to pass function handles rather than - function names. +- Complex RESTful web services can now be accessed by the `webread` and + `webwrite` functions alongside with the `weboptions` structure. One + major feature is the support for cookies to enable RESTful + communication with the web service. -- Using `clear` with no arguments now removes only local variables - from the current workspace. Global variables will no longer be - visible, but they continue to exist in the global workspace and - possibly other workspaces such as the base workspace. + Additionally, the system web browser can be opened by the `web` function. + +- The interpreter now supports handles to nested functions. + +- The graphics properties `"LineWidth"` and `"MarkerSize"` are now + measured in points, *not* pixels. Compared to previous versions + of Octave, some lines and markers will appear 4/3 larger. -#### Nonlinear Equations - -Several default solver options have been changed to be Matlab compatible. -This *may* result in existing code producing different results. - -- `fsolve` - - Option | New Default | Old Default - ---------------|------------------|------------- - `FinDiffType` | `"forward"` | `"central"` - `MaxFunEvals` | `100*length(x0)` | `Inf` - `TolFun` | `1e-6` | `1e-7` - `TolX` | `1e-6` | `1e-7` - `Updating` | `"off"` | `"on"` - -- `fminsearch` - - Option | New Default | Old Default - ---------|-------------|------------ - `TolFun` | `1e-7` | `1e-4` - -- `fminbnd` - - Option | New Default | Old Default - ---------------|-------------|------------ - `MaxFunEvals` | `500` | `Inf` - `MaxIter` | `500` | `Inf` - `TolX` | `1e-4` | `1e-8` - -- `fminunc` - - Option | New Default | Old Default - ---------------|------------------|------------ - `FinDiffType` | `"forward"` | `"central"` - `MaxFunEvals` | `100*length(x0)` | `Inf` - `TolX` | `1e-6` | `1e-7` - `TolFun` | `1e-6` | `1e-7` - - -#### Graphic objects - -- Figure graphic objects have a new property `"Number"` which is - read-only and will return the handle (number) of the figure. - However, if the property `"IntegerHandle"` has been set to `"off"` - then the property will return an empty matrix `[]`. - -- Patch and surface graphic objects now use the `"FaceNormals"` property - for flat lighting. +### Alphabetical list of new functions added in Octave 6 -- `"FaceNormals"` and `"VertexNormals"` for patch and surface graphic - objects are now calculated only when necessary to improve graphics - performance. In order for any normals to be calculated the - `"FaceLighting"` property must be set to `"flat"` (FaceNormals) or - `"gouraud"` (VertexNormals), **and** a light object must be present - in the axes. - -- The `"Margin"` property of `text`-objects has a new default of `3` - rather than `2`. - -- Printing to raster formats (bitmaps like PNG or JPEG) now uses an - OpenGL-based method by default. The print options `"-opengl"` - (raster) and `"-painters"` (vector) have been added ("qt" toolkit - only). The figure property `"renderer"` specifies which renderer to - use. When the property `"renderermode"` is `"auto"` Octave will select - `"-opengl"` for a raster output format and `"-painters"` for a vector - output format. - -- A new print option `"-RGBImage"` has been added which captures the - pixels of a figure as an image. This is similar to screen capture - tools, except that print formatting options can be used to, for - example, change the resolution or display the image in black and - white. - -- Two new print options for page-based formats (PDF, PostScript) have - been added. The `"-fillpage"` option will stretch the plot to occupy - the entire page with 0.25 inch margins all around. The `"-bestfit"` - option will expand the plot to take up as much room as possible on - the page without distorting the original aspect ratio of the plot. - -- Printing using the `"-dtiff"` output device will now create compressed - images using LZW compression. To produce uncompressed images use the - `"-dtiffn"` device. - - -### Legacy functions - -The following functions have been declared legacy functions which -means they are obsolete and should not be used in any new code. -Unlike deprecated functions, however, their removal from Octave has -not yet been scheduled. - - Function | Replacement - -----------------------|------------------ - `findstr` | `strfind` - `flipdim` | `flip` - `isdir` | `isfolder` or `dir_in_loadpath` - `isequalwithequalnans` | `isequaln` - `isstr` | `ischar` - `setstr` | `char` - `strmatch` | `strncmp` or `strcmp` - `strread` | `textscan` - `textread` | `textscan` +- `lightangle` +- `newline` +- `verLessThan` +- `web` +- `weboptions` +- `webread` +- `webwrite` ### Deprecated functions and properties -The following functions and graphics properties have been deprecated -in Octave 5 and will be removed from Octave 7 (or whatever version -is the second major release after 5): - -- Functions - - Function | Replacement - -------------------------|------------------- - `output_max_field_width` | `output_precision` - `is_keyword` | `iskeyword` - -- Graphics properties - - Object | Property | Value - -----------------|---------------|------------ - `text` | `fontangle` | `"oblique"` - `uibuttongroup` | `fontangle` | `"oblique"` - `uicontrol` | `fontangle` | `"oblique"` - `uipanel` | `fontangle` | `"oblique"` - `uitable` | `fontangle` | `"oblique"` - -- Specifying `legend` position with a numeric argument is deprecated. - Use a string argument instead. - -- The environment variable used by `mkoctfile` for linker flags is now - `LDFLAGS` rather than `LFLAGS`. `LFLAGS` is deprecated, and a warning - is emitted if is used, but it will continue to work. - - -### Removed functions and properties - -The following functions and properties were deprecated in Octave 4.2 -and have been removed from Octave 5. +The following functions and properties have been deprecated in Octave 6 +and will be removed from Octave 8 (or whatever version is the second +major release after 6): - Functions Function | Replacement -----------------------|------------------ - `bitmax` | `flintmax` - `mahalanobis` | `mahal` in Octave Forge statistics pkg - `md5sum` | `hash` - `octave_config_info` | `__octave_config_info__` - `onenormest` | `normest1` - `sleep` | `pause` - `usleep` | `pause` - `wavread` | `audioread` - `wavwrite` | `audiowrite` + `runtests` | `oruntests` - Properties - Object | Property | Value - ------------|-------------------|--------- - `axes` | `xaxislocation` | `"zero"` - | `yaxislocation` | `"zero"` - `hggroup` | `erasemode` | - `image` | `erasemode` | - `line` | `erasemode` | - `patch` | `erasemode` | - `patch` | `normalmode` | - `surface` | `erasemode` | - `surface` | `normalmode` | - `text` | `erasemode` | + Object | Property | Value + -----------------|---------------|------------ + | | -### Alphabetical list of new functions added in 5 +### Removed functions and properties + +The following functions and properties were deprecated in Octave 4.4 +and have been removed from Octave 6. + +- Functions + + Function | Replacement + ---------------------|------------------ + `chop` | `sprintf` for visual results + `desktop` | `isguirunning` + `tmpnam` | `tempname` + `toascii` | `double` + `java2mat` | `__java2mat__` + +- Properties -- `clearvars` -- `isfile` -- `isfolder` -- `matlab.lang.makeUniqueStrings` -- `matlab.lang.makeValidName` -- `movegui` -- `movfun` -- `movie` -- `movmad` -- `movmax` -- `movmean` -- `movmedian` -- `movmin` -- `movprod` -- `movslice` -- `movstd` -- `movsum` -- `movvar` -- `openfig` -- `ordeig` -- `savefig` -- `uitable` + Object | Property | Value + ---------------------|---------------------------|----------------------- + `figure` | `doublebuffer` | + | `mincolormap` | + | `wvisual` | + | `wvisualmode` | + | `xdisplay` | + | `xvisual` | + | `xvisualmode` | + `axes` | `drawmode` | + `annotation` | `edgecolor ("rectangle")` | + `text` | `fontweight` | `"demi"` and `"light"` + `uicontrol` | `fontweight` | `"demi"` and `"light"` + `uipanel` | `fontweight` | `"demi"` and `"light"` + `uibuttongroup` | `fontweight` | `"demi"` and `"light"` + `uitable` | `fontweight` | `"demi"` and `"light"` ### Old release news -- [Octave 4.4.x](etc/NEWS.4) -- [Octave 4.2.x](etc/NEWS.4) -- [Octave 4.0.x](etc/NEWS.4) -- [Octave 3.8.x](etc/NEWS.3) -- [Octave 3.6.x](etc/NEWS.3) -- [Octave 3.4.x](etc/NEWS.3) -- [Octave 3.2.x](etc/NEWS.3) -- [Octave 3.0.x](etc/NEWS.3) +- [Octave 5.x](etc/NEWS.5) +- [Octave 4.x](etc/NEWS.4) +- [Octave 3.x](etc/NEWS.3) - [Octave 2.x](etc/NEWS.2) - [Octave 1.x](etc/NEWS.1) diff -r 940c1b6e3453 -r 61701d1317a1 build-aux/subst-config-vals.in.sh --- a/build-aux/subst-config-vals.in.sh Wed Jul 10 20:02:44 2019 -0700 +++ b/build-aux/subst-config-vals.in.sh Fri Jul 12 12:14:43 2019 -0400 @@ -211,6 +211,9 @@ SUNDIALS_NVECSERIAL_CPPFLAGS="@SUNDIALS_NVECSERIAL_CPPFLAGS@" SUNDIALS_NVECSERIAL_LDFLAGS="@SUNDIALS_NVECSERIAL_LDFLAGS@" SUNDIALS_NVECSERIAL_LIBS="@SUNDIALS_NVECSERIAL_LIBS@" +SUNDIALS_SUNLINSOLKLU_CPPFLAGS="@SUNDIALS_SUNLINSOLKLU_CPPFLAGS@" +SUNDIALS_SUNLINSOLKLU_LDFLAGS="@SUNDIALS_SUNLINSOLKLU_LDFLAGS@" +SUNDIALS_SUNLINSOLKLU_LIBS="@SUNDIALS_SUNLINSOLKLU_LIBS@" TERM_LIBS="@TERM_LIBS@" UMFPACK_CPPFLAGS="@UMFPACK_CPPFLAGS@" UMFPACK_LDFLAGS="@UMFPACK_LDFLAGS@" @@ -380,6 +383,9 @@ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_CPPFLAGS%|\"${SUNDIALS_NVECSERIAL_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_LDFLAGS%|\"${SUNDIALS_NVECSERIAL_LDFLAGS}\"|" \ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_LIBS%|\"${SUNDIALS_NVECSERIAL_LIBS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_CPPFLAGS%|\"${SUNDIALS_SUNLINSOLKLU_CPPFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LDFLAGS%|\"${SUNDIALS_SUNLINSOLKLU_LDFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LIBS%|\"${SUNDIALS_SUNLINSOLKLU_LIBS}\"|" \ -e "s|%OCTAVE_CONF_TERM_LIBS%|\"${TERM_LIBS}\"|" \ -e "s|%OCTAVE_CONF_UMFPACK_CPPFLAGS%|\"${UMFPACK_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_UMFPACK_LDFLAGS%|\"${UMFPACK_LDFLAGS}\"|" \ diff -r 940c1b6e3453 -r 61701d1317a1 build-aux/subst-cross-config-vals.in.sh --- a/build-aux/subst-cross-config-vals.in.sh Wed Jul 10 20:02:44 2019 -0700 +++ b/build-aux/subst-cross-config-vals.in.sh Fri Jul 12 12:14:43 2019 -0400 @@ -215,6 +215,9 @@ SUNDIALS_NVECSERIAL_CPPFLAGS="@SUNDIALS_NVECSERIAL_CPPFLAGS@" SUNDIALS_NVECSERIAL_LDFLAGS="@SUNDIALS_NVECSERIAL_LDFLAGS@" SUNDIALS_NVECSERIAL_LIBS="@SUNDIALS_NVECSERIAL_LIBS@" +SUNDIALS_SUNLINSOLKLU_CPPFLAGS="@SUNDIALS_SUNLINSOLKLU_CPPFLAGS@" +SUNDIALS_SUNLINSOLKLU_LDFLAGS="@SUNDIALS_SUNLINSOLKLU_LDFLAGS@" +SUNDIALS_SUNLINSOLKLU_LIBS="@SUNDIALS_SUNLINSOLKLU_LIBS@" TERM_LIBS="@TERM_LIBS@" UMFPACK_CPPFLAGS="@UMFPACK_CPPFLAGS@" UMFPACK_LDFLAGS="@UMFPACK_LDFLAGS@" @@ -384,6 +387,9 @@ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_CPPFLAGS%|\"${SUNDIALS_NVECSERIAL_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_LDFLAGS%|\"${SUNDIALS_NVECSERIAL_LDFLAGS}\"|" \ -e "s|%OCTAVE_CONF_SUNDIALS_NVECSERIAL_LIBS%|\"${SUNDIALS_NVECSERIAL_LIBS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_CPPFLAGS%|\"${SUNDIALS_SUNLINSOLKLU_CPPFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LDFLAGS%|\"${SUNDIALS_SUNLINSOLKLU_LDFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LIBS%|\"${SUNDIALS_SUNLINSOLKLU_LIBS}\"|" \ -e "s|%OCTAVE_CONF_TERM_LIBS%|\"${TERM_LIBS}\"|" \ -e "s|%OCTAVE_CONF_UMFPACK_CPPFLAGS%|\"${UMFPACK_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_UMFPACK_LDFLAGS%|\"${UMFPACK_LDFLAGS}\"|" \ diff -r 940c1b6e3453 -r 61701d1317a1 build-aux/update-bug-status.sh --- a/build-aux/update-bug-status.sh Wed Jul 10 20:02:44 2019 -0700 +++ b/build-aux/update-bug-status.sh Fri Jul 12 12:14:43 2019 -0400 @@ -23,7 +23,7 @@ done | sort -u) fixed_bug_numbers=$(for num in $bug_numbers; do - status=$(wget -q -O - https://octave.org/testfailure/?$num | sed -n 's/.*>Status:<\/span><\/span> <\/td>\([^<]*\)<.*/\1/p'); + status=$(wget -q -O - https://octave.org/testfailure/?$num | tr -d '\n' | sed -n 's/.*>Status:<\/span><\/span> <\/td> *\([^<]*\)<.*/\1/p'); if [ "$status" = "Fixed" ]; then echo "$num"; fi done) diff -r 940c1b6e3453 -r 61701d1317a1 configure.ac --- a/configure.ac Wed Jul 10 20:02:44 2019 -0700 +++ b/configure.ac Fri Jul 12 12:14:43 2019 -0400 @@ -20,7 +20,7 @@ ### Initialize Autoconf AC_PREREQ([2.65]) -AC_INIT([GNU Octave], [5.1.1], [https://octave.org/bugs.html], [octave], +AC_INIT([GNU Octave], [6.0.0], [https://octave.org/bugs.html], [octave], [https://www.gnu.org/software/octave/]) ### Declare version numbers @@ -32,9 +32,9 @@ ## explains how to update these numbers for release and development ## versions. -OCTAVE_MAJOR_VERSION=5 -OCTAVE_MINOR_VERSION=1 -OCTAVE_PATCH_VERSION=1 +OCTAVE_MAJOR_VERSION=6 +OCTAVE_MINOR_VERSION=0 +OCTAVE_PATCH_VERSION=0 dnl PACKAGE_VERSION is set by the AC_INIT VERSION argument. OCTAVE_VERSION="$PACKAGE_VERSION" @@ -56,7 +56,7 @@ dnl FIXME: Since we also set libtool versions for liboctave and libinterp, dnl perhaps we should be computing the "api version" from those versions numbers dnl in some way instead of setting it independently here. -OCTAVE_API_VERSION="api-v53" +OCTAVE_API_VERSION="api-v53+" AC_SUBST(OCTAVE_MAJOR_VERSION) AC_SUBST(OCTAVE_MINOR_VERSION) @@ -850,9 +850,7 @@ RDYNAMIC_FLAG= DL_API_MSG="" dlopen_api=no -shl_load_api=no loadlibrary_api=no -dyld_api=no case $lt_cv_dlopen in dlopen) @@ -862,24 +860,12 @@ [Define to 1 if system has dlopen, dlsym, dlerror, and dlclose for dynamic linking.]) OCTAVE_CXX_FLAG([-rdynamic], [RDYNAMIC_FLAG=-rdynamic]) ;; - shl_load) - shl_load_api=yes - DL_API_MSG="shl_load" - AC_DEFINE(HAVE_SHL_LOAD_API, 1, - [Define to 1 if system has shl_load and shl_findsym for dynamic linking.]) - ;; LoadLibrary) loadlibrary_api=yes DL_API_MSG="LoadLibrary" AC_DEFINE(HAVE_LOADLIBRARY_API, 1, [Define to 1 if system has LoadLibrary for dynamic linking.]) ;; - dyld) - dyld_api=yes - DL_API_MSG="dyld" - AC_DEFINE(HAVE_DYLD_API, 1, - [Define to 1 if system has dyld for dynamic linking.]) - ;; *) AC_MSG_ERROR([Octave requires some way to perform dynamic linking.]) ;; @@ -2206,46 +2192,85 @@ ### Check for SUNDIALS NVECTOR serial library and header. OCTAVE_CHECK_LIB(sundials_nvecserial, [SUNDIALS NVECTOR], - [SUNDIALS NVECTOR serial library not found. Solvers ode15i and ode15s will be disabled.], + [SUNDIALS NVECTOR serial library not found. The solvers ode15i and ode15s will be disabled.], [nvector/nvector_serial.h nvector_serial.h ], [N_VNew_Serial], - [], [don't use SUNDIALS NVECTOR library, solvers ode15i and ode15s will be disabled]) + [], [don't use SUNDIALS NVECTOR library, disable solvers ode15i and ode15s]) ### Check for SUNDIALS IDA library and header. save_LIBS="$LIBS" LIBS="$SUNDIALS_NVECSERIAL_LIBS $KLU_LIBS $BLAS_LIBS $FLIBS $LIBS" OCTAVE_CHECK_LIB(sundials_ida, [SUNDIALS IDA], - [SUNDIALS IDA library not found. Solvers ode15i and ode15s will be disabled.], + [SUNDIALS IDA library not found. The solvers ode15i and ode15s will be disabled.], [ida/ida.h ida.h], [IDAInit], - [], [don't use SUNDIALS IDA library, solvers ode15i and ode15s will be disabled], - [warn_sundials_ida= - OCTAVE_CHECK_SUNDIALS_SIZEOF_REALTYPE - OCTAVE_CHECK_SUNDIALS_IDA_DENSE - OCTAVE_CHECK_SUNDIALS_IDAKLU]) + [], [don't use SUNDIALS IDA library, disable solvers ode15i and ode15s]) +LIBS="$save_LIBS" + +### Check for SUNDIALS library features, some required, some optional. + +LIBS="$SUNDIALS_IDA_LIBS $SUNDIALS_NVECSERIAL_LIBS $KLU_LIBS $BLAS_LIBS $FLIBS $LIBS" +if test -z "$warn_sundials_nvecserial" && test -z "$warn_sundials_ida"; then + dnl Any of the following tests could determine that SUNDIALS is incompatible + dnl and should be disabled. In that event, they all populate the same + dnl variable with appropriate warning messages, and further tests should be + dnl skipped if a warning message has already been generated that SUNDIALS is + dnl disabled. + warn_sundials_disabled= + if test -z "$warn_sundials_disabled"; then + OCTAVE_CHECK_SUNDIALS_COMPATIBLE_API + fi + if test -z "$warn_sundials_disabled"; then + OCTAVE_CHECK_SUNDIALS_SIZEOF_REALTYPE + fi + if test -z "$warn_sundials_disabled"; then + OCTAVE_CHECK_SUNDIALS_SUNLINSOL_DENSE + fi + dnl The following tests determine whether certain optional features are + dnl present in the SUNDIALS libraries, but will not disable using SUNDIALS. + if test -z "$warn_sundials_disabled"; then + OCTAVE_CHECK_SUNDIALS_SUNLINSOL_KLU + fi +fi LIBS="$save_LIBS" dnl Define this way instead of with an #if in oct-conf-post.h so that dnl the build features script will get the correct value. +dnl +dnl The test on the sunlinsol_klu.h header is a bit of a kluge. +dnl How can we do a better job here? Do we need to disable sundials +dnl any tests fail, or can we fix __ode15__.cc so that it still partially +dnl works when some things are missing (for example, KLU)? if test -n "$SUNDIALS_IDA_LIBS" \ && test -n "$SUNDIALS_NVECSERIAL_LIBS" \ - && test $octave_cv_sundials_ida_dense = yes \ - && test $octave_cv_sundials_realtype_is_double = yes; then + && test "x$octave_cv_sundials_sunlinsol_dense" = xyes \ + && test "x$octave_cv_sundials_realtype_is_double" = xyes \ + && test "x$octave_have_sundials_compatible_api" = xyes; then AC_DEFINE(HAVE_SUNDIALS, 1, [Define to 1 if SUNDIALS is available.]) ## Collections of options needed to build with SUNDIALS and its dependencies. - SUNDIALS_XCPPFLAGS="$SUNDIALS_IDA_CPPFLAGS $SUNDIALS_NVECSERIAL_CPPFLAGS $KLU_CPPFLAGS" - SUNDIALS_XLDFLAGS="$SUNDIALS_IDA_LDFLAGS $SUNDIALS_NVECSERIAL_LDFLAGS $KLU_LDFLAGS" - SUNDIALS_XLIBS="$SUNDIALS_IDA_LIBS $SUNDIALS_NVECSERIAL_LIBS $KLU_LIBS" + SUNDIALS_XCPPFLAGS="$SUNDIALS_IDA_CPPFLAGS $SUNDIALS_SUNLINSOLKLU_CPPFLAGS $SUNDIALS_NVECSERIAL_CPPFLAGS $KLU_CPPFLAGS" + SUNDIALS_XLDFLAGS="$SUNDIALS_IDA_LDFLAGS $SUNDIALS_SUNLINSOLKLU_LDFLAGS $SUNDIALS_NVECSERIAL_LDFLAGS $KLU_LDFLAGS" + SUNDIALS_XLIBS="$SUNDIALS_IDA_LIBS $SUNDIALS_SUNLINSOLKLU_LIBS $SUNDIALS_NVECSERIAL_LIBS $KLU_LIBS" else SUNDIALS_IDA_CPPFLAGS= SUNDIALS_IDA_LDFLAGS= SUNDIALS_IDA_LIBS= + SUNDIALS_SUNLINSOLKLU_CPPFLAGS= + SUNDIALS_SUNLINSOLKLU_LDFLAGS= + SUNDIALS_SUNLINSOLKLU_LIBS= SUNDIALS_NVECSERIAL_CPPFLAGS= SUNDIALS_NVECSERIAL_LDFLAGS= SUNDIALS_NVECSERIAL_LIBS= SUNDIALS_XCPPFLAGS= SUNDIALS_XLDFLAGS= SUNDIALS_XLIBS= + dnl Emit a fallback warning message in case SUNDIALS has been disabled for + dnl some reason that hasn't already generated one of these known warnings. + if test -z "$warn_sundials_nvecserial" && test -z "$warn_sundials_ida" \ + && test -z "$warn_sundials_disabled"; then + warn_sundials_disabled="SUNDIALS libraries are missing some feature. The solvers ode15i and ode15s will be disabled." + OCTAVE_CONFIGURE_WARNING([warn_sundials_disabled]) + fi fi AC_SUBST(SUNDIALS_XCPPFLAGS) @@ -2594,7 +2619,7 @@ esac GCC_EXTRA_FLAGS="-Wall -W $GCC_WSHADOW_OPTION -Wformat -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings -Wcast-align -Wcast-qual" -GXX_EXTRA_FLAGS="-Wall -W $GCC_WSHADOW_OPTION -Wold-style-cast -Wformat -Wpointer-arith -Wwrite-strings -Wcast-align -Wcast-qual" +GXX_EXTRA_FLAGS="-Wall -W $GCC_WSHADOW_OPTION -Woverloaded-virtual -Wold-style-cast -Wformat -Wpointer-arith -Wwrite-strings -Wcast-align -Wcast-qual" try_extra_warning_flags=yes AC_ARG_ENABLE([extra-warning-flags], @@ -3066,6 +3091,9 @@ SUNDIALS NVECTOR CPPFLAGS: $SUNDIALS_NVECSERIAL_CPPFLAGS SUNDIALS NVECTOR LDFLAGS: $SUNDIALS_NVECSERIAL_LDFLAGS SUNDIALS NVECTOR libraries: $SUNDIALS_NVECSERIAL_LIBS + SUNLINSOL KLU CPPFLAGS: $SUNDIALS_SUNLINSOLKLU_CPPFLAGS + SUNLINSOL KLU LDFLAGS: $SUNDIALS_SUNLINSOLKLU_LDFLAGS + SUNLINSOL KLU libraries: $SUNDIALS_SUNLINSOLKLU_LIBS TERM libraries: $TERM_LIBS UMFPACK CPPFLAGS: $UMFPACK_CPPFLAGS UMFPACK LDFLAGS: $UMFPACK_LDFLAGS diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/TODO --- a/doc/interpreter/TODO Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/TODO Fri Jul 12 12:14:43 2019 -0400 @@ -29,7 +29,7 @@ (???) * If Octave crashes, it now attempts to save all user-defined - variables in a file named `octave-core' in the current directory + variables in a file named 'octave-core' in the current directory before exiting. (???) @@ -39,7 +39,7 @@ that are not yet available in the public release of Info. (install) - * If it is present, Octave will now use an `ls-R' database file to + * If it is present, Octave will now use an 'ls-R' database file to speed up recursive path searching. Octave looks for a file called ls-R in the directory specified by the environment variable OCTAVE_DB_DIR. If that is not set but the environment variable diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/contributors.in --- a/doc/interpreter/contributors.in Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/contributors.in Fri Jul 12 12:14:43 2019 -0400 @@ -43,6 +43,7 @@ Remy Bruno Clemens Buchacher Ansgar Burchard +Antonius Burgers Marco Caliari Daniel Calvelo John C. Campbell @@ -430,6 +431,7 @@ Alexander Wilms Joe Winegarden Georg Wiora +Eddy Xiao Sahil Yadav Fook Fah Yap Sean Young diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/genpropdoc.m --- a/doc/interpreter/genpropdoc.m Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/genpropdoc.m Fri Jul 12 12:14:43 2019 -0400 @@ -503,11 +503,24 @@ @code{screenpixelsperinch} property of the root object."; case "pointer" - s.doc = doc_unused; + s.doc = "Name of the mouse pointer shape associated with the canvas \ +of the figure. When __prop__ is \"custom\", the shape is determined by \ +the @code{pointershapecdata} property.\n\n\ +__prop__ has no effect when the figure is in zoom, pan, or rotate mode. \ +In this case, Octave automatically uses a pointer shape appropriate \ +to the mode."; + case "pointershapecdata" - s.doc = doc_unused; + s.doc ="m-by-m matrix defining a custom pointer. Each \ +element defines a pixel with the element (1,1) representing the \ +top-left pixel. A value of 1 is colored black, a value of 2 is colored white, \ +and all other values are rendered as transparent."; + s.valid = "16-by-16 or 32-by-32 Matrix"; + case "pointershapehotspot" - s.doc = doc_unused; + s.doc ="For custom pointers only __prop__ defines the row and column \ +of the pixel in @code{pointershapecdata} that is used as the pointer location."; + s.valid = valid_2elvec; case "position" s.doc = "Specify the position and size of the figure canvas. \ @@ -536,7 +549,19 @@ s.valid = valid_fcn; case "selectiontype" - ## FIXME: docstring explaining what "{normal}|open|alt|extend" mean. + s.doc = "Selection type of the latest mouse click.\n\n\ +__prop__ may take different values depending on the combination of mouse \ +button and keyboard modifier that were used:\n\ +@table @code\n\ +@item normal:\n\ +Left-click.\n\ +@item alt:\n\ +Right-click or Ctrl+Left-click.\n\ +@item extend:\n\ +Shitf+Left-click, Middle click, or combined Left-click and Right-click.\n\ +@item open:\n\ +Double Left-click.\n\ +@end table"; case "sizechangedfcn" s.doc = "Callback triggered when the figure window size is changed.\ @@ -581,7 +606,21 @@ s.valid = valid_fcn; case "windowscrollwheelfcn" - s.doc = doc_unused; + s.doc = "Function that is executed when a user manipulates \ +the mouse wheel over this figure. \ +The function is called with two input arguments. The first \ +argument holds the handle of the calling figure. The second argument holds \ +an event structure which has the following members:\n\ +@table @code\n\ +@item VerticalScrollCount:\n\ +The number of wheel steps, typically 1 when scrolling down and -1 when \ +scrolling up.\n\ +@item VerticalScrollAmount:\n\ +The number of lines a wheel step should scroll. This value is always 3.\n\ +@item EventName:\n\ +The event name which is \"WindowScrollWheel\".\n\ +@end table\ +\n\n__fcnmsg__"; s.valid = valid_fcn; case "windowstyle" @@ -696,12 +735,12 @@ s.doc = doc_fontsize; s.valid = "scalar"; + case "fontsmoothing" + s.doc = "Control whether any text associated with __objname__ is anti-aliased."; + case "fontunits" s.doc = doc_fontunits; - case "fontsmoothing" - s.doc = doc_unused; - case "fontweight" s.doc = doc_fontweight; @@ -1082,6 +1121,9 @@ s.doc = doc_fontname; s.valid = valid_string; + case "fontsmoothing" + s.doc = "Control whether anti-aliasing is used when rendering text."; + case "fontsize" s.doc = doc_fontsize; s.valid = "scalar"; diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/macros.texi --- a/doc/interpreter/macros.texi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/macros.texi Fri Jul 12 12:14:43 2019 -0400 @@ -46,9 +46,14 @@ @c Implementation Note: @c For TeX, @vskip produces a nice separation. @c For Texinfo, '@sp 1' should work, but in practice produces ugly results -@c for HTML. We use a simple blank line to produce the correct behavior. +@c for HTML. We use a simple blank line to produce the correct +@c behavior. +@c +@c We use @xseealso now because Texinfo introduced its own @seealso +@c command. But instead of modifying all source files, we'll have the +@c munge-texi script convert @seealso to @xseealso. -@macro seealso {args} +@macro xseealso {args} @iftex @vskip 2pt @end iftex diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/mk-doc-cache.pl --- a/doc/interpreter/mk-doc-cache.pl Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/mk-doc-cache.pl Fri Jul 12 12:14:43 2019 -0400 @@ -71,6 +71,8 @@ next if $in_header; next if $line =~ /$tex_delim_pat/; + $line =~ s/\@seealso/\@xseealso/g; + ## escape {}@ characters for texinfo $line =~ s/([{}\@])/\@$1/g if $line =~ m/^$doc_delim/; diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/munge-texi.pl --- a/doc/interpreter/munge-texi.pl Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/munge-texi.pl Fri Jul 12 12:14:43 2019 -0400 @@ -73,6 +73,8 @@ TXI_LINE: while () { + s/\@seealso/\@xseealso/g; + if (/^\s*\@DOCSTRING\((\S+)\)/) { $func = $1; @@ -139,7 +141,9 @@ $repl .= "\@ref{XREF$node,,$func}, "; } substr($repl,-2) = ""; # Remove last ', ' - $_ = "\@seealso{$repl}$rest_of_line"; + # write out @xseealso because we have our own macro that conflicts + # with the one introduced in Texinfo 6. + $_ = "\@xseealso{$repl}$rest_of_line"; } $docstring .= $_; diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/oop.txi --- a/doc/interpreter/oop.txi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/oop.txi Fri Jul 12 12:14:43 2019 -0400 @@ -900,6 +900,8 @@ By default, in the example @var{prop4}, properties are not constant and have public read and write access. +@DOCSTRING(properties) + @node Methods @subsection Methods diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/plot.txi --- a/doc/interpreter/plot.txi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/plot.txi Fri Jul 12 12:14:43 2019 -0400 @@ -459,6 +459,8 @@ @DOCSTRING(camlight) +@DOCSTRING(lightangle) + @DOCSTRING(meshgrid) @DOCSTRING(ndgrid) diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/strings.txi --- a/doc/interpreter/strings.txi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/strings.txi Fri Jul 12 12:14:43 2019 -0400 @@ -248,7 +248,12 @@ the overhead of a function call and the input validation of the associated function. -Nevertheless, there are several other functions for concatenating string +The @code{newline} function can be used to join strings such that they appear +as multiple lines of text when displayed. + +@DOCSTRING(newline) + +In addition, there are several other functions for concatenating string objects which can be useful in specific circumstances: @code{char}, @code{strvcat}, @code{strcat}, and @code{cstrcat}. Finally, the general purpose concatenation functions can be used: see @ref{XREFcat,,cat}, diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/system.txi --- a/doc/interpreter/system.txi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/system.txi Fri Jul 12 12:14:43 2019 -0400 @@ -533,6 +533,8 @@ @DOCSTRING(compare_versions) +@DOCSTRING(verLessThan) + @DOCSTRING(license) @DOCSTRING(getrusage) diff -r 940c1b6e3453 -r 61701d1317a1 doc/interpreter/testfun.txi --- a/doc/interpreter/testfun.txi Wed Jul 10 20:02:44 2019 -0700 +++ b/doc/interpreter/testfun.txi Fri Jul 12 12:14:43 2019 -0400 @@ -470,8 +470,8 @@ @DOCSTRING(example) +@DOCSTRING(oruntests) + @DOCSTRING(rundemos) -@DOCSTRING(runtests) - @DOCSTRING(speed) diff -r 940c1b6e3453 -r 61701d1317a1 etc/HACKING.md --- a/etc/HACKING.md Wed Jul 10 20:02:44 2019 -0700 +++ b/etc/HACKING.md Fri Jul 12 12:14:43 2019 -0400 @@ -343,7 +343,7 @@ The rules for updating these version numbers are: - * Start with version information of ‘0:0:0’ for each libtool library. + * Start with version information of `0:0:0` for each libtool library. * Update the version information only immediately before a public release of your software. More frequent updates are unnecessary, @@ -351,7 +351,7 @@ faster. * If the library source code has changed at all since the last update, - then increment revision (‘c:r:a’ becomes ‘c:r+1:a’). + then increment revision (`c:r:a` becomes `c:r+1:a`). * If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0. diff -r 940c1b6e3453 -r 61701d1317a1 etc/NEWS.5 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etc/NEWS.5 Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,324 @@ +Summary of important user-visible changes for version 5 (2019-02-23): +-------------------------------------------------------------------- + +### General improvements + +- The Octave plotting system now supports high resolution screens, + i.e., those with greater than 96 DPI which are referred to as + HiDPI/Retina monitors. + +- Unicode character support for files and folders in Windows. + +- A new core function `movfun` will apply a function to a sliding + window of arbitrary size on a dataset and accumulate the results. + Many common cases have been implemented using the naming + scheme `movXXX` where `XXX` is the function that will be applied. + For example, the moving average over a dataset is `movmean`. + New moving window functions: + + `movfun` `movslice` + `movmad` `movmax` `movmean` `movmedian` `movmin` `movprod` + `movstd` `movsum` `movvar` + +- The `fsolve` function has been tweaked to use larger step sizes when + calculating the Jacobian of a function with finite differences. + This leads to faster convergence. + +- The `ranks` function has been recoded for performance and is now 25X + faster. In addition, it now supports a third argument that specifies + how to resolve the ranking of tie values. + +- The function `randi` has been recoded to produce an unbiased (all + results are equally likely) sample of integers. This may produce + different results in existing code. If it is necessary to reproduce + the exact random integer sequence as in previous versions use + + `ri = imin + floor ((imax - imin + 1) * rand ());` + +- The function `isdefinite` now returns `true` or `false` rather than + `-1`, `0`, or `1`. To test for a positive semi-definite matrix (old + output of `0`) check whether the following two conditions hold: + + `isdefinite (A) => 0` and `isdefinite (A + 5*TOL, TOL) => 1` + +- The `intmax`, `intmin`, and `flintmax` functions now accept a variable + as input. Existing code to query the range of an existing variable can + be simplified by removing the call to `class` that was previously + required. For example defining the variable `x = int8 (3)` in the + workspace, calls like + + `range = [ intmin(class(x)), intmax(class(x)) ]` + + can in Octave 5 be simplified to `range = [ intmin(x), intmax(x) ]`. + +- The path handling functions no longer perform variable or brace + expansion on path elements and Octave's load-path is no longer + subject to these expansions. + +- A new printing device is available, `"-ddumb"`, which produces ASCII + art for plots. This device is only available with the gnuplot toolkit. + +- The `msgbox` function has changed in two respects: the default WindowStyle + is now `"non-modal"`, and the default interpreter for the message is now + `"tex"`. Both WindowStyle and Interpreter can be controlled by passing an + option struct argument. + +### Dependencies + +- The GUI requires Qt libraries. The minimum Qt4 version supported is + Qt4.8. Qt5 of any version is preferred. + +- The OSMesa library is no longer used. To print invisible figures + when using OpenGL graphics, the Qt `QOFFSCREENSURFACE` feature must be + available and you must use the qt graphics toolkit. + +- The FFTW library is now required to perform FFT calculations. + The FFTPACK sources have been removed from Octave. + + +### Matlab compatibility + +- The determination of an object's dimensions, size, and shape by the + functions `ndims`, `rows`, `columns`, `isscalar`, `isvector`, + `isrow`, `iscolumn`, `ismatrix`, and `issquare` now fully depends + on the function size. Thus, any user-defined object can ensure correct + treatment by the aforementioned functions by properly overloading the + `size` function. + +- The functions `issymmetric` and `ishermitian` accept an option + `"nonskew"` or `"skew"` to calculate the symmetric or skew-symmetric + property of a matrix. Performance has also been increased. + +- The `issorted` function now uses a direction option of `"ascend"` + or `"descend"`. Change all uses of `"ascending"` and `"descending"` + in existing code to the new options. + +- The `strncmp` and `strncmpi` functions now return `true` if the two + input strings match, even though the number of characters specified + by `n` exceeds the string length. For Example: + + `strncmp ("abc", "abc", 100)` + + returns `true` in Octave 5 and `false` in older versions of Octave. + +- The `str2func` function no longer accepts a second `"global"` argument. + This argument was typically used to allow functions that accept + function names as arguments to avoid conflicts with subfunctions or + nested functions. Instead, it's best to avoid this situation + entirely and require users to pass function handles rather than + function names. + +- Using `clear` with no arguments now removes only local variables + from the current workspace. Global variables will no longer be + visible, but they continue to exist in the global workspace and + possibly other workspaces such as the base workspace. + + +#### Nonlinear Equations + +Several default solver options have been changed to be Matlab compatible. +This *may* result in existing code producing different results. + +- `fsolve` + + Option | New Default | Old Default + ---------------|------------------|------------- + `FinDiffType` | `"forward"` | `"central"` + `MaxFunEvals` | `100*length(x0)` | `Inf` + `TolFun` | `1e-6` | `1e-7` + `TolX` | `1e-6` | `1e-7` + `Updating` | `"off"` | `"on"` + +- `fminsearch` + + Option | New Default | Old Default + ---------|-------------|------------ + `TolFun` | `1e-7` | `1e-4` + +- `fminbnd` + + Option | New Default | Old Default + ---------------|-------------|------------ + `MaxFunEvals` | `500` | `Inf` + `MaxIter` | `500` | `Inf` + `TolX` | `1e-4` | `1e-8` + +- `fminunc` + + Option | New Default | Old Default + ---------------|------------------|------------ + `FinDiffType` | `"forward"` | `"central"` + `MaxFunEvals` | `100*length(x0)` | `Inf` + `TolX` | `1e-6` | `1e-7` + `TolFun` | `1e-6` | `1e-7` + + +#### Graphic objects + +- Figure graphic objects have a new property `"Number"` which is + read-only and will return the handle (number) of the figure. + However, if the property `"IntegerHandle"` has been set to `"off"` + then the property will return an empty matrix `[]`. + +- Patch and surface graphic objects now use the `"FaceNormals"` property + for flat lighting. + +- `"FaceNormals"` and `"VertexNormals"` for patch and surface graphic + objects are now calculated only when necessary to improve graphics + performance. In order for any normals to be calculated the + `"FaceLighting"` property must be set to `"flat"` (FaceNormals) or + `"gouraud"` (VertexNormals), **and** a light object must be present + in the axes. + +- The `"Margin"` property of `text`-objects has a new default of `3` + rather than `2`. + +- Printing to raster formats (bitmaps like PNG or JPEG) now uses an + OpenGL-based method by default. The print options `"-opengl"` + (raster) and `"-painters"` (vector) have been added ("qt" toolkit + only). The figure property `"renderer"` specifies which renderer to + use. When the property `"renderermode"` is `"auto"` Octave will select + `"-opengl"` for a raster output format and `"-painters"` for a vector + output format. + +- A new print option `"-RGBImage"` has been added which captures the + pixels of a figure as an image. This is similar to screen capture + tools, except that print formatting options can be used to, for + example, change the resolution or display the image in black and + white. + +- Two new print options for page-based formats (PDF, PostScript) have + been added. The `"-fillpage"` option will stretch the plot to occupy + the entire page with 0.25 inch margins all around. The `"-bestfit"` + option will expand the plot to take up as much room as possible on + the page without distorting the original aspect ratio of the plot. + +- Printing using the `"-dtiff"` output device will now create compressed + images using LZW compression. To produce uncompressed images use the + `"-dtiffn"` device. + + +### Legacy functions + +The following functions have been declared legacy functions which +means they are obsolete and should not be used in any new code. +Unlike deprecated functions, however, their removal from Octave has +not yet been scheduled. + + Function | Replacement + -----------------------|------------------ + `findstr` | `strfind` + `flipdim` | `flip` + `isdir` | `isfolder` or `dir_in_loadpath` + `isequalwithequalnans` | `isequaln` + `isstr` | `ischar` + `setstr` | `char` + `strmatch` | `strncmp` or `strcmp` + `strread` | `textscan` + `textread` | `textscan` + + +### Deprecated functions and properties + +The following functions and graphics properties have been deprecated +in Octave 5 and will be removed from Octave 7 (or whatever version +is the second major release after 5): + +- Functions + + Function | Replacement + -------------------------|------------------- + `output_max_field_width` | `output_precision` + `is_keyword` | `iskeyword` + +- Graphics properties + + Object | Property | Value + -----------------|---------------|------------ + `text` | `fontangle` | `"oblique"` + `uibuttongroup` | `fontangle` | `"oblique"` + `uicontrol` | `fontangle` | `"oblique"` + `uipanel` | `fontangle` | `"oblique"` + `uitable` | `fontangle` | `"oblique"` + +- Specifying `legend` position with a numeric argument is deprecated. + Use a string argument instead. + +- The environment variable used by `mkoctfile` for linker flags is now + `LDFLAGS` rather than `LFLAGS`. `LFLAGS` is deprecated, and a warning + is emitted if is used, but it will continue to work. + + +### Removed functions and properties + +The following functions and properties were deprecated in Octave 4.2 +and have been removed from Octave 5. + +- Functions + + Function | Replacement + -----------------------|------------------ + `bitmax` | `flintmax` + `mahalanobis` | `mahal` in Octave Forge statistics pkg + `md5sum` | `hash` + `octave_config_info` | `__octave_config_info__` + `onenormest` | `normest1` + `sleep` | `pause` + `usleep` | `pause` + `wavread` | `audioread` + `wavwrite` | `audiowrite` + +- Properties + + Object | Property | Value + ------------|-------------------|--------- + `axes` | `xaxislocation` | `"zero"` + | `yaxislocation` | `"zero"` + `hggroup` | `erasemode` | + `image` | `erasemode` | + `line` | `erasemode` | + `patch` | `erasemode` | + `patch` | `normalmode` | + `surface` | `erasemode` | + `surface` | `normalmode` | + `text` | `erasemode` | + + +### Alphabetical list of new functions added in 5 + +- `clearvars` +- `isfile` +- `isfolder` +- `matlab.lang.makeUniqueStrings` +- `matlab.lang.makeValidName` +- `movegui` +- `movfun` +- `movie` +- `movmad` +- `movmax` +- `movmean` +- `movmedian` +- `movmin` +- `movprod` +- `movslice` +- `movstd` +- `movsum` +- `movvar` +- `openfig` +- `ordeig` +- `savefig` +- `uitable` + + +### Old release news + +- [Octave 4.4.x](etc/NEWS.4) +- [Octave 4.2.x](etc/NEWS.4) +- [Octave 4.0.x](etc/NEWS.4) +- [Octave 3.8.x](etc/NEWS.3) +- [Octave 3.6.x](etc/NEWS.3) +- [Octave 3.4.x](etc/NEWS.3) +- [Octave 3.2.x](etc/NEWS.3) +- [Octave 3.0.x](etc/NEWS.3) +- [Octave 2.x](etc/NEWS.2) +- [Octave 1.x](etc/NEWS.1) diff -r 940c1b6e3453 -r 61701d1317a1 etc/RELEASE.BUG_FIX_LIST --- a/etc/RELEASE.BUG_FIX_LIST Wed Jul 10 20:02:44 2019 -0700 +++ b/etc/RELEASE.BUG_FIX_LIST Fri Jul 12 12:14:43 2019 -0400 @@ -1,4 +1,4 @@ -The following bugs must be fixed prior to the next release. See also the [[5.0.0 Release Checklist]]. +The following bugs must be fixed prior to the next release. See also the [[6.0.0 Release Checklist]].

Sign up for a bug by filling in the Owner field so people won't duplicate each other's work.

@@ -21,4 +21,4 @@ == Other Bugs == == Potentially excluded bugs == -These bugs have been provisionally decided not to be blockers for 5.0.0. That doesn't mean they can't be fixed, if someone wants to fix them. +These bugs have been provisionally decided not to be blockers for 6.0.0. That doesn't mean they can't be fixed, if someone wants to fix them. diff -r 940c1b6e3453 -r 61701d1317a1 etc/RELEASE.CHECKLIST --- a/etc/RELEASE.CHECKLIST Wed Jul 10 20:02:44 2019 -0700 +++ b/etc/RELEASE.CHECKLIST Fri Jul 12 12:14:43 2019 -0400 @@ -3,7 +3,7 @@ The format of the file is wiki markup and can be directly used as a page on wiki.octave.org. > -==5.0.0 Release Tasks== +==6.0.0 Release Tasks== # Update gnulib to latest version @@ -23,7 +23,7 @@ #: Completion Date: # Clear all bugs identified as must-fix -#* See [[Bug Fix List - 5.0 Release]] +#* See [[Bug Fix List - 6.0 Release]] #: Completion Date: # GPL License activities @@ -67,6 +67,11 @@ #* Submit call for translations for GUI strings #: Completion Date: +# Update shared library and oct file API version numbers +#* Increment oct file API version number (configure.ac:OCTAVE_API_VERSION, increment number and drop "+" suffix) +#* Increment libtool versioning (liboctave/module.mk:%canon_reldir%_%canon_reldir%_current, libinterp/module.mk:%canon_reldir%_liboctinterp_current, libgui/module.mk:%canon_reldir%_liboctgui_current) +#: Completion Date: + # Verify build process and create release candidates #* Update configure.ac with new version information #** Update AC_INIT, OCTAVE_MAJOR_VERSION, OCTAVE_MINOR_VERSION, OCTAVE_PATCH_VERSION, OCTAVE_RELEASE_DATE @@ -84,7 +89,6 @@ # Final Release #* Update version information #** Update configure.ac (AC_INIT, OCTAVE_MAJOR_VERSION, OCTAVE_MINOR_VERSION, OCTAVE_PATCH_VERSION, OCTAVE_RELEASE_DATE) -#** Update libtool versioning (configure.ac:OCTAVE_API_VERSION, liboctave/module.mk:%canon_reldir%_%canon_reldir%_current, libinterp/module.mk:%canon_reldir%_liboctinterp_current, libgui/module.mk:%canon_reldir%_liboctgui_current) #** Update NEWS (final release date) #** Update CITATION (version, year, URL) #** Update org.octave.Octave.appdata.xml (version number and release date) @@ -98,6 +102,7 @@ # Post-Release #* Update configure.ac (AC_INIT, OCTAVE_MAJOR_VERSION, OCTAVE_MINOR_VERSION, OCTAVE_PATCH_VERSION) to next release cycle +#* Update oct file API version number (configure.ac:OCTAVE_API_VERSION, add "+" suffix) #* Remove all deprecated functions (either OCTAVE_DEPRECATED in C++ or scripts/deprecated for m-files) scheduled for deletion in default branch #* Move NEWS file to backup in etc/NEWS.X #* Create new NEWS file diff -r 940c1b6e3453 -r 61701d1317a1 etc/icons/org.octave.Octave.appdata.xml diff -r 940c1b6e3453 -r 61701d1317a1 etc/module.mk --- a/etc/module.mk Wed Jul 10 20:02:44 2019 -0700 +++ b/etc/module.mk Fri Jul 12 12:14:43 2019 -0400 @@ -9,6 +9,7 @@ %reldir%/NEWS.2 \ %reldir%/NEWS.3 \ %reldir%/NEWS.4 \ + %reldir%/NEWS.5 \ %reldir%/PROJECTS \ %reldir%/gdbinit diff -r 940c1b6e3453 -r 61701d1317a1 examples/code/funcdemo.cc --- a/examples/code/funcdemo.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/examples/code/funcdemo.cc Fri Jul 12 12:14:43 2019 -0400 @@ -1,7 +1,7 @@ #include #include -DEFUN_DLD (funcdemo, args, nargout, "Function Demo") +DEFMETHOD_DLD (funcdemo, interp, args, nargout, "Function Demo") { int nargin = args.length (); @@ -15,18 +15,9 @@ octave_value_list retval; - if (args(0).is_function_handle () || args(0).is_inline_function ()) - { - octave_function *fcn = args(0).function_value (); - - retval = feval (fcn, newargs, nargout); - } - else if (args(0).is_string ()) - { - std::string fcn = args(0).string_value (); - - retval = feval (fcn, newargs, nargout); - } + if (args(0).is_function_handle () || args(0).is_inline_function () + || args(0).is_string ()) + retval = interp.feval (args(0), newargs, nargout); else error ("funcdemo: INPUT must be string, inline, or function handle"); diff -r 940c1b6e3453 -r 61701d1317a1 examples/code/make_int.cc --- a/examples/code/make_int.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/examples/code/make_int.cc Fri Jul 12 12:14:43 2019 -0400 @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -161,12 +160,7 @@ const octave_integer& v1 = dynamic_cast (a1); const octave_integer& v2 = dynamic_cast (a2); - int d = v2.integer_value (); - - if (d == 0) - warn_divide_by_zero (); - - return new octave_integer (v1.integer_value () / d); + return new octave_integer (v1.integer_value () / v2.integer_value ()); } @@ -175,12 +169,7 @@ const octave_integer& v1 = dynamic_cast (a1); const octave_scalar& v2 = dynamic_cast (a2); - double d = v2.double_value (); - - if (d == 0.0) - warn_divide_by_zero (); - - return new octave_scalar (v1.double_value () / d); + return new octave_scalar (v1.double_value () / v2.double_value ()); } DEFBINOP (ldiv, integer, integer) @@ -188,12 +177,7 @@ const octave_integer& v1 = dynamic_cast (a1); const octave_integer& v2 = dynamic_cast (a2); - int d = v1.integer_value (); - - if (d == 0) - warn_divide_by_zero (); - - return new octave_integer (v2.integer_value () / d); + return new octave_integer (v2.integer_value () / v1.integer_value ()); } DEFBINOP_OP (lt, integer, integer, <) @@ -210,12 +194,7 @@ const octave_integer& v1 = dynamic_cast (a1); const octave_integer& v2 = dynamic_cast (a2); - int d = v2.integer_value (); - - if (d == 0) - warn_divide_by_zero (); - - return new octave_integer (v1.integer_value () / d); + return new octave_integer (v1.integer_value () / v2.integer_value ()); } DEFBINOP (el_ldiv, integer, integer) @@ -223,12 +202,7 @@ const octave_integer& v1 = dynamic_cast (a1); const octave_integer& v2 = dynamic_cast (a2); - int d = v1.integer_value (); - - if (d == 0) - warn_divide_by_zero (); - - return new octave_integer (v2.integer_value () / d); + return new octave_integer (v2.integer_value () / v1.integer_value ()); } DEFBINOP_OP (el_and, integer, integer, &&) diff -r 940c1b6e3453 -r 61701d1317a1 examples/code/oregonator.cc --- a/examples/code/oregonator.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/examples/code/oregonator.cc Fri Jul 12 12:14:43 2019 -0400 @@ -1,7 +1,7 @@ #include DEFUN_DLD (oregonator, args, , - "The `oregonator'.\n\ + "The 'oregonator'.\n\ \n\ Reference:\n\ \n\ diff -r 940c1b6e3453 -r 61701d1317a1 examples/code/oregonator.m --- a/examples/code/oregonator.m Wed Jul 10 20:02:44 2019 -0700 +++ b/examples/code/oregonator.m Fri Jul 12 12:14:43 2019 -0400 @@ -1,4 +1,4 @@ -## The `oregonator'. +## The 'oregonator'. ## ## Reference: ## diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/BaseControl.cc --- a/libgui/graphics/BaseControl.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/BaseControl.cc Fri Jul 12 12:14:43 2019 -0400 @@ -93,6 +93,7 @@ BaseControl::BaseControl (const graphics_object& go, QWidget *w) : Object (go, w), m_normalizedFont (false), m_keyPressHandlerDefined (false) { + qObject ()->setObjectName ("UIControl"); init (w); } @@ -123,6 +124,12 @@ { } void + BaseControl::redraw (void) + { + update (uicontrol::properties::ID_POSITION); + } + + void BaseControl::update (int pId) { uicontrol::properties& up = properties (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/BaseControl.h --- a/libgui/graphics/BaseControl.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/BaseControl.h Fri Jul 12 12:14:43 2019 -0400 @@ -43,6 +43,7 @@ protected: void init (QWidget *w, bool callBase = false); + void redraw (void); void update (int pId); private: diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/ButtonGroup.cc --- a/libgui/graphics/ButtonGroup.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/ButtonGroup.cc Fri Jul 12 12:14:43 2019 -0400 @@ -378,6 +378,9 @@ void ButtonGroup::redraw (void) { + update (uibuttongroup::properties::ID_POSITION); + + // FIXME: is it really necessary to update the opengl canvas here? Canvas *canvas = m_container->canvas (m_handle); if (canvas) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Canvas.cc --- a/libgui/graphics/Canvas.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Canvas.cc Fri Jul 12 12:14:43 2019 -0400 @@ -41,8 +41,11 @@ #include "annotation-dialog.h" +#include "interpreter-private.h" +#include "interpreter.h" #include "oct-opengl.h" #include "octave-qt-link.h" +#include "resource-manager.h" #include "builtin-defun-decls.h" @@ -70,33 +73,113 @@ } void - Canvas::setCursor (MouseMode mode) + Canvas::setCursor (MouseMode mode, std::string fallback, + QImage cdata, Matrix hotspot) { QWidget *w = qWidget (); - + QCursor cursor = Qt::ArrowCursor; if (w) { - static QCursor origCursor = w->cursor (); - switch (mode) { + case NoMode: + { + cursor = Qt::ArrowCursor; + + if (fallback == "arrow") + cursor = Qt::ArrowCursor; + else if (fallback == "botl") + cursor = QCursor (octave::resource_manager::icon + ("bottom_left_corner").pixmap (22,22), + 5, 16); + else if (fallback == "botr") + cursor = QCursor (octave::resource_manager::icon + ("bottom_right_corner").pixmap (22, 22), + 16, 16); + else if (fallback == "bottom") + cursor = QCursor (octave::resource_manager::icon + ("bottom_side").pixmap (22, 22), + 11, 16); + else if (fallback == "circle") + cursor = QCursor (octave::resource_manager::icon + ("circle").pixmap (22, 22), + 10, 10); + else if (fallback == "cross" || fallback == "crosshair") + cursor = QCursor (octave::resource_manager::icon + ("cross").pixmap (22, 22), 10, 10); + else if (fallback == "custom") + { + if (hotspot(0) > cdata.width () || hotspot(0) < 1.0 + || hotspot(1) > cdata.height () || hotspot(1) < 1.0) + hotspot = Matrix (1, 2, 1); + + cursor = QCursor (QPixmap::fromImage (cdata), + static_cast (hotspot(1) - 1), + static_cast (hotspot(0) - 1)); + } + else if (fallback == "fleur") + cursor = QCursor (octave::resource_manager::icon + ("fleur").pixmap (22, 22), 10, 4); + else if (fallback == "hand") + cursor = QCursor (octave::resource_manager::icon + ("hand2").pixmap (22, 22), 7, 3); + else if (fallback == "ibeam") + cursor = Qt::IBeamCursor; + else if (fallback == "left") + cursor = QCursor (octave::resource_manager::icon + ("left_side").pixmap (22, 22), 4, 10); + else if (fallback == "right") + cursor = QCursor (octave::resource_manager::icon + ("right_side").pixmap (22, 22), + 17, 10); + else if (fallback == "top") + cursor = QCursor (octave::resource_manager::icon + ("top_side").pixmap (22, 22), 11, 4); + else if (fallback == "topl") + cursor = QCursor (octave::resource_manager::icon + ("top_left_corner").pixmap (22, 22), + 4, 4); + else if (fallback == "topr") + cursor = QCursor (octave::resource_manager::icon + ("top_right_corner").pixmap (22, 22), + 16, 4); + else if (fallback == "watch") + cursor = Qt::BusyCursor; + } + break; + case SelectMode: + cursor = Qt::ArrowCursor; + break; + case PanMode: + cursor = QCursor (octave::resource_manager::icon ("figure-pan") + .pixmap (22, 22)); + break; + case RotateMode: - w->setCursor (Qt::OpenHandCursor); + cursor = QCursor (octave::resource_manager::icon ("figure-rotate") + .pixmap (22, 22)); + break; + + case TextMode: + cursor = Qt::IBeamCursor; break; case ZoomInMode: - w->setCursor (QPixmap (":/images/zoom-in.png")); + cursor = QCursor (octave::resource_manager::icon ("figure-zoom-in") + .pixmap (22, 22), 9, 9); break; case ZoomOutMode: - w->setCursor (QPixmap (":/images/zoom-out.png")); + cursor = QCursor (octave::resource_manager::icon ("figure-zoom-out") + .pixmap (22, 22), 9, 9); break; default: - w->setCursor (origCursor); + cursor = Qt::ArrowCursor; break; } + w->setCursor (cursor); } } @@ -186,78 +269,14 @@ void Canvas::annotation_callback (const octave_value_list& args) { - Ffeval (ovl ("annotation").append (args)); + octave::interpreter& interp + = octave::__get_interpreter__ ("Canvas::annotation_callback"); + + interp.feval ("annotation", args); redraw (); } - void - Canvas::canvasToggleAxes (const graphics_handle& handle) - { - gh_manager::auto_lock lock; - - graphics_object go = gh_manager::get_object (handle); - - if (go.valid_object ()) - { - figure::properties& fp = Utils::properties
(go); - - graphics_handle ah = fp.get_currentaxes (); - - graphics_object ax = gh_manager::get_object (ah); - - if (ax.valid_object ()) - { - axes::properties& ap = Utils::properties (ax); - - if (ap.handlevisibility_is ("on")) - { - ap.set_visible (! ap.is_visible ()); - - redraw (true); - } - } - } - } - - void - Canvas::canvasToggleGrid (const graphics_handle& handle) - { - gh_manager::auto_lock lock; - - graphics_object go = gh_manager::get_object (handle); - - if (go.valid_object ()) - { - figure::properties& fp = Utils::properties
(go); - - graphics_handle ah = fp.get_currentaxes (); - - graphics_object ax = gh_manager::get_object (ah); - - if (ax.valid_object ()) - { - axes::properties& ap = Utils::properties (ax); - - std::string tmp; - - // If any grid is off, then turn them all on. If they are all - // on, then turn them off. - - std::string state = ((ap.get_xgrid () == "off" - || ap.get_ygrid () == "off" - || ap.get_zgrid () == "off") - ? "on" : "off"); - - ap.set_xgrid (state); - ap.set_ygrid (state); - ap.set_zgrid (state); - - redraw (true); - } - } - } - static void autoscale_axes (axes::properties& ap) { @@ -272,32 +291,6 @@ } void - Canvas::canvasAutoAxes (const graphics_handle& handle) - { - gh_manager::auto_lock lock; - - graphics_object go = gh_manager::get_object (handle); - - if (go.valid_object ()) - { - figure::properties& fp = Utils::properties
(go); - - graphics_handle ah = fp.get_currentaxes (); - - graphics_object ax = gh_manager::get_object (ah); - - if (ax.valid_object ()) - { - axes::properties& ap = Utils::properties (ax); - - autoscale_axes (ap); - - redraw (true); - } - } - } - - void Canvas::canvasPaintEvent (void) { if (! m_redrawBlocked) @@ -639,14 +632,15 @@ currentObj = graphics_object (); } - if (axesObj) - { - if (axesObj.get_properties ().handlevisibility_is ("on") - && axesObj.get_properties ().get_tag () != "legend" - && axesObj.get_properties ().get_tag () != "colorbar") - Utils::properties
(figObj) - .set_currentaxes (axesObj.get_handle ().as_octave_value ()); - } + // Make selected axes current + bool valid_axes = axesObj.valid_object () + && axesObj.get_properties ().handlevisibility_is ("on") + && axesObj.get_properties ().get_tag () != "legend" + && axesObj.get_properties ().get_tag () != "colorbar"; + + if (valid_axes) + Utils::properties
(figObj) + .set_currentaxes (axesObj.get_handle ().as_octave_value ()); Figure *fig = dynamic_cast
(Backend::toolkitObject (figObj)); @@ -670,9 +664,10 @@ fprop.set_currentobject (Matrix ()); // Update figure "selectiontype" and "currentpoint" - gh_manager::post_set ( - figObj.get_handle (), "selectiontype", - Utils::figureSelectionType (event, isdblclick), false); + gh_manager::post_set (figObj.get_handle (), "selectiontype", + Utils::figureSelectionType (event, + isdblclick), + false); updateCurrentPoint (figObj, obj, event); @@ -718,7 +713,7 @@ case RotateMode: case ZoomInMode: case ZoomOutMode: - if (axesObj && axesObj.get_properties ().handlevisibility_is ("on")) + if (valid_axes) { bool redraw_figure = true; @@ -912,6 +907,8 @@ { std::string mode; + graphics_object figObj (obj.get_ancestor ("figure")); + graphics_object axesObj; Matrix children = obj.get_properties ().get_children (); @@ -937,8 +934,6 @@ { MouseMode newMouseMode = NoMode; - graphics_object figObj (obj.get_ancestor ("figure")); - Figure *fig = dynamic_cast
(Backend::toolkitObject (figObj)); if (fig) @@ -1012,6 +1007,13 @@ if (redrawFigure) redraw (false); } + + if (! figObj.get ("windowscrollwheelfcn").isempty ()) + { + octave_scalar_map eventData = Utils::makeScrollEventStruct (event); + gh_manager::post_callback (m_handle, "windowscrollwheelfcn", + eventData); + } } } @@ -1028,13 +1030,14 @@ graphics_object figObj (obj.get_ancestor ("figure")); updateCurrentPoint (figObj, obj); - } - octave_scalar_map eventData = Utils::makeKeyEventStruct (event); + octave_scalar_map eventData = Utils::makeKeyEventStruct (event); - gh_manager::post_set (m_handle, "currentcharacter", - eventData.getfield ("Character"), false); - gh_manager::post_callback (m_handle, "keypressfcn", eventData); + gh_manager::post_set (figObj.get_handle (), "currentcharacter", + eventData.getfield ("Character"), false); + gh_manager::post_callback (figObj.get_handle (), "keypressfcn", + eventData); + } return true; } @@ -1047,8 +1050,15 @@ { if (! event->isAutoRepeat () && (m_eventMask & KeyRelease)) { - gh_manager::post_callback (m_handle, "keyreleasefcn", - Utils::makeKeyEventStruct (event)); + gh_manager::auto_lock lock; + graphics_object obj = gh_manager::get_object (m_handle); + + if (obj.valid_object ()) + { + graphics_object figObj (obj.get_ancestor ("figure")); + gh_manager::post_callback (figObj.get_handle (), "keyreleasefcn", + Utils::makeKeyEventStruct (event)); + } return true; } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Canvas.h --- a/libgui/graphics/Canvas.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Canvas.h Fri Jul 12 12:14:43 2019 -0400 @@ -63,17 +63,14 @@ void clearEventMask (int m) { m_eventMask &= (~m); } void setEventMask (int m) { m_eventMask = m; } - void setCursor (MouseMode mode); + void setCursor (MouseMode mode, std::string fallback, + QImage cdata, Matrix hotspot); virtual QWidget * qWidget (void) = 0; static Canvas * create (const std::string& name, QWidget *parent, const graphics_handle& handle); - virtual void toggleAxes (const graphics_handle& handle) = 0; - virtual void toggleGrid (const graphics_handle& handle) = 0; - virtual void autoAxes (const graphics_handle& handle) = 0; - virtual uint8NDArray getPixels (void) { return do_getPixels (m_handle); }; protected: diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Figure.cc --- a/libgui/graphics/Figure.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Figure.cc Fri Jul 12 12:14:43 2019 -0400 @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -50,7 +49,6 @@ #include "Container.h" #include "Figure.h" #include "FigureWindow.h" -#include "MouseModeActionGroup.h" #include "QtHandlesUtils.h" #include "file-ops.h" @@ -83,6 +81,29 @@ return r; } + static QImage + pointer_to_qimage (const Matrix& cdata) + { + QImage retval (cdata.rows (), cdata.columns (), QImage::Format_ARGB32); + QColor tmp ("White"); + QColor black ("Black"); + QColor white ("White"); + for (octave_idx_type ii = 0; ii < cdata.rows (); ii++) + for (octave_idx_type jj = 0; jj < cdata.columns (); jj++) + { + if (cdata(ii,jj) == 1.0) + tmp = black; + else if (cdata(ii,jj) == 2.0) + tmp = white; + else + tmp.setAlpha (0); + + retval.setPixel (jj, ii, tmp.rgba ()); + } + + return retval; + } + Figure* Figure::create (const graphics_object& go) { @@ -92,42 +113,31 @@ Figure::Figure (const graphics_object& go, FigureWindow *win) : Object (go, win), m_blockUpdates (false), m_figureToolBar (nullptr), m_menuBar (nullptr), m_innerRect (), m_outerRect (), - m_mouseModeGroup (nullptr), m_previousHeight (0), m_resizable (true) + m_previousHeight (0), m_resizable (true) { m_container = new Container (win); win->setCentralWidget (m_container); figure::properties& fp = properties
(); - // Register for the signal that indicates when a window has moved - // to a different screen - connect (win, SIGNAL (figureWindowShown ()), - this, SLOT (figureWindowShown ())); + // Adjust figure position + m_innerRect = boundingBoxToRect (fp.get_boundingbox (true)); + m_outerRect = boundingBoxToRect (fp.get_boundingbox (false)); + + set_geometry (m_innerRect); + + // Menubar + m_menuBar = new MenuBar (win); + win->setMenuBar (m_menuBar); + m_menuBar->addReceiver (this); // Status bar m_statusBar = win->statusBar (); - int boffset = 0; - - // Toolbar and menubar - createFigureToolBarAndMenuBar (); - int toffset = 0; + m_statusBar->setVisible (false); if (fp.toolbar_is ("figure") || (fp.toolbar_is ("auto") && fp.menubar_is ("figure"))) - { - toffset += m_figureToolBar->sizeHint ().height (); - boffset += m_statusBar->sizeHint ().height (); - } - else - { - m_figureToolBar->hide (); - m_statusBar->hide (); - } - - m_innerRect = boundingBoxToRect (fp.get_boundingbox (true)); - m_outerRect = boundingBoxToRect (fp.get_boundingbox (false)); - - set_geometry (m_innerRect.adjusted (0, -toffset, 0, boffset)); + showFigureStatusBar (true); // Enable mouse tracking unconditionally enableMouseTracking (); @@ -149,12 +159,20 @@ // Handle resizing constraints update (figure::properties::ID_RESIZE); + // Custom pointer data + update (figure::properties::ID_POINTERSHAPECDATA); + // Visibility update (figure::properties::ID_VISIBLE); connect (this, SIGNAL (asyncUpdate (void)), this, SLOT (updateContainer (void))); + // Register for the signal that indicates when a window has moved + // to a different screen + connect (win, SIGNAL (figureWindowShown ()), + this, SLOT (figureWindowShown ())); + win->addReceiver (this); m_container->addReceiver (this); } @@ -162,55 +180,6 @@ Figure::~Figure (void) { } - static std::string - mouse_mode_to_string (MouseMode mode) - { - switch (mode) - { - case NoMode: - return "none"; - - case RotateMode: - return "rotate"; - - case ZoomInMode: - return "zoom in"; - - case ZoomOutMode: - return "zoom out"; - - case PanMode: - return "pan"; - - case TextMode: - return "text"; - - default: - break; - } - - return "none"; - } - - static MouseMode - mouse_mode_from_string (const std::string& mode) - { - if (mode == "none") - return NoMode; - else if (mode == "rotate") - return RotateMode; - else if (mode == "zoom in") - return ZoomInMode; - else if (mode == "zoom out") - return ZoomOutMode; - else if (mode == "pan") - return PanMode; - else if (mode == "text") - return TextMode; - else - return NoMode; - } - QString Figure::fileName (void) { @@ -251,49 +220,18 @@ mode += ' ' + direction; } - return mouse_mode_from_string (mode); - } - - void - Figure::createFigureToolBarAndMenuBar (void) - { - QMainWindow *win = qWidget (); - - m_figureToolBar = win->addToolBar (tr ("Figure ToolBar")); - m_figureToolBar->setMovable (false); - m_figureToolBar->setFloatable (false); - - m_mouseModeGroup = new MouseModeActionGroup (win); - connect (m_mouseModeGroup, SIGNAL (modeChanged (MouseMode)), - SLOT (setMouseMode (MouseMode))); - m_figureToolBar->addActions (m_mouseModeGroup->actions ()); - - QAction *toggle_axes = m_figureToolBar->addAction (tr ("Axes")); - connect (toggle_axes, SIGNAL (triggered (void)), - this, SLOT (toggleAxes (void))); + if (mode == "rotate") + return RotateMode; + else if (mode == "zoom in") + return ZoomInMode; + else if (mode == "zoom out") + return ZoomOutMode; + else if (mode == "pan") + return PanMode; + else if (mode == "text") + return TextMode; - QAction *toggle_grid = m_figureToolBar->addAction (tr ("Grid")); - connect (toggle_grid, SIGNAL (triggered (void)), - this, SLOT (toggleGrid (void))); - - QAction *auto_axes = m_figureToolBar->addAction (tr ("Autoscale")); - connect (auto_axes, SIGNAL (triggered (void)), - this, SLOT (autoAxes (void))); - - m_menuBar = new MenuBar (win); - win->setMenuBar (m_menuBar); - m_menuBar->addReceiver (this); - } - - void - Figure::updateFigureToolBarAndMenuBar (void) - { - if (m_mouseModeGroup) - { - m_blockUpdates = true; - m_mouseModeGroup->setMode (mouseMode ()); - m_blockUpdates = false; - } + return NoMode; } void @@ -307,6 +245,11 @@ win->setFixedSize (QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); } + // Unlock window if it is maximized or full-screen + int state = win->windowState (); + if (state == Qt::WindowFullScreen || state == Qt::WindowMaximized) + win->setWindowState (Qt::WindowNoState); + win->setGeometry (r); if (! m_resizable) @@ -330,20 +273,20 @@ if (canvas) canvas->redraw (); - foreach (QFrame *frame, - qWidget ()->findChildren ()) + foreach (QObject *qobj, + qWidget ()->findChildren ()) { - if (frame->objectName () == "UIPanel" - || frame->objectName () == "UIButtonGroup") + if (qobj->objectName () == "UIPanel" + || qobj->objectName () == "UIButtonGroup" + || qobj->objectName () == "UIControl" + || qobj->objectName () == "UITable") { - Object *obj = Object::fromQObject (frame); + Object *obj = Object::fromQObject (qobj); if (obj) obj->slotRedraw (); } } - - updateFigureToolBarAndMenuBar (); } void @@ -388,7 +331,6 @@ if (canvas) canvas->blockRedraw (true); - m_menuBar->removeReceiver (this); m_container->removeReceiver (this); qWidget ()->removeReceiver (this); } @@ -468,18 +410,14 @@ } break; + case figure::properties::ID_MENUBAR: case figure::properties::ID_TOOLBAR: if (fp.toolbar_is ("none")) - showFigureToolBar (false); + showFigureStatusBar (false); else if (fp.toolbar_is ("figure")) - showFigureToolBar (true); + showFigureStatusBar (true); else // "auto" - showFigureToolBar (fp.menubar_is ("figure")); - break; - - case figure::properties::ID_MENUBAR: - if (fp.toolbar_is ("auto")) - showFigureToolBar (fp.menubar_is ("figure")); + showFigureStatusBar (fp.menubar_is ("figure")); break; case figure::properties::ID_KEYPRESSFCN: @@ -487,6 +425,25 @@ m_container->canvas (m_handle)->clearEventMask (Canvas::KeyPress); else m_container->canvas (m_handle)->addEventMask (Canvas::KeyPress); + // Signal the change to uipanels as well + foreach (QObject *qobj, + qWidget ()->findChildren ()) + { + if (qobj->objectName () == "UIPanel") + { + Object *obj = Object::fromQObject (qobj); + + if (obj) + { + if (fp.get_keypressfcn ().isempty ()) + obj->innerContainer ()->canvas (m_handle)-> + clearEventMask (Canvas::KeyPress); + else + obj->innerContainer ()->canvas (m_handle)-> + addEventMask (Canvas::KeyPress); + } + } + } break; case figure::properties::ID_KEYRELEASEFCN: @@ -495,6 +452,26 @@ else m_container->canvas (m_handle)->addEventMask (Canvas::KeyRelease); break; + // Signal the change to uipanels as well + foreach (QObject *qobj, + qWidget ()->findChildren ()) + { + if (qobj->objectName () == "UIPanel") + { + Object *obj = Object::fromQObject (qobj); + + if (obj) + { + if (fp.get_keypressfcn ().isempty ()) + obj->innerContainer ()->canvas (m_handle)-> + clearEventMask (Canvas::KeyRelease); + else + obj->innerContainer ()->canvas (m_handle)-> + addEventMask (Canvas::KeyRelease); + } + } + } + break; case figure::properties::ID_WINDOWSTYLE: if (fp.windowstyle_is ("modal")) @@ -514,8 +491,22 @@ break; + case figure::properties::ID_POINTERSHAPECDATA: + m_pointer_cdata = + pointer_to_qimage (fp.get_pointershapecdata ().matrix_value ()); + if (fp.get_pointer () != "custom") + break; + OCTAVE_FALLTHROUGH; + + case figure::properties::ID_POINTER: + case figure::properties::ID_POINTERSHAPEHOTSPOT: case figure::properties::ID___MOUSE_MODE__: - m_container->canvas (m_handle)->setCursor (mouseMode ()); + case figure::properties::ID___ZOOM_MODE__: + m_container->canvas (m_handle)->setCursor (mouseMode (), + fp.get_pointer (), + m_pointer_cdata, + fp.get_pointershapehotspot () + .matrix_value()); break; default: @@ -526,22 +517,21 @@ } void - Figure::showFigureToolBar (bool visible) + Figure::showFigureStatusBar (bool visible) { - if ((! m_figureToolBar->isHidden ()) != visible) + if (m_statusBar + && (! m_statusBar->isHidden ()) != visible) { - int dy1 = m_figureToolBar->sizeHint ().height (); - int dy2 = m_statusBar->sizeHint ().height (); + int dy = m_statusBar->sizeHint ().height (); QRect r = qWidget ()->geometry (); if (! visible) - r.adjust (0, dy1, 0, -dy2); + r.adjust (0, 0, 0, -dy); else - r.adjust (0, -dy1, 0, dy2); + r.adjust (0, 0, 0, dy); m_blockUpdates = true; set_geometry (r); - m_figureToolBar->setVisible (visible); m_statusBar->setVisible (visible); m_blockUpdates = false; @@ -584,14 +574,6 @@ return qWidget ()->menuBar (); } - struct UpdateBoundingBoxData - { - Matrix m_bbox; - bool m_internal; - graphics_handle m_handle; - Figure *m_figure; - }; - void Figure::updateBoundingBox (bool internal, int flags) { @@ -779,35 +761,19 @@ } void - Figure::setMouseMode (MouseMode mode) - { - if (m_blockUpdates) - return; - - gh_manager::auto_lock lock; - - figure::properties& fp = properties
(); - - fp.set___mouse_mode__ (mouse_mode_to_string (mode)); - - Canvas *canvas = m_container->canvas (m_handle); - - if (canvas) - canvas->setCursor (mode); - } - - void - Figure::addCustomToolBar (QToolBar *bar, bool visible) + Figure::addCustomToolBar (QToolBar *bar, bool visible, bool isdefault) { QMainWindow *win = qWidget (); + if (isdefault) + m_figureToolBar = bar; + if (! visible) win->addToolBar (bar); else { QSize sz = bar->sizeHint (); QRect r = win->geometry (); - //qDebug () << "Figure::addCustomToolBar:" << r; r.adjust (0, -sz.height (), 0, 0); @@ -817,7 +783,6 @@ win->addToolBar (bar); m_blockUpdates = false; - //qDebug () << "Figure::addCustomToolBar:" << win->geometry (); updateBoundingBox (false); } } @@ -853,33 +818,6 @@ } void - Figure::toggleAxes (void) - { - Canvas *canvas = m_container->canvas (m_handle); - - if (canvas) - canvas->toggleAxes (m_handle); - } - - void - Figure::toggleGrid (void) - { - Canvas *canvas = m_container->canvas (m_handle); - - if (canvas) - canvas->toggleGrid (m_handle); - } - - void - Figure::autoAxes (void) - { - Canvas *canvas = m_container->canvas (m_handle); - - if (canvas) - canvas->autoAxes (m_handle); - } - - void Figure::figureWindowShown () { #if defined (HAVE_QSCREEN_DEVICEPIXELRATIO) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Figure.h --- a/libgui/graphics/Figure.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Figure.h Fri Jul 12 12:14:43 2019 -0400 @@ -39,15 +39,13 @@ enum MouseMode { - // NOTE: These values must match the order of the buttons in the - // MouseModeActionGroup object. - - NoMode = 0, - RotateMode = 1, - ZoomInMode = 2, - ZoomOutMode = 3, - PanMode = 4, - TextMode = 5 + NoMode, + RotateMode, + ZoomInMode, + ZoomOutMode, + PanMode, + SelectMode, + TextMode }; class Container; @@ -55,8 +53,6 @@ class MenuBar; class ToolBar; - class MouseModeActionGroup; - class Figure : public Object, public MenuContainer, @@ -101,22 +97,16 @@ void beingDeleted (void); private: - void createFigureToolBarAndMenuBar (void); - void showFigureToolBar (bool visible); - void addCustomToolBar (QToolBar *bar, bool visible); + void showFigureStatusBar (bool visible); + void addCustomToolBar (QToolBar *bar, bool visible, bool isdefault); void showCustomToolBar (QToolBar *bar, bool visible); - void updateFigureToolBarAndMenuBar (void); void set_geometry (QRect r); void enableMouseTracking (void); private slots: - void setMouseMode (MouseMode mode); void updateFigureHeight (int delta_h); void updateContainer (void); - void toggleAxes (void); - void toggleGrid (void); - void autoAxes (void); void figureWindowShown (); void screenChanged (QScreen*); @@ -134,7 +124,7 @@ QStatusBar *m_statusBar; QRect m_innerRect; QRect m_outerRect; - MouseModeActionGroup *m_mouseModeGroup; + QImage m_pointer_cdata; int m_previousHeight; bool m_resizable; }; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/GLCanvas.cc --- a/libgui/graphics/GLCanvas.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/GLCanvas.cc Fri Jul 12 12:14:43 2019 -0400 @@ -147,7 +147,6 @@ try { -#if defined (Q_OS_MAC) if (fig.get ("visible").string_value () == "on") octave::gl2ps_print (m_glfcns, fig, file_cmd.toStdString (), term.toStdString ()); @@ -171,10 +170,6 @@ fbo.release (); } -#else - octave::gl2ps_print (m_glfcns, fig, file_cmd.toStdString (), - term.toStdString ()); -#endif } catch (octave::execution_exception& e) { @@ -185,24 +180,6 @@ } } - void - GLCanvas::toggleAxes (const graphics_handle& gh) - { - canvasToggleAxes (gh); - } - - void - GLCanvas::toggleGrid (const graphics_handle& gh) - { - canvasToggleGrid (gh); - } - - void - GLCanvas::autoAxes (const graphics_handle& gh) - { - canvasAutoAxes (gh); - } - graphics_object GLCanvas::selectFromAxes (const graphics_object& ax, const QPoint& pt) { diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/GLCanvas.h --- a/libgui/graphics/GLCanvas.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/GLCanvas.h Fri Jul 12 12:14:43 2019 -0400 @@ -60,9 +60,6 @@ uint8NDArray do_getPixels (const graphics_handle& handle); void do_print (const QString& file_cmd, const QString& term, const graphics_handle& handle); - void toggleAxes (const graphics_handle& handle); - void toggleGrid (const graphics_handle& handle); - void autoAxes (const graphics_handle& handle); void drawZoomBox (const QPoint& p1, const QPoint& p2); void resize (int /* x */, int /* y */, int /* width */, int /* height */) { } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/MouseModeActionGroup.cc --- a/libgui/graphics/MouseModeActionGroup.cc Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - -Copyright (C) 2011-2019 Michael Goffioul - -This file is part of Octave. - -Octave 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. - -Octave 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 Octave; see the file COPYING. If not, see -. - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include -#include - -#include "Figure.h" -#include "MouseModeActionGroup.h" - -namespace QtHandles -{ - - MouseModeActionGroup::MouseModeActionGroup (QObject *xparent) - : QObject (xparent), m_current (nullptr) - { - m_actions.append (new QAction (QIcon (":/images/rotate.png"), - tr ("Rotate"), this)); - QAction *zoom_in = new QAction ("Z+", this); - zoom_in->setToolTip (tr ("Zoom In")); - m_actions.append (zoom_in); - - QAction *zoom_out = new QAction ("Z-", this); - zoom_out->setToolTip (tr ("Zoom Out")); - m_actions.append (zoom_out); - - m_actions.append (new QAction (QIcon (":/images/pan.png"), - tr ("Pan"), this)); - m_actions.append (new QAction (QIcon::fromTheme ("insert-text"), - tr ("Insert Text"), this)); - /* - // FIXME: Re-instate this button when the plotedit function - // has been implemented. - m_actions.append (new QAction (QIcon (":/images/select.png"), - tr ("Select"), this)); - */ - - foreach (QAction *a, m_actions) - { - a->setCheckable (true); - connect (a, SIGNAL (toggled (bool)), this, SLOT (actionToggled (bool))); - } - } - - MouseModeActionGroup::~MouseModeActionGroup (void) - { } - - void - MouseModeActionGroup::actionToggled (bool checked) - { - if (! checked) - { - if (sender () == m_current) - { - m_current = nullptr; - emit modeChanged (NoMode); - } - } - else - { - int i = m_actions.indexOf (qobject_cast (sender ())); - - if (i >= 0) - { - m_current = m_actions[i]; - for (int j = 0; j < m_actions.size (); j++) - { - if (j != i) - m_actions[j]->setChecked (false); - } - - emit modeChanged (static_cast (i+1)); - } - } - } - - void - MouseModeActionGroup::setMode (MouseMode mode) - { - for (int i = 0; i < m_actions.size (); i++) - m_actions[i]->setChecked (i+1 == mode); - } - -}; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/MouseModeActionGroup.h --- a/libgui/graphics/MouseModeActionGroup.h Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - -Copyright (C) 2011-2019 Michael Goffioul - -This file is part of Octave. - -Octave 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. - -Octave 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 Octave; see the file COPYING. If not, see -. - -*/ - -#if ! defined (octave_MouseModeActionGroup_h) -#define octave_MouseModeActionGroup_h 1 - -#include -#include - -#include "Figure.h" - -class QAction; - -namespace QtHandles -{ - - class MouseModeActionGroup : public QObject - { - Q_OBJECT - - public: - MouseModeActionGroup (QObject *parent = nullptr); - ~MouseModeActionGroup (void); - - QList actions (void) const { return m_actions; } - - void setMode (MouseMode mode); - - signals: - void modeChanged (MouseMode mode); - - private slots: - void actionToggled (bool checked); - - private: - QList m_actions; - QAction *m_current; - }; - -}; - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/ObjectFactory.cc --- a/libgui/graphics/ObjectFactory.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/ObjectFactory.cc Fri Jul 12 12:14:43 2019 -0400 @@ -138,7 +138,7 @@ else if (go.isa ("uitoggletool")) obj = ToggleTool::create (go); else - qWarning ("ObjectFactory::createObject: unsupported type `%s'", + qWarning ("ObjectFactory::createObject: unsupported type '%s'", go.type ().c_str ()); if (obj) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Panel.cc --- a/libgui/graphics/Panel.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Panel.cc Fri Jul 12 12:14:43 2019 -0400 @@ -138,6 +138,13 @@ frame->installEventFilter (this); m_container->installEventFilter (this); + + graphics_object fig (go.get_ancestor ("figure")); + if (! fig.get ("keypressfcn").isempty ()) + m_container->canvas (m_handle)->addEventMask (Canvas::KeyPress); + + if (! fig.get ("keyreleasefcn").isempty ()) + m_container->canvas (m_handle)->addEventMask (Canvas::KeyRelease); if (pp.is_visible ()) QTimer::singleShot (0, frame, SLOT (show (void))); @@ -337,6 +344,8 @@ void Panel::redraw (void) { + update (uipanel::properties::ID_POSITION); + Canvas *canvas = m_container->canvas (m_handle); if (canvas) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/QtHandlesUtils.cc --- a/libgui/graphics/QtHandlesUtils.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/QtHandlesUtils.cc Fri Jul 12 12:14:43 2019 -0400 @@ -113,8 +113,6 @@ if (! mapsInitialized) { weightMap["normal"] = QFont::Normal; - weightMap["light"] = QFont::Light; - weightMap["demi"] = QFont::DemiBold; weightMap["bold"] = QFont::Bold; angleMap["normal"] = QFont::StyleNormal; @@ -186,13 +184,9 @@ return "normal"; else if (buttons == Qt::RightButton) return "alt"; -#if defined (Q_WS_WIN) - else if (buttons == (Qt::LeftButton | Qt::RightButton)) + else if (buttons == Qt::MidButton + || buttons == (Qt::LeftButton | Qt::RightButton)) return "extend"; -#elif defined (Q_WS_X11) - else if (buttons == Qt::MidButton) - return "extend"; -#endif } else if (buttons == Qt::LeftButton) { @@ -390,6 +384,29 @@ return retval; } + octave_scalar_map + makeScrollEventStruct (QWheelEvent *event) + { + octave_scalar_map retval; + + // We assume a standard mouse with 15 degree steps and Qt returns + // 1/8 of a degree. +#if defined (HAVE_QWHEELEVENT_ANGLEDELTA) + int ydelta = -(event->angleDelta().y ()); +#else + int ydelta = (event->orientation () == Qt::Vertical + ? -(event->delta ()) : 0); +#endif + retval.setfield ("VerticalScrollCount", octave_value (ydelta / 120)); + + // FIXME: Is there any way to access the number of lines a scroll step + // should correspond to? + retval.setfield ("VerticalScrollAmount", octave_value (3)); + retval.setfield ("EventName", octave_value ("WindowScrollWheel")); + + return retval; + } + } } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/QtHandlesUtils.h --- a/libgui/graphics/QtHandlesUtils.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/QtHandlesUtils.h Fri Jul 12 12:14:43 2019 -0400 @@ -35,6 +35,7 @@ class QKeyEvent; class QMouseEvent; +class QWheelEvent; namespace QtHandles { @@ -78,6 +79,7 @@ int height = -1); octave_scalar_map makeKeyEventStruct (QKeyEvent *event); + octave_scalar_map makeScrollEventStruct (QWheelEvent *event); } } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Table.cc --- a/libgui/graphics/Table.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Table.cc Fri Jul 12 12:14:43 2019 -0400 @@ -442,6 +442,7 @@ : Object (go, tableWidget), m_tableWidget (tableWidget), m_curData (), m_blockUpdates (false) { + qObject ()->setObjectName ("UItable"); uitable::properties& tp = properties (); m_curData = octave_value (tp.get_data ()); @@ -863,6 +864,12 @@ } void + Table::redraw (void) + { + update (uitable::properties::ID_POSITION); + } + + void Table::update (int pId) { uitable::properties& tp = properties (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/Table.h --- a/libgui/graphics/Table.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/Table.h Fri Jul 12 12:14:43 2019 -0400 @@ -50,7 +50,7 @@ protected: void update (int pId); - //void redraw (void); + void redraw (void); void updateColumnname (void); void updateColumnwidth (void); void updateData (void); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/ToolBar.cc --- a/libgui/graphics/ToolBar.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/ToolBar.cc Fri Jul 12 12:14:43 2019 -0400 @@ -94,7 +94,8 @@ dynamic_cast
(Object::fromQObject (bar->parentWidget ())); if (m_figure) - m_figure->addCustomToolBar (bar, tp.is_visible ()); + m_figure->addCustomToolBar (bar, tp.is_visible (), + tp.get_tag () == "__default_toolbar__"); bar->installEventFilter (this); } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/ToolBarButton.cc --- a/libgui/graphics/ToolBarButton.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/ToolBarButton.cc Fri Jul 12 12:14:43 2019 -0400 @@ -29,6 +29,7 @@ #include "ToolBarButton.h" #include "QtHandlesUtils.h" +#include "resource-manager.h" namespace QtHandles { @@ -41,8 +42,22 @@ action->setToolTip (Utils::fromStdString (tp.get_tooltipstring ())); action->setVisible (tp.is_visible ()); - QImage img = Utils::makeImageFromCData (tp.get_cdata (), 16, 16); - action->setIcon (QIcon (QPixmap::fromImage (img))); + + // Get the icon data from cdata or as a named icon + QImage img = Utils::makeImageFromCData (tp.get_cdata (), 32, 32); + + if (img.width () == 0) + { + QIcon ico; + std::string name = tp.get___named_icon__ (); + if (! name.empty ()) + ico = octave::resource_manager::icon (QString::fromStdString (name)); + + action->setIcon (ico); + } + else + action->setIcon (QIcon (QPixmap::fromImage (img))); + if (tp.is_separator ()) { m_separator = new QAction (action); @@ -83,9 +98,19 @@ case T::properties::ID_CDATA: { - QImage img = Utils::makeImageFromCData (tp.get_cdata (), 16, 16); + // Get the icon data from cdata or as a named icon + QImage img = Utils::makeImageFromCData (tp.get_cdata (), 32, 32); - action->setIcon (QIcon (QPixmap::fromImage (img))); + if (img.width () == 0) + { + QIcon ico; + std::string name = tp.get___named_icon__ (); + if (! name.empty ()) + ico = octave::resource_manager::icon (QString::fromStdString (name)); + action->setIcon (ico); + } + else + action->setIcon (QIcon (QPixmap::fromImage (img))); } break; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/README --- a/libgui/graphics/images/README Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -This copyright and license notice covers the images in this directory. -************************************************************************ - -TITLE: Crystal Project Icons -AUTHOR: Everaldo Coelho -SITE: http://www.everaldo.com -CONTACT: everaldo@everaldo.com - -Copyright (c) 2006-2007 Everaldo Coelho. - -The following images are derivative work: - -zoom-in.png -zoom-out.png diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/pan.png Binary file libgui/graphics/images/pan.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/rotate.png Binary file libgui/graphics/images/rotate.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/select.png Binary file libgui/graphics/images/select.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/zoom-in.png Binary file libgui/graphics/images/zoom-in.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/images/zoom-out.png Binary file libgui/graphics/images/zoom-out.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/module.mk --- a/libgui/graphics/module.mk Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/graphics/module.mk Fri Jul 12 12:14:43 2019 -0400 @@ -13,7 +13,6 @@ %reldir%/moc-FigureWindow.cc \ %reldir%/moc-ListBoxControl.cc \ %reldir%/moc-Menu.cc \ - %reldir%/moc-MouseModeActionGroup.cc \ %reldir%/moc-Object.cc \ %reldir%/moc-ObjectFactory.cc \ %reldir%/moc-ObjectProxy.cc \ @@ -42,10 +41,6 @@ BUILT_SOURCES += $(__init_qt___UI_H) -__init_qt___RC = %reldir%/qrc-qthandles.cc - -$(__init_qt___RC): | %reldir%/$(octave_dirstamp) - noinst_HEADERS += \ %reldir%/__init_qt__.h \ %reldir%/annotation-dialog.h \ @@ -67,7 +62,6 @@ %reldir%/Logger.h \ %reldir%/Menu.h \ %reldir%/MenuContainer.h \ - %reldir%/MouseModeActionGroup.h \ %reldir%/Object.h \ %reldir%/ObjectFactory.h \ %reldir%/ObjectProxy.h \ @@ -108,7 +102,6 @@ %reldir%/ListBoxControl.cc \ %reldir%/Logger.cc \ %reldir%/Menu.cc \ - %reldir%/MouseModeActionGroup.cc \ %reldir%/Object.cc \ %reldir%/ObjectFactory.cc \ %reldir%/ObjectProxy.cc \ @@ -130,7 +123,7 @@ TEMPLATE_SRC = \ %reldir%/ToolBarButton.cc -nodist_%canon_reldir%___init_qt___la_SOURCES = $(__init_qt___MOC) $(__init_qt___RC) +nodist_%canon_reldir%___init_qt___la_SOURCES = $(__init_qt___MOC) %canon_reldir%___init_qt___la_CPPFLAGS = \ $(AM_CPPFLAGS) \ @@ -195,20 +188,12 @@ $(GRAPHICS_PKG_ADD_FILE) libgui_EXTRA_DIST += \ - %reldir%/qthandles.qrc \ - %reldir%/images/README \ - %reldir%/images/pan.png \ - %reldir%/images/rotate.png \ - %reldir%/images/select.png \ - %reldir%/images/zoom-in.png \ - %reldir%/images/zoom-out.png \ $(__init_qt___UI) libgui_CLEANFILES += \ $(GRAPHICS_OCT_FILES) \ $(GRAPHICS_PKG_ADD_FILE) \ $(__init_qt___MOC) \ - $(__init_qt___RC) \ $(__init_qt___UI_H) endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/graphics/qthandles.qrc --- a/libgui/graphics/qthandles.qrc Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ - - - images/pan.png - images/rotate.png - images/select.png - images/zoom-in.png - images/zoom-out.png - - diff -r 940c1b6e3453 -r 61701d1317a1 libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslator.h --- a/libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslator.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslator.h Fri Jul 12 12:14:43 2019 -0400 @@ -9,10 +9,10 @@ "key Return-Shift+NewLine : \"\\r\\n\"\n" "key Return+Shift : \"\\EOM\"\n" "key Backspace : \"\\x7f\"\n" -"key Up -Shift-Ansi : \"\\EA\"\n" -"key Down -Shift-Ansi : \"\\EB\"\n" -"key Right-Shift-Ansi : \"\\EC\"\n" -"key Left -Shift-Ansi : \"\\ED\"\n" +"key Up -Shift-Ansi : \"\\EA\"\n" +"key Down -Shift-Ansi : \"\\EB\"\n" +"key Right -Shift-Ansi : \"\\EC\"\n" +"key Left -Shift-Ansi : \"\\ED\"\n" "key Up -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOA\"\n" "key Down -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOB\"\n" "key Right -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOC\"\n" @@ -27,10 +27,10 @@ "key Left -Shift+AnyMod+Ansi : \"\\E[1;*D\"\n" "key Enter+NewLine : \"\\r\\n\"\n" "key Enter-NewLine : \"\\r\"\n" -"key Home -AnyMod -AppCuKeys : \"\\E[H\" \n" -"key End -AnyMod -AppCuKeys : \"\\E[F\" \n" -"key Home -AnyMod +AppCuKeys : \"\\EOH\" \n" -"key End -AnyMod +AppCuKeys : \"\\EOF\" \n" +"key Home -AnyMod -AppCuKeys : \"\\E[H\"\n" +"key End -AnyMod -AppCuKeys : \"\\E[F\"\n" +"key Home -AnyMod +AppCuKeys : \"\\EOH\"\n" +"key End -AnyMod +AppCuKeys : \"\\EOF\"\n" "key Home +AnyMod : \"\\E[1;*H\"\n" "key End +AnyMod : \"\\E[1;*F\"\n" "key Insert -AnyMod : \"\\E[2~\"\n" @@ -65,10 +65,10 @@ "key F10 +AnyMod : \"\\E[21;*~\"\n" "key F11 +AnyMod : \"\\E[23;*~\"\n" "key F12 +AnyMod : \"\\E[24;*~\"\n" -"key Space +Control : \"\\x00\"\n" +"key Space +Control : \"\\x00\"\n" "key Up +Shift-AppScreen : scrollLineUp\n" "key Prior +Shift-AppScreen : scrollPageUp\n" "key Down +Shift-AppScreen : scrollLineDown\n" "key Next +Shift-AppScreen : scrollPageDown\n" -"key ScrollLock : scrollLock\n" +"key ScrollLock : scrollLock\n" "\0" diff -r 940c1b6e3453 -r 61701d1317a1 libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslatorMac.h --- a/libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslatorMac.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/qterminal/libqterminal/unix/ExtendedDefaultTranslatorMac.h Fri Jul 12 12:14:43 2019 -0400 @@ -1,53 +1,53 @@ "keyboard \"Default (XFree 4)\"" -"key Escape : \"\\x1b\"" -"key Tab -Shift : \"\\t\"\n" -"key Tab +Shift+Ansi : \"\\E[Z\"\n" -"key Tab +Shift-Ansi : \"\\t\"\n" -"key Backtab +Ansi : \"\\E[Z\"\n" -"key Backtab -Ansi : \"\\t\"\n" +"key Escape : \"\\x1b\"" +"key Tab -Shift : \"\\t\"\n" +"key Tab +Shift+Ansi : \"\\E[Z\"\n" +"key Tab +Shift-Ansi : \"\\t\"\n" +"key Backtab +Ansi : \"\\E[Z\"\n" +"key Backtab -Ansi : \"\\t\"\n" "key Return-Shift-NewLine : \"\\r\"\n" "key Return-Shift+NewLine : \"\\r\\n\"\n" -"key Return+Shift : \"\\EOM\"\n" -"key Backspace : \"\\x7f\"\n" -"key Meta +C: \"\\x03\"\n" -"key Up -Shift+Ansi-AppCuKeys : \"\\E[A\"\n" -"key Down -Shift+Ansi-AppCuKeys : \"\\E[B\"\n" -"key Right-Shift+Ansi-AppCuKeys : \"\\E[C\"\n" -"key Left -Shift+Ansi-AppCuKeys : \"\\E[D\"\n" -"key Up -Ansi : \"\\E[1;*A\"\n" -"key Down -Ansi : \"\\E[1;*B\"\n" -"key Right -Ansi : \"\\E[1;*C\"\n" -"key Left -Ansi : \"\\E[1;*D\"\n" +"key Return+Shift : \"\\EOM\"\n" +"key Backspace : \"\\x7f\"\n" +"key Meta +C : \"\\x03\"\n" +"key Up -Shift+Ansi-AppCuKeys : \"\\E[A\"\n" +"key Down -Shift+Ansi-AppCuKeys : \"\\E[B\"\n" +"key Right -Shift+Ansi-AppCuKeys : \"\\E[C\"\n" +"key Left -Shift+Ansi-AppCuKeys : \"\\E[D\"\n" +"key Up -Ansi : \"\\E[1;*A\"\n" +"key Down -Ansi : \"\\E[1;*B\"\n" +"key Right -Ansi : \"\\E[1;*C\"\n" +"key Left -Ansi : \"\\E[1;*D\"\n" "key Enter+NewLine : \"\\r\\n\"\n" "key Enter-NewLine : \"\\r\"\n" -"key Home -AnyMod -AppCuKeys : \"\\E[H\"\n" -"key End -AnyMod -AppCuKeys : \"\\E[F\"\n" -"key Home -AnyMod +AppCuKeys : \"\\EOH\"\n" -"key End -AnyMod +AppCuKeys : \"\\EOF\"\n" -"key Home +AnyMod : \"\\E[1;*H\"\n" -"key End +AnyMod : \"\\E[1;*F\"\n" -"key Insert -AnyMod : \"\\E[2~\"\n" -"key Delete -AnyMod : \"\\E[3~\"\n" -"key Insert +AnyMod : \"\\E[2;*~\"\n" -"key Delete +AnyMod : \"\\E[3;*~\"\n" -"key Prior -Shift-AnyMod : \"\\E[5~\"\n" -"key Next -Shift-AnyMod : \"\\E[6~\"\n" -"key Prior -Shift+AnyMod : \"\\E[5;*~\"\n" -"key Next -Shift+AnyMod : \"\\E[6;*~\"\n" -"key F1 -AnyMod : \"\\EOP\"\n" -"key F2 -AnyMod : \"\\EOQ\"\n" -"key F3 -AnyMod : \"\\EOR\"\n" -"key F4 -AnyMod : \"\\EOS\"\n" -"key F5 -AnyMod : \"\\EOT\"\n" -"key F6 -AnyMod : \"\\EOU\"\n" -"key F7 -AnyMod : \"\\EOV\"\n" -"key F8 -AnyMod : \"\\EOW\"\n" -"key F9 -AnyMod : \"\\EOX\"\n" -"key F10 -AnyMod : \"\\EOY\"\n" -"key Space +Control : \"\\x00\"\n" -"key Up +Shift-AppScreen : scrollLineUp\n" -"key Prior +Shift-AppScreen : scrollPageUp\n" -"key Down +Shift-AppScreen : scrollLineDown\n" -"key Next +Shift-AppScreen : scrollPageDown\n" -"key ScrollLock : scrollLock\n" +"key Home -AnyMod -AppCuKeys : \"\\E[H\"\n" +"key End -AnyMod -AppCuKeys : \"\\E[F\"\n" +"key Home -AnyMod +AppCuKeys : \"\\EOH\"\n" +"key End -AnyMod +AppCuKeys : \"\\EOF\"\n" +"key Home +AnyMod : \"\\E[1;*H\"\n" +"key End +AnyMod : \"\\E[1;*F\"\n" +"key Insert -AnyMod : \"\\E[2~\"\n" +"key Delete -AnyMod : \"\\E[3~\"\n" +"key Insert +AnyMod : \"\\E[2;*~\"\n" +"key Delete +AnyMod : \"\\E[3;*~\"\n" +"key Prior -Shift-AnyMod : \"\\E[5~\"\n" +"key Next -Shift-AnyMod : \"\\E[6~\"\n" +"key Prior -Shift+AnyMod : \"\\E[5;*~\"\n" +"key Next -Shift+AnyMod : \"\\E[6;*~\"\n" +"key F1 -AnyMod : \"\\EOP\"\n" +"key F2 -AnyMod : \"\\EOQ\"\n" +"key F3 -AnyMod : \"\\EOR\"\n" +"key F4 -AnyMod : \"\\EOS\"\n" +"key F5 -AnyMod : \"\\EOT\"\n" +"key F6 -AnyMod : \"\\EOU\"\n" +"key F7 -AnyMod : \"\\EOV\"\n" +"key F8 -AnyMod : \"\\EOW\"\n" +"key F9 -AnyMod : \"\\EOX\"\n" +"key F10 -AnyMod : \"\\EOY\"\n" +"key Space +Control : \"\\x00\"\n" +"key Up +Shift-AppScreen : scrollLineUp\n" +"key Prior +Shift-AppScreen : scrollPageUp\n" +"key Down +Shift-AppScreen : scrollLineDown\n" +"key Next +Shift-AppScreen : scrollPageDown\n" +"key ScrollLock : scrollLock\n" "\0" diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/documentation.cc --- a/libgui/src/documentation.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/documentation.cc Fri Jul 12 12:14:43 2019 -0400 @@ -133,7 +133,7 @@ QLabel *find_label = new QLabel (tr ("Find:"), find_footer); m_find_line_edit = new QLineEdit (find_footer); connect (m_find_line_edit, SIGNAL (returnPressed (void)), - this, SLOT(find_forward (void))); + this, SLOT(find (void))); connect (m_find_line_edit, SIGNAL (textEdited (const QString&)), this, SLOT(find_forward_from_anchor (const QString&))); QToolButton *forward_button = new QToolButton (find_footer); @@ -141,7 +141,7 @@ forward_button->setToolTip (tr ("Search forward")); forward_button->setIcon (resource_manager::icon ("go-down")); connect (forward_button, SIGNAL (pressed (void)), - this, SLOT(find_forward (void))); + this, SLOT(find (void))); QToolButton *backward_button = new QToolButton (find_footer); backward_button->setText (tr ("Search backward")); backward_button->setToolTip (tr ("Search backward")); @@ -166,7 +166,7 @@ m_findnext_shortcut->setContext (Qt::WidgetWithChildrenShortcut); connect (m_findnext_shortcut, SIGNAL (activated (void)), - this, SLOT(find_forward (void))); + this, SLOT(find (void))); m_findprev_shortcut->setContext (Qt::WidgetWithChildrenShortcut); connect (m_findprev_shortcut, SIGNAL (activated (void)), this, SLOT(find_backward (void))); @@ -690,21 +690,32 @@ m_filter->setCurrentIndex (0); } - void documentation::find_forward (void) + void documentation::find_backward (void) + { + find (true); + } + + void documentation::find (bool backward) { if (! m_help_engine) return; - m_doc_browser->find (m_find_line_edit->text ()); - record_anchor_position (); - } + QTextDocument::FindFlags find_flags = 0; + if (backward) + find_flags = QTextDocument::FindBackward; - void documentation::find_backward (void) - { - if (! m_help_engine) - return; + if (! m_doc_browser->find (m_find_line_edit->text (), find_flags)) + { + // Nothing was found, restart search from the begin or end of text + QTextCursor textcur = m_doc_browser->textCursor (); + if (backward) + textcur.movePosition (QTextCursor::End); + else + textcur.movePosition (QTextCursor::Start); + m_doc_browser->setTextCursor (textcur); + m_doc_browser->find (m_find_line_edit->text (), find_flags); + } - m_doc_browser->find (m_find_line_edit->text (), QTextDocument::FindBackward); record_anchor_position (); } @@ -713,10 +724,18 @@ if (! m_help_engine) return; + // Search from the current position QTextCursor textcur = m_doc_browser->textCursor (); textcur.setPosition (m_search_anchor_position); m_doc_browser->setTextCursor (textcur); - m_doc_browser->find (text); + + if (! m_doc_browser->find (text)) + { + // Nothing was found, restart search from the beginning + textcur.movePosition (QTextCursor::Start); + m_doc_browser->setTextCursor (textcur); + m_doc_browser->find (text); + } } void documentation::record_anchor_position (void) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/documentation.h --- a/libgui/src/documentation.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/documentation.h Fri Jul 12 12:14:43 2019 -0400 @@ -115,7 +115,7 @@ void global_search_finished (int hits); void filter_update (const QString& expression); void filter_update_history (void); - void find_forward (void); + void find (bool backward = false); void find_backward (void); void find_forward_from_anchor (const QString& text); void record_anchor_position (void); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/files-dock-widget.cc --- a/libgui/src/files-dock-widget.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/files-dock-widget.cc Fri Jul 12 12:14:43 2019 -0400 @@ -671,7 +671,8 @@ // editor: close old emit file_remove_signal (old_name, new_name); // Do the renaming - bool st = path.rename (old_name, new_name); + QFile f (old_name); // Must use QFile, not QDir (bug #56298) + bool st = f.rename (new_name); // editor: load new/old file depending on success emit file_renamed_signal (st); // Clear cache of file browser diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/gui-preferences.h --- a/libgui/src/gui-preferences.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/gui-preferences.h Fri Jul 12 12:14:43 2019 -0400 @@ -28,8 +28,9 @@ //#endif #include +#include +#include #include -#include // Structure for the definition of pairs: key and default value @@ -98,6 +99,14 @@ const gui_pref ed_session_lines ("editor/saved_session_lines", QVariant (QStringList ())); +// Tabs +const QStringList ed_tab_position_names ( + QStringList () << QT_TRANSLATE_NOOP ("file_editor::file_editor","Top") + << QT_TRANSLATE_NOOP ("file_editor::file_editor","Bottom") + << QT_TRANSLATE_NOOP ("file_editor::file_editor","Left") + << QT_TRANSLATE_NOOP ("file_editor::file_editor","Right")); +const gui_pref ed_tab_position ("editor/tab_position", + QVariant (QTabWidget::North)); // File handling const gui_pref ed_show_dbg_file ("editor/show_dbg_file", QVariant (true)); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/history-dock-widget.cc --- a/libgui/src/history-dock-widget.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/history-dock-widget.cc Fri Jul 12 12:14:43 2019 -0400 @@ -34,6 +34,7 @@ #include "error.h" #include "resource-manager.h" +#include "gui-preferences.h" #include "cmd-hist.h" @@ -340,4 +341,18 @@ m_history_list_view->setTextElideMode (Qt::ElideRight); } + + void history_dock_widget::notice_settings (const QSettings *settings) + { + QFont font = QFont (); + + font.setStyleHint (QFont::TypeWriter); + QString default_font = settings->value (global_mono_font.key, global_mono_font.def).toString (); + + font.setFamily (settings->value (cs_font.key, default_font).toString ()); + font.setPointSize (settings->value ("terminal/fontSize", 10).toInt ()); + + m_history_list_view->setFont (font); + } + } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/history-dock-widget.h --- a/libgui/src/history-dock-widget.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/history-dock-widget.h Fri Jul 12 12:14:43 2019 -0400 @@ -50,6 +50,7 @@ void append_history (const QString& hist_entry); void clear_history (void); void save_settings (void); + void notice_settings (const QSettings *); signals: diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/bottom_left_corner.png Binary file libgui/src/icons/bottom_left_corner.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/bottom_right_corner.png Binary file libgui/src/icons/bottom_right_corner.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/bottom_side.png Binary file libgui/src/icons/bottom_side.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/circle.png Binary file libgui/src/icons/circle.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/circle.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/circle.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/cross.png Binary file libgui/src/icons/cross.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-axes.png Binary file libgui/src/icons/figure-axes.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-axes.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-axes.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-grid.png Binary file libgui/src/icons/figure-grid.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-grid.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-grid.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-pan.png Binary file libgui/src/icons/figure-pan.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-pan.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-pan.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,971 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + view + refresh + reload + reboot + + + + + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-rotate.png Binary file libgui/src/icons/figure-rotate.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-rotate.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-rotate.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,441 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + View Refresh + + + reload + refresh + view + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-text.png Binary file libgui/src/icons/figure-text.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-text.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-text.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,639 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Generic font + + + Andreas Nilsson + + + + + the oxygen guys + + + + http://tango-project.org + + + font + type + letter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-in.png Binary file libgui/src/icons/figure-zoom-in.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-in.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-zoom-in.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,1183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Martin Ruskov + + + http://commons.wikimedia.org/wiki/Tango_icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-original.png Binary file libgui/src/icons/figure-zoom-original.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-original.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-zoom-original.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + 1 + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-out.png Binary file libgui/src/icons/figure-zoom-out.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/figure-zoom-out.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/figure-zoom-out.svg Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,1180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Martin Ruskov + + + http://commons.wikimedia.org/wiki/Tango_icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/fleur.png Binary file libgui/src/icons/fleur.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/hand2.png Binary file libgui/src/icons/hand2.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/icons_license --- a/libgui/src/icons/icons_license Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/icons/icons_license Fri Jul 12 12:14:43 2019 -0400 @@ -33,6 +33,11 @@ edit-paste.svg edit-redo.svg edit-undo.svg +figure-rotate.svg (original file view-refresh.svg) +figure-text.svg (original file font-x-generic.svg) +figure-zoom-in.svg (original file view-zoom-in.svg) +figure-zoom-original.svg (original file view-zoom-original.svg) +figure-zoom-out.svg (original file view-zoom-out.svg) folder.svg folder-new.svg go-down.svg @@ -45,12 +50,15 @@ preferences-system.svg user-home.svg view-refresh.svg +view-refresh.svg =========================================== Icons created by the Octave developers with elements from the Tango theme =========================================== +figure-axes.svg +figure-grid.svg logo.png widget-close.svg widget-dock.svg @@ -59,6 +67,7 @@ Icons with elements from the Tango theme =========================================== +figure-pan.svg graphic_logo_DocumentationDockWidget.svg graphic_logo_FileEditor.svg graphic_logo_FilesDockWidget.svg @@ -87,3 +96,26 @@ db-stop.svg plot-xy-curve.svg system-run.svg + +Cursors from the standard DMZ theme +=================================== + +https://github.com/GalliumOS/dmz-cursor-theme/tree/master/DMZ-White + +bottom_left_corner.png +bottom_right_corner.png +bottom_side.png +cross.png +fleur.png +hand2.png +left_side.png +right_side.png +top_left_corner.png +top_right_corner.png +top_side.png + +Cursor created by the Octave developers +with elements from the DMZ theme: + +circle.png +circle.svg \ No newline at end of file diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/left_side.png Binary file libgui/src/icons/left_side.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/right_side.png Binary file libgui/src/icons/right_side.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/top_left_corner.png Binary file libgui/src/icons/top_left_corner.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/top_right_corner.png Binary file libgui/src/icons/top_right_corner.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/icons/top_side.png Binary file libgui/src/icons/top_side.png has changed diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/interpreter-qobject.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/interpreter-qobject.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,114 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "interpreter-qobject.h" +#include "octave-qobject.h" +#include "octave-qt-link.h" +#include "qt-application.h" + +#include "input.h" +#include "interpreter.h" + +namespace octave +{ + interpreter_qobject::interpreter_qobject (base_qobject *oct_qobj) + : QObject (), m_octave_qobject (oct_qobj), + m_qt_link (new octave_qt_link ()) + { + octave_link::connect_link (m_qt_link); + + connect (m_qt_link, SIGNAL (confirm_shutdown_signal (void)), + m_octave_qobject, SLOT (confirm_shutdown_octave (void))); + + connect (m_qt_link, + SIGNAL (copy_image_to_clipboard_signal (const QString&, bool)), + m_octave_qobject, + SLOT (copy_image_to_clipboard (const QString&, bool))); + } + + void interpreter_qobject::execute (void) + { + // The Octave application context owns the interpreter. + + qt_application& app_context = m_octave_qobject->app_context (); + + interpreter& interp = app_context.create_interpreter (); + + int exit_status = 0; + + try + { + // Final initialization. + + interp.initialize (); + + if (app_context.start_gui_p ()) + { + input_system& input_sys = interp.get_input_system (); + + input_sys.PS1 (">> "); + input_sys.PS2 (""); + } + + if (interp.initialized ()) + { + // The interpreter should be completely ready at this point so let + // the GUI know. + + emit octave_ready_signal (); + + // Start executing commands in the command window. + + exit_status = interp.execute (); + } + } + catch (const exit_exception& ex) + { + exit_status = ex.exit_status (); + } + + // Whether or not initialization succeeds we need to clean up the + // interpreter once we are done with it. + + app_context.delete_interpreter (); + + emit octave_finished_signal (exit_status); + } + + void interpreter_qobject::confirm_shutdown (bool closenow) + { + // Wait for link thread to go to sleep state. + m_qt_link->lock (); + + m_qt_link->shutdown_confirmation (closenow); + + m_qt_link->unlock (); + + // Awake the worker thread so that it continues shutting down (or not). + m_qt_link->wake_all (); + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/interpreter-qobject.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/interpreter-qobject.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,67 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_interpreter_qobject_h) +#define octave_interpreter_qobject_h 1 + +#include + +namespace octave +{ + class base_qobject; + class octave_qt_link; + + class interpreter_qobject : public QObject + { + Q_OBJECT + + public: + + interpreter_qobject (base_qobject *oct_qobj); + + ~interpreter_qobject (void) = default; + + octave_qt_link * qt_link (void) { return m_qt_link; } + + void confirm_shutdown (bool closenow); + + signals: + + void octave_ready_signal (void); + void octave_finished_signal (int); + + public slots: + + //! Initialize and execute the octave interpreter. + + void execute (void); + + private: + + base_qobject *m_octave_qobject; + + octave_qt_link *m_qt_link; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/file-editor-interface.h --- a/libgui/src/m-editor/file-editor-interface.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/file-editor-interface.h Fri Jul 12 12:14:43 2019 -0400 @@ -73,6 +73,8 @@ public slots: + virtual void toplevel_change (bool) = 0; + virtual void handle_file_remove (const QString& o, const QString& n) = 0; virtual void request_new_file (const QString& command = QString ()) = 0; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/file-editor-tab.cc --- a/libgui/src/m-editor/file-editor-tab.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/file-editor-tab.cc Fri Jul 12 12:14:43 2019 -0400 @@ -72,7 +72,6 @@ #include "uniconv-wrappers.h" #include "bp-table.h" -#include "call-stack.h" #include "interpreter-private.h" #include "interpreter.h" #include "oct-map.h" @@ -113,6 +112,9 @@ _bp_conditions.clear (); m_bp_restore_count = 0; + m_breakpoint_info.remove_next = false; + m_breakpoint_info.remove_line = -1; + // Initialize last modification date to now m_last_modified = QDateTime::currentDateTimeUtc(); @@ -163,7 +165,6 @@ // Leave the find dialog box out of memory until requested. _find_dialog = nullptr; - _find_dialog_is_visible = false; // symbols _edit_area->setMarginType (1, QsciScintilla::SymbolMargin); @@ -210,6 +211,7 @@ edit_area_layout->addWidget (_edit_area); edit_area_layout->addWidget (_status_bar); edit_area_layout->setMargin (0); + edit_area_layout->setSpacing (0); setLayout (edit_area_layout); // connect modified signal @@ -391,15 +393,23 @@ &ok); if (ok) // If cancel, don't change breakpoint condition. { + interpreter& interp + = __get_interpreter__ ("handle_context_menu_break_condition"); + + error_system& es = interp.get_error_system (); + try { // Suppress error messages on the console. unwind_protect frame; - frame.protect_var (buffer_error_messages); - buffer_error_messages++; - - bp_table& bptab - = __get_bp_table__ ("handle_context_menu_break_condition"); + + int bem = es.buffer_error_messages (); + frame.add_method (es, &error_system::set_buffer_error_messages, bem); + + es.buffer_error_messages (bem + 1); + + tree_evaluator& tw = interp.get_evaluator (); + bp_table& bptab = tw.get_bp_table (); bptab.condition_valid (new_condition.toStdString ()); valid = true; @@ -413,7 +423,7 @@ } // In case we repeat, set new prompt. - prompt = "ERROR: " + last_error_message () + "\n\ndbstop if"; + prompt = "ERROR: " + es.last_error_message () + "\n\ndbstop if"; cond = new_condition; } else @@ -618,9 +628,8 @@ else { // Otherwise, delete the newly created lexer and - // use the old, exisiting one - if (lexer) - delete lexer; + // use the old, existing one. + delete lexer; } } @@ -639,23 +648,33 @@ bool update_apis = false; // flag, whether update of apis files - // get path to prepared api info + // Get path to prepared api info (cache). Temporarily set the + // application name to 'octave' instead of 'GNU Octave' name for + // not having blanks in the path. + QString tmp_app_name = QCoreApplication::applicationName (); + QCoreApplication::setApplicationName ("octave"); // Set new name + #if defined (HAVE_QSTANDARDPATHS) - QString prep_apis_path - = QStandardPaths::writableLocation (QStandardPaths::HomeLocation) - + "/.config/octave/" + QString (OCTAVE_VERSION) + "/qsci/"; + QString local_data_path + = QStandardPaths::writableLocation (QStandardPaths::CacheLocation); #else - QString prep_apis_path - = QDesktopServices::storageLocation (QDesktopServices::HomeLocation) - + "/.config/octave/" + QString (OCTAVE_VERSION) + "/qsci/"; + QString local_data_path + = QDesktopServices::storageLocation (QDesktopServices::CacheLocation); #endif + QCoreApplication::setApplicationName ("octave"); // Set temp. name + + QString prep_apis_path = local_data_path + "/" + + QString (OCTAVE_VERSION) + "/qsci/"; + // get settings which infos are used for octave bool octave_builtins = settings->value ( "editor/codeCompletion_octave_builtins", true).toBool (); bool octave_functions = settings->value ( "editor/codeCompletion_octave_functions", true).toBool (); + QCoreApplication::setApplicationName (tmp_app_name); // Restore name + if (_is_octave_file) { // Keywords and Builtins do not change, these informations can be @@ -843,6 +862,31 @@ _lexer_apis->savePrepared (_prep_apis_file); } + // Slot for editors signal is its toplevel state has changed + void file_editor_tab::handle_toplevel_changed (bool) + { + // The find dialog has to be re-created since making the editor + // floating or docked obviously changes the parent/child relation + // of the find dialog + if (_find_dialog == nullptr) + return; + else + { + if (_find_dialog->isVisible ()) + { + _find_dialog->save_data (&m_find_dlg_data); // Save current data + delete _find_dialog; + _find_dialog = nullptr; + + find_create (); // Create new dialog + + _find_dialog->restore_data (&m_find_dlg_data); // Restore data + + _edit_area->setFocus (); + } + } + } + // slot for fetab_set_focus: sets the focus to the current edit area void file_editor_tab::set_focus (const QWidget *ID) { @@ -916,7 +960,7 @@ delete printer; } - void file_editor_tab::run_file (const QWidget *ID) + void file_editor_tab::run_file (const QWidget *ID, bool step_into) { if (ID != this) return; @@ -928,6 +972,22 @@ return; // still invalid filename: "save as" was cancelled } + if (step_into) + { + // Get current first breakpoint and set breakpoint waiting for + // the returned line number. Store whether to remove this breakpoint + // afterwards. + int first_bp_line + = _edit_area->markerFindNext (0, (1 << marker::breakpoint)) + 1; + + // Set flag for storing the line number of the breakpoint + m_breakpoint_info.remove_next = true; + m_breakpoint_info.do_not_remove_line = first_bp_line; + + // Add breakpoint, storing its line number + handle_request_add_breakpoint (1, QString ()); + } + QFileInfo info (_file_name); emit run_file_signal (info); } @@ -1014,7 +1074,16 @@ { bp_table& bptab = __get_bp_table__ ("octave_qt_link::file_in_path"); - bptab.add_breakpoint (info.function_name, line_info, info.condition); + bp_table::intmap bpmap + = bptab.add_breakpoint (info.function_name, "", line_info, info.condition); + + // Store some info breakpoint + if (m_breakpoint_info.remove_next && (bpmap.size() > 0)) + { + bp_table::intmap::iterator bp_it = bpmap.begin(); + m_breakpoint_info.remove_line = bp_it->second; + m_breakpoint_info.remove_next = false; + } } } @@ -1251,15 +1320,36 @@ { // Find dialog is going to hide. Save location of window for // when it is reshown. - _find_dialog_geometry = _find_dialog->geometry (); - _find_dialog_is_visible = false; + m_find_dlg_data.geometry = _find_dialog->geometry (); + m_find_dlg_data.is_visible = false; } + // Slot for initially creating and showing the find dialog void file_editor_tab::find (const QWidget *ID, QList fetab_actions) { if (ID != this) return; + m_find_dlg_data.actions = fetab_actions.mid (0,2); + + // Create the dialog + find_create (); + + // Since find_create shows the dialog without activating the widget + // (which is reuqired in other cases) do this manually here + _find_dialog->activateWindow (); + + // Initiate search text from possible selection and save the initial + // data from the dialog on the defined structure + _find_dialog->init_search_text (); + _find_dialog->save_data (&m_find_dlg_data); + } + + // This methos creates the find dialog in way that is at first suitable + // for re-creating it after the toplevel of the editor has changed. + // The find dialog is initially creatied, activated and shown with find () + void file_editor_tab::find_create () + { // The find_dialog feature doesn't need a slot for return info. // Rather than Qt::DeleteOnClose, let the find feature hang about // in case it contains useful information like previous searches @@ -1270,8 +1360,8 @@ if (! _find_dialog) { _find_dialog = new find_dialog (_edit_area, - fetab_actions.mid (0,2), - qobject_cast (sender ())); + m_find_dlg_data.actions, + this); connect (_find_dialog, SIGNAL (finished (int)), this, SLOT (handle_find_dialog_finished (int))); @@ -1282,20 +1372,16 @@ _find_dialog, SLOT (find_prev ())); _find_dialog->setWindowModality (Qt::NonModal); - _find_dialog_geometry = _find_dialog->geometry (); } else if (! _find_dialog->isVisible ()) { - _find_dialog->setGeometry (_find_dialog_geometry); + _find_dialog->setGeometry (m_find_dlg_data.geometry); QPoint p = _find_dialog->pos (); _find_dialog->move (p.x ()+10, p.y ()+10); } + _find_dialog->setAttribute(Qt::WA_ShowWithoutActivating); _find_dialog->show (); - _find_dialog_is_visible = true; - _find_dialog->activateWindow (); - _find_dialog->init_search_text (); - } void file_editor_tab::find_next (const QWidget *ID) @@ -1604,10 +1690,7 @@ } } - if (modified) - emit file_name_changed (title.prepend ("* "), tooltip); - else - emit file_name_changed (title, tooltip); + emit file_name_changed (title, tooltip, modified); } void file_editor_tab::handle_copy_available (bool enableCopy) @@ -2026,13 +2109,7 @@ octave_value sym; try { - // FIXME: maybe we should be looking up functions directly - // instead of using a function that can also find variables? - - symbol_scope curr_scope - = __get_current_scope__ ("file_editor_tab::exit_debug_and_clear"); - - sym = curr_scope.find (base_name); + sym = symtab.find_user_function (base_name); } catch (const execution_exception& e) { @@ -2053,11 +2130,12 @@ // If this file is loaded, check that we aren't currently running it bool retval = true; + octave_idx_type curr_frame = -1; - size_t nskip = 0; - call_stack& cs - = __get_call_stack__ ("file_editor_tab::exit_debug_and_clear"); - octave_map stk = cs.backtrace (nskip, curr_frame, false); + tree_evaluator& tw + = __get_evaluator__ ("file_editor_tab::exit_debug_and_clear"); + octave_map stk = tw.backtrace (curr_frame, false); + Cell names = stk.contents ("name"); for (octave_idx_type i = names.numel () - 1; i >= 0; i--) { @@ -2077,8 +2155,8 @@ // Wait until dbquit has actually occurred while (names.numel () > i) { - octave::sleep (0.01); - stk = cs.backtrace (nskip, curr_frame, false); + sleep (0.01); + stk = tw.backtrace (curr_frame, false); names = stk.contents ("name"); } } @@ -2614,6 +2692,10 @@ _edit_area->setTabWidth (settings->value ("editor/tab_width",2).toInt ()); + m_ind_char_width = 1; + if (_edit_area->indentationsUseTabs ()) + m_ind_char_width = _edit_area->tabWidth (); + _edit_area->SendScintilla (QsciScintillaBase::SCI_SETHSCROLLBAR, settings->value ("editor/show_hscroll_bar",true).toBool ()); _edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTH,-1); @@ -2711,16 +2793,16 @@ { if (_find_dialog->isVisible ()) { - _find_dialog_geometry = _find_dialog->geometry (); + m_find_dlg_data.geometry = _find_dialog->geometry (); _find_dialog->hide (); } } return; } - if (_find_dialog && _find_dialog_is_visible) + if (_find_dialog && m_find_dlg_data.is_visible) { - _find_dialog->setGeometry (_find_dialog_geometry); + _find_dialog->setGeometry (m_find_dlg_data.geometry); QPoint p = _find_dialog->pos (); _find_dialog->move (p.x ()+10, p.y ()+10); _find_dialog->show (); @@ -2824,7 +2906,18 @@ } } else - dp = new marker (_edit_area, line, marker::debugger_position); + { + dp = new marker (_edit_area, line, marker::debugger_position); + + // In case of a not modified file we might have to remove + // a breakpoint here if we have stepped into the file + if (line == m_breakpoint_info.remove_line) + { + m_breakpoint_info.remove_line = -1; + if (line != m_breakpoint_info.do_not_remove_line) + handle_request_remove_breakpoint (line); + } + } connect (this, SIGNAL (remove_position_via_debugger_linenr (int)), dp, SLOT (handle_remove_via_original_linenr (int))); @@ -2952,7 +3045,8 @@ { // Obviously, we have a newline here if (_smart_indent || _auto_endif) - _edit_area->smart_indent (_smart_indent, _auto_endif, _line); + _edit_area->smart_indent (_smart_indent, _auto_endif, + _line, m_ind_char_width); } } @@ -3086,7 +3180,7 @@ // get cursor position after having found an occurrence _edit_area->getCursorPosition (&oline, &ocol); // mark the selection - _edit_area->show_selection_markers (oline, ocol, wlen); + _edit_area->show_selection_markers (oline, ocol-wlen, oline, ocol); // find next occurrence find_result_available = _edit_area->findNext (); @@ -3109,6 +3203,7 @@ QRegExp rxfun2 ("^[\t ]*function[\t ]+([^\\(]+)\\([^\\)]*\\)[\t ]*$"); QRegExp rxfun3 ("^[\t ]*function[^=]+=[\t ]*([^\\s]+)[\t ]*$"); QRegExp rxfun4 ("^[\t ]*function[\t ]+([^\\s]+)[\t ]*$"); + QRegExp rxfun5 ("^[\t ]*classdef[\t ]+([^\\s]+)[\t ]*$"); QStringList lines = _edit_area->text ().split ("\n"); @@ -3122,6 +3217,8 @@ return rxfun3.cap (1).remove (QRegExp ("[ \t]*")); else if (rxfun4.indexIn (lines.at (i)) != -1) return rxfun4.cap (1).remove (QRegExp ("[ \t]*")); + else if (rxfun5.indexIn (lines.at (i)) != -1) + return rxfun5.cap (1).remove (QRegExp ("[ \t]*")); } return QString (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/file-editor-tab.h --- a/libgui/src/m-editor/file-editor-tab.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/file-editor-tab.h Fri Jul 12 12:14:43 2019 -0400 @@ -84,6 +84,7 @@ // Simply transmit filename. void file_name_query (const QWidget *ID); + void handle_toplevel_changed (bool); void set_focus (const QWidget *ID); void set_current_directory (const QString& dir); void context_help (const QWidget *ID, bool); @@ -94,7 +95,7 @@ bool remove_on_success); void save_file_as (const QWidget *ID); void print_file (const QWidget *ID); - void run_file (const QWidget *ID); + void run_file (const QWidget *ID, bool step_into = false); void context_run (const QWidget *ID); void toggle_bookmark (const QWidget *ID); void next_bookmark (const QWidget *ID); @@ -154,7 +155,9 @@ signals: - void file_name_changed (const QString& fileName, const QString& toolTip); + void file_name_changed (const QString& fileName, + const QString& toolTip, + bool modified); void editor_state_changed (bool copy_available, bool is_octave_file); void set_focus_editor_signal (QWidget *); void tab_remove_request (void); @@ -244,6 +247,8 @@ std::string condition; }; + void find_create (void); + bool valid_file_name (const QString& file = QString ()); bool exit_debug_and_clear (const QString& full_name, const QString& base_name); @@ -299,16 +304,13 @@ bool _always_reload_changed_files; bool _smart_indent; int _auto_endif; + int m_ind_char_width; QFileSystemWatcher _file_system_watcher; QIntList _bp_lines; QStringList _bp_conditions; - find_dialog *_find_dialog; - bool _find_dialog_is_visible; - QRect _find_dialog_geometry; - QsciAPIs *_lexer_apis; QString _prep_apis_file; @@ -321,6 +323,17 @@ bool _lines_changed; bool _highlight_all_occurrences; int m_bp_restore_count; + + struct + { + bool remove_next; + int remove_line; + int do_not_remove_line; + } m_breakpoint_info; + + find_dialog *_find_dialog; + find_dialog::find_dialog_data m_find_dlg_data; + }; } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/file-editor.cc --- a/libgui/src/m-editor/file-editor.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/file-editor.cc Fri Jul 12 12:14:43 2019 -0400 @@ -66,6 +66,7 @@ this->setTabBar (bar); setTabsClosable (true); + setUsesScrollButtons (true); #if defined (HAVE_QTABWIDGET_SETMOVABLE) setMovable (true); #endif @@ -95,6 +96,9 @@ m_closed = false; m_no_focus = false; + m_copy_action_enabled = false; + m_undo_action_enabled = false; + construct (); // actions that should also be available in the find dialog @@ -129,10 +133,6 @@ m_undo_action = shared_actions.at (UNDO_ACTION); m_tool_bar->insertAction (m_redo_action,m_undo_action); m_edit_menu->insertAction (m_redo_action,m_undo_action); - // copy - m_copy_action = shared_actions.at (COPY_ACTION); - m_tool_bar->insertAction (m_cut_action,m_copy_action); - m_edit_menu->insertAction (m_cut_action,m_copy_action); // select all m_selectall_action = shared_actions.at (SELECTALL_ACTION); m_edit_menu->insertAction (m_find_action,m_selectall_action); @@ -142,6 +142,10 @@ m_tool_bar->insertAction (m_find_action,m_paste_action); m_edit_menu->insertAction (m_selectall_action,m_paste_action); m_edit_menu->insertSeparator (m_selectall_action); + // copy + m_copy_action = shared_actions.at (COPY_ACTION); + m_tool_bar->insertAction (m_paste_action,m_copy_action); + m_edit_menu->insertAction (m_paste_action,m_copy_action); // find files m_find_files_action = shared_actions.at (FIND_FILES_ACTION); m_edit_menu->insertAction (m_find_action, m_find_files_action); @@ -149,14 +153,20 @@ void file_editor::handle_enter_debug_mode (void) { - m_run_action->setEnabled (false); - m_run_action->setShortcut (QKeySequence ()); + QSettings *settings = resource_manager::get_settings (); + QString sc_run = settings->value ("shortcuts/editor_run:run_file").toString (); + QString sc_cont = settings->value ("shortcuts/main_debug:continue").toString (); + + if (sc_run == sc_cont) + m_run_action->setShortcut (QKeySequence ()); // prevent ambigous shortcuts + + m_run_action->setToolTip (tr ("Continue")); // update tool tip } void file_editor::handle_exit_debug_mode (void) { - m_run_action->setEnabled (true); shortcut_manager::set_shortcut (m_run_action, "editor_run:run_file"); + m_run_action->setToolTip (tr ("Save File and Run")); // update tool tip } void file_editor::check_actions (void) @@ -194,6 +204,8 @@ m_close_action->setEnabled (have_tabs); m_close_all_action->setEnabled (have_tabs); m_close_others_action->setEnabled (have_tabs && m_tab_widget->count () > 1); + + emit editor_tabs_changed_signal (have_tabs); } // empty_script determines whether we have to create an empty script @@ -340,10 +352,20 @@ // when editor loses focus, enable the actions, which are always active // in the main window due to missing info on selected text and undo actions - if (! enable && m_copy_action && m_undo_action) + if (m_copy_action && m_undo_action) { - m_copy_action->setEnabled (true); - m_undo_action->setEnabled (true); + if (enable) + { + m_copy_action->setEnabled (m_copy_action_enabled); + m_undo_action->setEnabled (m_undo_action_enabled); + } + else + { + m_copy_action_enabled = m_copy_action->isEnabled (); + m_undo_action_enabled = m_undo_action->isEnabled (); + m_copy_action->setEnabled (true); + m_undo_action->setEnabled (true); + } } } @@ -444,12 +466,9 @@ // pass a signal to. Hence, functionality is here. file_editor_tab *fileEditorTab = new file_editor_tab (m_ced); - if (fileEditorTab) - { - add_file_editor_tab (fileEditorTab, ""); // new tab with empty title - fileEditorTab->new_file (commands); // title is updated here - focus (); // focus editor and new tab - } + add_file_editor_tab (fileEditorTab, ""); // new tab with empty title + fileEditorTab->new_file (commands); // title is updated here + focus (); // focus editor and new tab } void file_editor::request_close_file (bool) @@ -542,7 +561,15 @@ void file_editor::request_run_file (bool) { - emit fetab_run_file (m_tab_widget->currentWidget ()); + if ((Fisdebugmode ())(0).is_true ()) + emit request_dbcont_signal (); + else + emit fetab_run_file (m_tab_widget->currentWidget ()); + } + + void file_editor::request_step_into_file () + { + emit fetab_run_file (m_tab_widget->currentWidget (), true); } void file_editor::request_context_run (bool) @@ -743,7 +770,8 @@ } void file_editor::handle_file_name_changed (const QString& fname, - const QString& tip) + const QString& tip, + bool modified) { QObject *fileEditorTab = sender (); if (fileEditorTab) @@ -754,6 +782,11 @@ { m_tab_widget->setTabText (i, fname); m_tab_widget->setTabToolTip (i, tip); + if (modified) + m_tab_widget->setTabIcon (i, + resource_manager::icon ("document-save")); + else + m_tab_widget->setTabIcon (i, QIcon ()); } } } @@ -820,6 +853,9 @@ setFocusProxy (m_tab_widget->currentWidget ()); } + + m_copy_action_enabled = m_copy_action->isEnabled (); + m_undo_action_enabled = m_undo_action->isEnabled (); } void file_editor::handle_mru_add_file (const QString& file_name, @@ -1008,7 +1044,7 @@ std::string ndir = new_name.toStdString (); std::string ofile = old.fileName ().toStdString (); f_data.new_file_name = QString::fromStdString ( - octave::sys::env::make_absolute (ofile, ndir)); + sys::env::make_absolute (ofile, ndir)); } else f_data.new_file_name = new_name; @@ -1066,23 +1102,64 @@ int icon_size = st->pixelMetric (global_icon_sizes[size_idx]); m_tool_bar->setIconSize (QSize (icon_size, icon_size)); + // Tab position + QTabWidget::TabPosition pos = static_cast( + settings->value (ed_tab_position.key, ed_tab_position.def).toInt ()); + m_tab_widget->setTabPosition (pos); + + // Update style sheet properties depending on position + QString width_str ("width"); + QString height_str ("height"); + if (pos == QTabWidget::West || pos == QTabWidget::East) + { + width_str = QString ("height"); + height_str = QString ("width"); + } + + // Min and max width for full path titles int tab_width_min = settings->value ("editor/notebook_tab_width_min", 160) .toInt (); int tab_width_max = settings->value ("editor/notebook_tab_width_max", 300) .toInt (); + // Get suitable height of a tab related to font and icon size + int height = 1.5*QFontMetrics (m_tab_widget->font ()).height (); + int is = 1.5*m_tab_widget->iconSize ().height (); + if (is > height) + height = is; + + // Style sheet for tab height + QString style_sheet = QString ("QTabBar::tab {max-" + height_str + ": %1px;}") + .arg (height); + + // Style sheet for tab height together with width if (settings->value ("editor/longWindowTitle", false).toBool ()) { - QString style_sheet = QString ("QTabBar::tab " - "{min-width: %1px; max-width: %2px;}") - .arg (tab_width_min).arg (tab_width_max); + style_sheet = QString ("QTabBar::tab " + " {max-" + height_str + ": %1px;" + " min-" + width_str + ": %2px;" + " max-" + width_str + ": %3px;}") + .arg (height).arg (tab_width_min).arg (tab_width_max); m_tab_widget->setElideMode (Qt::ElideLeft); - m_tab_widget->setStyleSheet (style_sheet); } else - m_tab_widget->setElideMode (Qt::ElideNone); - - m_tab_widget->setUsesScrollButtons (true); + { + m_tab_widget->setElideMode (Qt::ElideNone); + } + +#if defined (Q_OS_MAC) + // FIXME: This is a workaround for missing tab close buttons on MacOS + // in several Qt versions (https://bugreports.qt.io/browse/QTBUG-61092) + QString close_button_css ( + "QTabBar::close-button" + " { width: 6px; image: url(:/actions/icons/widget-close.png);}\n" + "QTabBar::close-button:hover" + " { background-color: #cccccc; }" + ); + style_sheet = style_sheet + close_button_css; +#endif + + m_tab_widget->setStyleSheet (style_sheet); bool show_it; show_it = settings->value ("editor/showLineNumbers",true).toBool (); @@ -1218,6 +1295,12 @@ } + void file_editor::toplevel_change (bool toplevel) + { + emit fetab_toplevel_changed (toplevel); + octave_dock_widget::toplevel_change (toplevel); + } + void file_editor::update_octave_directory (const QString& dir) { m_ced = dir; @@ -1325,103 +1408,100 @@ if (! fileEditorTab) fileEditorTab = new file_editor_tab (); - if (fileEditorTab) + fileEditorTab->set_encoding (encoding); + QString result = fileEditorTab->load_file (openFileName); + if (result == "") { - fileEditorTab->set_encoding (encoding); - QString result = fileEditorTab->load_file (openFileName); - if (result == "") + // Supply empty title then have the file_editor_tab update + // with full or short name. + if (! reusing) + add_file_editor_tab (fileEditorTab, "", index); + fileEditorTab->update_window_title (false); + // file already loaded, add file to mru list here + QFileInfo file_info = QFileInfo (openFileName); + handle_mru_add_file (file_info.canonicalFilePath (), + encoding); + + if (line > 0) { - // Supply empty title then have the file_editor_tab update - // with full or short name. - if (! reusing) - add_file_editor_tab (fileEditorTab, "", index); - fileEditorTab->update_window_title (false); - // file already loaded, add file to mru list here - QFileInfo file_info = QFileInfo (openFileName); - handle_mru_add_file (file_info.canonicalFilePath (), - encoding); - - if (line > 0) - { - if (insert) - emit fetab_goto_line (fileEditorTab, line); - - if (debug_pointer) - emit fetab_insert_debugger_pointer (fileEditorTab, - line); - if (breakpoint_marker) - emit fetab_do_breakpoint_marker (insert, fileEditorTab, - line, cond); - } + if (insert) + emit fetab_goto_line (fileEditorTab, line); + + if (debug_pointer) + emit fetab_insert_debugger_pointer (fileEditorTab, + line); + if (breakpoint_marker) + emit fetab_do_breakpoint_marker (insert, fileEditorTab, + line, cond); + } + } + else + { + delete fileEditorTab; + fileEditorTab = nullptr; + + if (QFile::exists (openFileName)) + { + // File not readable: + // create a NonModal message about error. + QMessageBox *msgBox + = new QMessageBox (QMessageBox::Critical, + tr ("Octave Editor"), + tr ("Could not open file\n%1\nfor read: %2."). + arg (openFileName).arg (result), + QMessageBox::Ok, this); + + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); } else { - delete fileEditorTab; - fileEditorTab = nullptr; - - if (QFile::exists (openFileName)) + // File does not exist, should it be created? + bool create_file = true; + QMessageBox *msgBox; + + if (! settings->value ("editor/create_new_file", false).toBool ()) { - // File not readable: - // create a NonModal message about error. - QMessageBox *msgBox - = new QMessageBox (QMessageBox::Critical, - tr ("Octave Editor"), - tr ("Could not open file\n%1\nfor read: %2."). - arg (openFileName).arg (result), - QMessageBox::Ok, this); - - msgBox->setWindowModality (Qt::NonModal); - msgBox->setAttribute (Qt::WA_DeleteOnClose); - msgBox->show (); + msgBox = new QMessageBox (QMessageBox::Question, + tr ("Octave Editor"), + tr ("File\n%1\ndoes not exist. " + "Do you want to create it?").arg (openFileName), + QMessageBox::NoButton,nullptr); + QPushButton *create_button = + msgBox->addButton (tr ("Create"), QMessageBox::YesRole); + msgBox->addButton (tr ("Cancel"), QMessageBox::RejectRole); + msgBox->setDefaultButton (create_button); + msgBox->exec (); + + QAbstractButton *clicked_button = msgBox->clickedButton (); + if (clicked_button != create_button) + create_file = false; + + delete msgBox; } - else + + if (create_file) { - // File does not exist, should it be created? - bool create_file = true; - QMessageBox *msgBox; - - if (! settings->value ("editor/create_new_file", false).toBool ()) + // create the file and call the editor again + QFile file (openFileName); + if (! file.open (QIODevice::WriteOnly)) { - msgBox = new QMessageBox (QMessageBox::Question, + // error opening the file + msgBox = new QMessageBox (QMessageBox::Critical, tr ("Octave Editor"), - tr ("File\n%1\ndoes not exist. " - "Do you want to create it?").arg (openFileName), - QMessageBox::NoButton,nullptr); - QPushButton *create_button = - msgBox->addButton (tr ("Create"), QMessageBox::YesRole); - msgBox->addButton (tr ("Cancel"), QMessageBox::RejectRole); - msgBox->setDefaultButton (create_button); - msgBox->exec (); - - QAbstractButton *clicked_button = msgBox->clickedButton (); - if (clicked_button != create_button) - create_file = false; - - delete msgBox; + tr ("Could not open file\n%1\nfor write: %2."). + arg (openFileName).arg (file.errorString ()), + QMessageBox::Ok, this); + + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); } - - if (create_file) + else { - // create the file and call the editor again - QFile file (openFileName); - if (! file.open (QIODevice::WriteOnly)) - { - // error opening the file - msgBox = new QMessageBox (QMessageBox::Critical, - tr ("Octave Editor"), - tr ("Could not open file\n%1\nfor write: %2."). - arg (openFileName).arg (file.errorString ()), - QMessageBox::Ok, this); - - msgBox->setWindowModality (Qt::NonModal); - msgBox->setAttribute (Qt::WA_DeleteOnClose); - msgBox->show (); - } - else - { - file.close (); - request_open_file (openFileName); - } + file.close (); + request_open_file (openFileName); } } } @@ -2024,7 +2104,7 @@ m_run_action = add_action (_run_menu, resource_manager::icon ("system-run"), - tr ("Save File and Run"), + tr ("Save File and Run / Continue"), SLOT (request_run_file (bool))); m_run_selection_action @@ -2084,8 +2164,8 @@ // m_undo_action: later via main window m_tool_bar->addAction (m_redo_action); m_tool_bar->addSeparator (); + m_tool_bar->addAction (m_cut_action); // m_copy_action: later via the main window - m_tool_bar->addAction (m_cut_action); // m_paste_action: later via the main window m_tool_bar->addAction (m_find_action); //m_tool_bar->addAction (m_find_next_action); @@ -2104,6 +2184,7 @@ vbox_layout->addWidget (m_tool_bar); vbox_layout->addWidget (m_tab_widget); vbox_layout->setMargin (0); + vbox_layout->setSpacing (0); editor_widget->setLayout (vbox_layout); setWidget (editor_widget); @@ -2119,6 +2200,9 @@ main_win (), SLOT (process_settings_dialog_request (const QString&))); + connect (this, SIGNAL (request_dbcont_signal (void)), + main_win (), SLOT (debug_continue (void))); + connect (m_mru_file_menu, SIGNAL (triggered (QAction *)), this, SLOT (request_mru_open_file (QAction *))); @@ -2160,9 +2244,9 @@ main_win (), SLOT (execute_command_in_terminal (const QString&))); // Signals from the file editor_tab - connect (f, SIGNAL (file_name_changed (const QString&, const QString&)), + connect (f, SIGNAL (file_name_changed (const QString&, const QString&, bool)), this, SLOT (handle_file_name_changed (const QString&, - const QString&))); + const QString&, bool))); connect (f, SIGNAL (editor_state_changed (bool, bool)), this, SLOT (handle_editor_state_changed (bool, bool))); @@ -2246,8 +2330,8 @@ connect (this, SIGNAL (fetab_print_file (const QWidget*)), f, SLOT (print_file (const QWidget*))); - connect (this, SIGNAL (fetab_run_file (const QWidget*)), - f, SLOT (run_file (const QWidget*))); + connect (this, SIGNAL (fetab_run_file (const QWidget*, bool)), + f, SLOT (run_file (const QWidget*, bool))); connect (this, SIGNAL (fetab_context_run (const QWidget*)), f, SLOT (context_run (const QWidget*))); @@ -2320,6 +2404,9 @@ connect (this, SIGNAL (fetab_set_focus (const QWidget*)), f, SLOT (set_focus (const QWidget*))); + connect (this, SIGNAL (fetab_toplevel_changed (bool)), + f, SLOT (handle_toplevel_changed (bool))); + connect (this, SIGNAL (fetab_insert_debugger_pointer (const QWidget*, int)), f, SLOT (insert_debugger_pointer (const QWidget*, int))); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/file-editor.h --- a/libgui/src/m-editor/file-editor.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/file-editor.h Fri Jul 12 12:14:43 2019 -0400 @@ -122,6 +122,7 @@ signals: + void fetab_toplevel_changed (bool); void fetab_settings_changed (const QSettings *settings); void fetab_change_request (const QWidget *ID); void fetab_file_name_query (const QWidget *ID); @@ -136,7 +137,7 @@ void fetab_save_file (const QWidget *ID); void fetab_save_file_as (const QWidget *ID); void fetab_print_file (const QWidget *ID); - void fetab_run_file (const QWidget *ID); + void fetab_run_file (const QWidget *ID, bool step_into = false); void fetab_context_run (const QWidget *ID); void fetab_toggle_bookmark (const QWidget *ID); void fetab_next_bookmark (const QWidget *ID); @@ -176,8 +177,13 @@ void request_open_file_external (const QString& file_name, int line); void file_loaded_signal (void); + void editor_tabs_changed_signal (bool); + void request_dbcont_signal (void); + public slots: + void toplevel_change (bool); + void focus (void); void set_focus (QWidget *fet); void enable_menu_shortcuts (bool); @@ -198,6 +204,7 @@ void request_save_file (bool); void request_save_file_as (bool); void request_run_file (bool); + void request_step_into_file (); void request_context_run (bool); void request_toggle_bookmark (bool); void request_next_bookmark (bool); @@ -242,7 +249,8 @@ void request_completion (bool); void handle_file_name_changed (const QString& fileName, - const QString& toolTip); + const QString& toolTip, + bool modified); void handle_tab_close_request (int index); void handle_tab_remove_request (void); void handle_add_filename_to_list (const QString& fileName, @@ -429,6 +437,9 @@ QAction *m_previous_breakpoint_action; QAction *m_remove_all_breakpoints_action; + bool m_copy_action_enabled; + bool m_undo_action_enabled; + QMenu *m_edit_menu; QMenu *m_edit_cmd_menu; QMenu *m_edit_fmt_menu; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/find-dialog.cc --- a/libgui/src/m-editor/find-dialog.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/find-dialog.cc Fri Jul 12 12:14:43 2019 -0400 @@ -80,9 +80,9 @@ namespace octave { - find_dialog::find_dialog (QsciScintilla *edit_area, + find_dialog::find_dialog (octave_qscintilla *edit_area, QList find_actions, QWidget *p) - : QDialog (p) + : QDialog (p), m_in_sel (false), m_sel_beg (-1), m_sel_end (-1) { setWindowTitle (tr ("Find and Replace")); setWindowIcon (QIcon (":/actions/icons/find.png")); @@ -120,14 +120,9 @@ _regex_check_box = new QCheckBox (tr ("Regular E&xpressions")); _backward_check_box = new QCheckBox (tr ("Search &backward")); _search_selection_check_box = new QCheckBox (tr ("Search se&lection")); -#if defined (HAVE_QSCI_FINDSELECTION) _search_selection_check_box->setCheckable (true); if (edit_area) _search_selection_check_box->setEnabled (edit_area->hasSelectedText ()); -#else - _search_selection_check_box->setCheckable (false); - _search_selection_check_box->setEnabled (false); -#endif _edit_area = edit_area; connect (_find_next_button, SIGNAL (clicked ()), @@ -147,12 +142,10 @@ connect (_search_line_edit, SIGNAL (textChanged (QString)), this, SLOT (handle_search_text_changed (QString))); -#if defined (HAVE_QSCI_FINDSELECTION) connect (_edit_area, SIGNAL (copyAvailable (bool)), this, SLOT (handle_selection_changed (bool))); connect (_search_selection_check_box, SIGNAL (stateChanged (int)), this, SLOT (handle_sel_search_changed (int))); -#endif QVBoxLayout *extension_layout = new QVBoxLayout (); extension_layout->setMargin (0); @@ -202,6 +195,39 @@ } + void find_dialog::save_data (find_dialog_data *fdlg_data) + { + fdlg_data->text = _search_line_edit->text (); + fdlg_data->replace_text = _replace_line_edit->text (); + fdlg_data->geometry = geometry (); + fdlg_data->is_visible = isVisible (); + fdlg_data->options = 0 + + _extension->isVisible () * FIND_DLG_MORE + + _case_check_box->isChecked () * FIND_DLG_CASE + + _from_start_check_box->isChecked () * FIND_DLG_START + + _wrap_check_box->isChecked () * FIND_DLG_WRAP + + _regex_check_box->isChecked () * FIND_DLG_REGX + + _whole_words_check_box->isChecked () * FIND_DLG_WORDS + + _backward_check_box->isChecked () * FIND_DLG_BACK + + _search_selection_check_box->isChecked () * FIND_DLG_SEL; + } + + void find_dialog::restore_data (const find_dialog_data* fdlg_data) + { + setGeometry (fdlg_data->geometry); + setVisible (fdlg_data->is_visible); + _search_line_edit->setText (fdlg_data->text); + _replace_line_edit->setText (fdlg_data->replace_text); + _extension->setVisible (FIND_DLG_MORE & fdlg_data->options); + _case_check_box->setChecked (FIND_DLG_CASE & fdlg_data->options); + _from_start_check_box->setChecked (FIND_DLG_START & fdlg_data->options); + _wrap_check_box->setChecked (FIND_DLG_WRAP & fdlg_data->options); + _regex_check_box->setChecked (FIND_DLG_REGX & fdlg_data->options); + _whole_words_check_box->setChecked (FIND_DLG_WORDS & fdlg_data->options); + _backward_check_box->setChecked (FIND_DLG_BACK & fdlg_data->options); + _search_selection_check_box->setChecked (FIND_DLG_SEL & fdlg_data->options); + } + // set text of "search from start" depending on backward search void find_dialog::handle_backward_search_changed (int backward) { @@ -218,17 +244,12 @@ _find_result_available = false; } -#if defined (HAVE_QSCI_FINDSELECTION) void find_dialog::handle_sel_search_changed (int selected) { _from_start_check_box->setEnabled (! selected); _find_result_available = false; } -#else - void find_dialog::handle_sel_search_changed (int /* selected */) { } -#endif -#if defined (HAVE_QSCI_FINDSELECTION) void find_dialog::handle_selection_changed (bool has_selected) { if (_rep_active) @@ -236,12 +257,7 @@ _search_selection_check_box->setEnabled (has_selected); _find_result_available = false; - if (! has_selected) - _search_selection_check_box->setChecked (false); } -#else - void find_dialog::handle_selection_changed (bool /* has_selected */) { } -#endif // initialize search text with selected text if this is in one single line void find_dialog::init_search_text (void) @@ -278,46 +294,84 @@ if (! _edit_area) return; - int line, col; - line = col = -1; + // line adn col: -1 means search starts at current position + int line = -1, col = -1; + bool do_wrap = _wrap_check_box->isChecked (); bool do_forward = forward; + // Initialize the selection begin and end if it is the first search + if (! _find_result_available) + { + if (_search_selection_check_box->isChecked () + && _edit_area->hasSelectedText ()) + { + int l1, c1, l2, c2; + _edit_area->getSelection (&l1, &c1, &l2, &c2); + + // Store the position of the selection + m_sel_beg = _edit_area->positionFromLineIndex (l1, c1); + m_sel_end = _edit_area->positionFromLineIndex (l2, c2); + m_in_sel = true; + } + else + m_in_sel = false; + } + + // Get the correct line/col for beginning the search if (_rep_all) { + // Replace All if (_rep_all == 1) { - line = 0; - col = 0; - } - do_wrap = false; - // The following line is a workaround for the issue that when replacing - // a text with a new one with different size within the selection, - // the selection is not updated leading to missing or extra replacements. - // This does not happen, when the selection is search backwards - do_forward = ! _search_selection_check_box->isChecked (); - } - else - { - if (_from_start_check_box->isChecked ()) - { - if (do_forward) + // Start at the beginning of file/sel if it is the first try + if (m_in_sel) + _edit_area->lineIndexFromPosition (m_sel_beg, &line, &col); + else { line = 0; col = 0; } + } + do_wrap = false; // Never wrap when replacing all + } + else + { + // Normal search (not replace all): calculate start position of + // search (in file or selection) + if (_from_start_check_box->isChecked () + || (m_in_sel && (! _find_result_available))) + { + // From the beginning or the end of file/sel + if (do_forward) + { + // From the beginning + if (m_in_sel) + _edit_area->lineIndexFromPosition (m_sel_beg, &line, &col); + else + { + line = 0; + col = 0; + } + } else { - line = _edit_area->lines () - 1; - col = _edit_area->text (line).length () - 1; - if (col == -1) - col = 0; + // From the end + if (m_in_sel) + _edit_area->lineIndexFromPosition (m_sel_end, &line, &col); + else + { + line = _edit_area->lines () - 1; + col = _edit_area->text (line).length () - 1; + if (col == -1) + col = 0; + } } } else if (! do_forward) { - // search from position before search characters text length - // if search backward on existing results, + // Start from where the cursor is. Fix QScintilla's cursor + // positioning _edit_area->getCursorPosition (&line,&col); if (_find_result_available && _edit_area->hasSelectedText ()) { @@ -325,54 +379,65 @@ currpos -= (_search_line_edit->text ().length ()); if (currpos < 0) currpos = 0; - _edit_area->lineIndexFromPosition (currpos, &line,&col); + _edit_area->lineIndexFromPosition (currpos, &line, &col); } } } - if (_edit_area->hasSelectedText () - && _search_selection_check_box->isChecked ()) - { -#if defined (HAVE_QSCI_FINDSELECTION) - if (_find_result_available) - _find_result_available = _edit_area->findNext (); - else - _find_result_available - = _edit_area->findFirstInSelection ( - _search_line_edit->text (), - _regex_check_box->isChecked (), - _case_check_box->isChecked (), - _whole_words_check_box->isChecked (), - do_forward, - true + // Do the search + _find_result_available = _edit_area->findFirst ( + _search_line_edit->text (), + _regex_check_box->isChecked (), + _case_check_box->isChecked (), + _whole_words_check_box->isChecked (), + do_wrap, + do_forward, + line,col, + true #if defined (HAVE_QSCI_VERSION_2_6_0) - , true + , true #endif - ); -#endif - } - else + ); + + if (_find_result_available) { - _find_result_available - = _edit_area->findFirst (_search_line_edit->text (), - _regex_check_box->isChecked (), - _case_check_box->isChecked (), - _whole_words_check_box->isChecked (), - do_wrap, - do_forward, - line,col, - true -#if defined (HAVE_QSCI_VERSION_2_6_0) - , true -#endif - ); + // Search successful: reset search-from-start box and check for + // the current selection + _from_start_check_box->setChecked (0); + + if (m_in_sel) + { + _edit_area->getCursorPosition (&line,&col); + int pos = _edit_area->positionFromLineIndex (line, col); + + int l1, c1, l2, c2; + _edit_area->lineIndexFromPosition (m_sel_beg, &l1, &c1); + _edit_area->lineIndexFromPosition (m_sel_end, &l2, &c2); + _edit_area->show_selection_markers (l1, c1, l2, c2); + + // Check if new start position is still wihtin the selection + _find_result_available = pos >= m_sel_beg && pos <= m_sel_end; + } } + // No more search hits + if (! _find_result_available) + { + if (m_in_sel) + { + // Restore real selection and remove marker for selection + int l1, c1, l2, c2; + _edit_area->lineIndexFromPosition (m_sel_beg, &l1, &c1); + _edit_area->lineIndexFromPosition (m_sel_end, &l2, &c2); + _edit_area->setSelection (l1, c1, l2, c2); + _edit_area->clear_selection_markers (); + } - if (_find_result_available) - _from_start_check_box->setChecked (0); - else if (! _rep_all) - no_matches_message (); + // Display message if not replace all + if (! _rep_all) + no_matches_message (); + } + } void find_dialog::do_replace (void) @@ -380,7 +445,16 @@ if (_edit_area) { _rep_active = true; // changes in selection not made by the user + _edit_area->replace (_replace_line_edit->text ()); + if (m_in_sel) + { + // Update the length of the selection + m_sel_end = m_sel_end + - _search_line_edit->text ().toUtf8 ().size () + + _replace_line_edit->text ().toUtf8 ().size (); + } + _rep_active = false; } } @@ -389,14 +463,7 @@ { if (_edit_area) { - // The following line is a workaround for the issue that when replacing - // a text with a new one with different size within the selection, - // the selection is not updated leading to missing or extra replacements. - // This does not happen, when the selection is search backwards - if (_search_selection_check_box->isChecked ()) - _backward_check_box->setChecked (true); - - // do the replace if we have selected text + // Do the replace if we have selected text if (_find_result_available && _edit_area->hasSelectedText ()) do_replace (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/find-dialog.h --- a/libgui/src/m-editor/find-dialog.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/find-dialog.h Fri Jul 12 12:14:43 2019 -0400 @@ -63,7 +63,8 @@ #define octave_find_dialog_h 1 #include -#include + +#include "octave-qscintilla.h" class QCheckBox; class QDialogButtonBox; @@ -77,10 +78,36 @@ class find_dialog : public QDialog { Q_OBJECT + public: - find_dialog (QsciScintilla *edit_area, QList find_actions, + + typedef struct + { + QList actions; + QString text; + QString replace_text; + QRect geometry; + bool is_visible; + int options; + } find_dialog_data; + + enum find_dialog_options + { + FIND_DLG_MORE = 1, + FIND_DLG_CASE = 2, + FIND_DLG_START = 4, + FIND_DLG_WRAP = 8, + FIND_DLG_REGX = 16, + FIND_DLG_WORDS = 32, + FIND_DLG_BACK = 64, + FIND_DLG_SEL = 128 + }; + + find_dialog (octave_qscintilla *edit_area, QList find_actions, QWidget *parent = nullptr); void init_search_text (void); + void save_data (find_dialog_data *fdlg_data); + void restore_data (const find_dialog_data *fdlg_data); private slots: void handle_sel_search_changed (int); @@ -118,10 +145,14 @@ QPushButton *_replace_all_button; QPushButton *_more_button; QWidget *_extension; - QsciScintilla *_edit_area; + octave_qscintilla *_edit_area; bool _find_result_available; int _rep_all; bool _rep_active; + + bool m_in_sel; + int m_sel_beg; + int m_sel_end; }; } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/octave-qscintilla.cc --- a/libgui/src/m-editor/octave-qscintilla.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/octave-qscintilla.cc Fri Jul 12 12:14:43 2019 -0400 @@ -65,45 +65,45 @@ // Used for testing the last word of an "if" etc. line, // or the first word of the following line. -static bool -is_end (const QString& candidate, const QString& opening) -{ - bool retval = false; - - if (opening == "do") // The only one that can't be ended by "end" - { - if (candidate == "until") - retval = true; - } - else - { - if (candidate == "end") - retval = true; - else - { - if (opening == "try") - { - if (candidate == "catch" || candidate == "end_try_catch") - retval = true; - } - else if (opening == "unwind_protect") - { - if (candidate == "unwind_protect_cleanup" - || candidate == "end_unwind_protect") - retval = true; - } - else if (candidate == "end" + opening) - retval = true; - else if (opening == "if" && candidate == "else") - retval = true; - } - } - - return retval; -} - namespace octave { + static bool + is_end (const QString& candidate, const QString& opening) + { + bool retval = false; + + if (opening == "do") // The only one that can't be ended by "end" + { + if (candidate == "until") + retval = true; + } + else + { + if (candidate == "end") + retval = true; + else + { + if (opening == "try") + { + if (candidate == "catch" || candidate == "end_try_catch") + retval = true; + } + else if (opening == "unwind_protect") + { + if (candidate == "unwind_protect_cleanup" + || candidate == "end_unwind_protect") + retval = true; + } + else if (candidate == "end" + opening) + retval = true; + else if (opening == "if" && candidate == "else") + retval = true; + } + } + + return retval; + } + octave_qscintilla::octave_qscintilla (QWidget *p) : QsciScintilla (p), m_word_at_cursor (), m_selection (), m_selection_replacement (), m_selection_line (-1), @@ -499,8 +499,8 @@ } // Do smart indendation after if, for, ... - void octave_qscintilla::smart_indent (bool do_smart_indent, - int do_auto_close, int line) + void octave_qscintilla::smart_indent (bool do_smart_indent, int do_auto_close, + int line, int ind_char_width) { QString prevline = text (line); @@ -534,7 +534,7 @@ { // Do smart indent in the current line (line+1) indent (line+1); - setCursorPosition (line+1, indentation (line) + indentationWidth ()); + setCursorPosition (line+1, indentation (line+1) / ind_char_width); } if (do_auto_close @@ -726,11 +726,12 @@ } } - void octave_qscintilla::show_selection_markers (int line, int col, int len) + void octave_qscintilla::show_selection_markers (int l1, int c1, int l2, int c2) { - fillIndicatorRange (line, col - len, line, col, m_indicator_id); + fillIndicatorRange (l1, c1, l2, c2, m_indicator_id); - markerAdd (line, marker::selection); + if (l1 == l2) + markerAdd (l1, marker::selection); } void octave_qscintilla::contextmenu_help (bool) @@ -965,9 +966,11 @@ // end* (until) (catch) if (linenr < lines () - 1) { - int offset = 1; + int offset = 2; // linenr is the old line, thus, linnr+1 is the + // new one and can not be taken into account size_t next_start; QString next_line; + do // find next non-blank line { next_line = text (linenr + offset++); @@ -975,8 +978,11 @@ } while (linenr + offset < lines () && next_start == std::string::npos); + if (next_start == std::string::npos) next_start = 0; + if (start == 0 && next_start == 0) + return; // bug #56160, don't add at 0 if (next_start > start) // more indented => don't add "end" return; if (next_start == start) // same => check if already is "end" @@ -1022,7 +1028,9 @@ next_line = "end" + first_word + "\n"; } - insertAt (QString (start, ' ') + next_line, linenr + 2, 0); + //insertAt (QString (start, ' ') + next_line, linenr + 2, 0); + insertAt (next_line, linenr + 2, 0); + setIndentation (linenr + 2, indentation (linenr)); } void octave_qscintilla::dragEnterEvent (QDragEnterEvent *e) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/m-editor/octave-qscintilla.h --- a/libgui/src/m-editor/octave-qscintilla.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/m-editor/octave-qscintilla.h Fri Jul 12 12:14:43 2019 -0400 @@ -63,13 +63,14 @@ QStringList comment_string (bool comment = true); int get_style (int pos = -1); int is_style_comment (int pos = -1); - void smart_indent (bool do_smart_indent, int do_auto_close, int line); + void smart_indent (bool do_smart_indent, int do_auto_close, + int line, int ind_char_width); void smart_indent_line_or_selected_text (int lineFrom, int lineTo); void set_word_selection (const QString& word = QString ()); - void show_selection_markers (int line, int col, int len); + void show_selection_markers (int l1, int c1, int l2, int c2); void set_selection_marker_color (const QColor& c); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/main-window.cc --- a/libgui/src/main-window.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/main-window.cc Fri Jul 12 12:14:43 2019 -0400 @@ -55,7 +55,10 @@ #if defined (HAVE_QSCINTILLA) # include "file-editor.h" #endif +#include "interpreter-qobject.h" #include "main-window.h" +#include "news-reader.h" +#include "octave-qobject.h" #include "settings-dialog.h" #include "shortcut-manager.h" #include "welcome-wizard.h" @@ -67,7 +70,6 @@ #include "url-transfer.h" #include "builtin-defun-decls.h" -#include "call-stack.h" #include "defaults.h" #include "defun.h" #include "interpreter-private.h" @@ -80,90 +82,22 @@ #include "utils.h" #include "version.h" -static octave::file_editor_interface * -create_default_editor (QWidget *p) -{ -#if defined (HAVE_QSCINTILLA) - return new octave::file_editor (p); -#else - octave_unused_parameter (p); - - return 0; -#endif -} - -// Disable all Qt messages by default. - -static void -#if defined (QTMESSAGEHANDLER_ACCEPTS_QMESSAGELOGCONTEXT) -message_handler (QtMsgType, const QMessageLogContext &, const QString &) -#else -message_handler (QtMsgType, const char *) -#endif -{ } - namespace octave { - octave_interpreter::octave_interpreter (gui_application& app_context) - : QObject (), m_app_context (app_context) - { } - - void octave_interpreter::execute (void) + static file_editor_interface * + create_default_editor (QWidget *p) { - // The application context owns the interpreter. - - interpreter& interp = m_app_context.create_interpreter (); - - int exit_status = 0; - - try - { - // Final initialization. - - interp.initialize (); - - if (m_app_context.start_gui_p ()) - { - input_system& input_sys = interp.get_input_system (); - - input_sys.PS1 (">> "); - input_sys.PS2 (""); - - tree_evaluator& tw = interp.get_evaluator (); - - tw.PS4 (""); - } - - if (interp.initialized ()) - { - // The interpreter should be completely ready at this point so let - // the GUI know. - - emit octave_ready_signal (); - - // Start executing commands in the command window. - - exit_status = interp.execute (); - } - } - catch (const exit_exception& ex) - { - exit_status = ex.exit_status (); - } - - // Whether or not initialization succeeds we need to clean up the - // interpreter once we are done with it. - - m_app_context.delete_interpreter (); - - emit octave_finished_signal (exit_status); +#if defined (HAVE_QSCINTILLA) + return new file_editor (p); +#else + octave_unused_parameter (p); + + return 0; +#endif } - main_window::main_window (octave_qt_app& oct_qt_app, - octave_qt_link *oct_qt_lnk) - : QMainWindow (), - m_qt_app (oct_qt_app.qt_app ()), m_octave_qt_link (oct_qt_lnk), - m_workspace_model (nullptr), + main_window::main_window (base_qobject& qobj) + : QMainWindow (), m_octave_qobj (qobj), m_workspace_model (nullptr), m_status_bar (nullptr), m_command_window (nullptr), m_history_window (nullptr), m_file_browser_window (nullptr), m_doc_browser_window (nullptr), m_editor_window (nullptr), @@ -179,7 +113,7 @@ if (resource_manager::is_first_run ()) { // Before wizard. - oct_qt_app.config_translators (); + qobj.config_translators (); welcome_wizard welcomeWizard; @@ -195,7 +129,7 @@ resource_manager::reload_settings (); // After settings. - oct_qt_app.config_translators (); + qobj.config_translators (); } resource_manager::update_network_settings (); @@ -229,7 +163,9 @@ QGuiApplication::setDesktopFileName ("org.octave.Octave.desktop"); #endif - m_default_style = m_qt_app->style ()->objectName (); + QApplication *qapp = m_octave_qobj.qapplication (); + + m_default_style = qapp->style ()->objectName (); QSettings *settings = resource_manager::get_settings (); @@ -279,10 +215,6 @@ main_window::~main_window (void) { - // Note that we don't delete m_main_thread here. That is handled by - // deleteLater slot that is called when the m_main_thread issues a - // finished signal. - // Destroy the terminal first so that STDERR stream is redirected back // to its original pipe to capture error messages at exit. @@ -495,8 +427,12 @@ void main_window::file_remove_proxy (const QString& o, const QString& n) { + interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj (); + + octave_qt_link *qt_link = interp_qobj->qt_link (); + // Wait for worker to suspend - m_octave_qt_link->lock (); + qt_link->lock (); // Close the file if opened #if defined (HAVE_QSCINTILLA) @@ -507,8 +443,8 @@ #endif // We are done: Unlock and wake the worker thread - m_octave_qt_link->unlock (); - m_octave_qt_link->wake_all (); + qt_link->unlock (); + qt_link->wake_all (); } void main_window::open_online_documentation_page (void) @@ -738,7 +674,11 @@ QStyle *new_style = QStyleFactory::create (preferred_style); if (new_style) - m_qt_app->setStyle (new_style); + { + QApplication *qapp = m_octave_qobj.qapplication (); + + qapp->setStyle (new_style); + } // the widget's icons (when floating) QString icon_set @@ -1016,7 +956,7 @@ m_debug_continue->setEnabled (false); m_debug_step_into->setEnabled (false); - m_debug_step_over->setEnabled (false); + m_debug_step_over->setEnabled (m_editor_has_tabs); m_debug_step_out->setEnabled (false); m_debug_quit->setEnabled (false); @@ -1040,9 +980,18 @@ void main_window::debug_step_over (void) { - octave_cmd_debug *cmd - = new octave_cmd_debug ("step", m_suppress_dbg_location); - queue_cmd (cmd); + if (m_debug_quit->isEnabled ()) + { + // We are in debug mode, just call dbstep + octave_cmd_debug *cmd + = new octave_cmd_debug ("step", m_suppress_dbg_location); + queue_cmd (cmd); + } + else + { + // Not in debug mode: "step into" the current editor file + emit step_into_file_signal (); + } } void main_window::debug_step_out (void) @@ -1707,17 +1656,21 @@ connect (m_workspace_model, SIGNAL (model_changed (void)), m_workspace_window, SLOT (handle_model_changed (void))); - connect (m_octave_qt_link, + interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj (); + + octave_qt_link *qt_link = interp_qobj->qt_link (); + + connect (qt_link, SIGNAL (edit_variable_signal (const QString&, const octave_value&)), this, SLOT (edit_variable (const QString&, const octave_value&))); - connect (m_octave_qt_link, SIGNAL (refresh_variable_editor_signal (void)), + connect (qt_link, SIGNAL (refresh_variable_editor_signal (void)), this, SLOT (refresh_variable_editor (void))); - connect (m_workspace_model, - SIGNAL (rename_variable (const QString&, const QString&)), + connect (m_workspace_window, + SIGNAL (rename_variable_signal (const QString&, const QString&)), this, SLOT (handle_rename_variable_request (const QString&, const QString&))); @@ -1773,6 +1726,12 @@ connect (this, SIGNAL (editor_focus_changed (bool)), m_editor_window, SLOT (enable_menu_shortcuts (bool))); + connect (this, SIGNAL (step_into_file_signal (void)), + m_editor_window, SLOT (request_step_into_file (void))); + + connect (m_editor_window, SIGNAL (editor_tabs_changed_signal (bool)), + this, SLOT (editor_tabs_changed (bool))); + connect (m_editor_window, SIGNAL (request_open_file_external (const QString&, int)), m_external_editor, @@ -1862,12 +1821,12 @@ m_editor_window, SLOT (handle_file_renamed (bool))); // Signals for removing/renaming files/dirs in the temrinal window - connect (m_octave_qt_link, SIGNAL (file_renamed_signal (bool)), + connect (qt_link, SIGNAL (file_renamed_signal (bool)), m_editor_window, SLOT (handle_file_renamed (bool))); #endif // Signals for removing/renaming files/dirs in the temrinal window - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (file_remove_signal (const QString&, const QString&)), this, SLOT (file_remove_proxy (const QString&, const QString&))); @@ -1879,88 +1838,92 @@ void main_window::construct_octave_qt_link (void) { - connect (m_octave_qt_link, + interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj (); + + octave_qt_link *qt_link = interp_qobj->qt_link (); + + connect (qt_link, SIGNAL (set_workspace_signal (bool, bool, const symbol_info_list&)), m_workspace_model, SLOT (set_workspace (bool, bool, const symbol_info_list&))); - connect (m_octave_qt_link, SIGNAL (clear_workspace_signal (void)), + connect (qt_link, SIGNAL (clear_workspace_signal (void)), m_workspace_model, SLOT (clear_workspace (void))); - connect (m_octave_qt_link, SIGNAL (change_directory_signal (QString)), + connect (qt_link, SIGNAL (change_directory_signal (QString)), this, SLOT (change_directory (QString))); - connect (m_octave_qt_link, SIGNAL (change_directory_signal (QString)), + connect (qt_link, SIGNAL (change_directory_signal (QString)), m_file_browser_window, SLOT (update_octave_directory (QString))); - connect (m_octave_qt_link, SIGNAL (change_directory_signal (QString)), + connect (qt_link, SIGNAL (change_directory_signal (QString)), m_editor_window, SLOT (update_octave_directory (QString))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (execute_command_in_terminal_signal (QString)), this, SLOT (execute_command_in_terminal (QString))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (set_history_signal (const QStringList&)), m_history_window, SLOT (set_history (const QStringList&))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (append_history_signal (const QString&)), m_history_window, SLOT (append_history (const QString&))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (clear_history_signal (void)), m_history_window, SLOT (clear_history (void))); - connect (m_octave_qt_link, SIGNAL (enter_debugger_signal (void)), + connect (qt_link, SIGNAL (enter_debugger_signal (void)), this, SLOT (handle_enter_debugger (void))); - connect (m_octave_qt_link, SIGNAL (exit_debugger_signal (void)), + connect (qt_link, SIGNAL (exit_debugger_signal (void)), this, SLOT (handle_exit_debugger (void))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (show_preferences_signal (void)), this, SLOT (process_settings_dialog_request (void))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (gui_preference_signal (const QString&, const QString&, QString*)), this, SLOT (gui_preference (const QString&, const QString&, QString*))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (edit_file_signal (const QString&)), m_active_editor, SLOT (handle_edit_file_request (const QString&))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (insert_debugger_pointer_signal (const QString&, int)), this, SLOT (handle_insert_debugger_pointer_request (const QString&, int))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (delete_debugger_pointer_signal (const QString&, int)), this, SLOT (handle_delete_debugger_pointer_request (const QString&, int))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (update_breakpoint_marker_signal (bool, const QString&, int, const QString&)), this, SLOT (handle_update_breakpoint_marker_request (bool, const QString&, int, const QString&))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (show_doc_signal (const QString &)), this, SLOT (handle_show_doc (const QString &))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (register_doc_signal (const QString &)), this, SLOT (handle_register_doc (const QString &))); - connect (m_octave_qt_link, + connect (qt_link, SIGNAL (unregister_doc_signal (const QString &)), this, SLOT (handle_unregister_doc (const QString &))); } @@ -2058,6 +2021,7 @@ file_menu->addSeparator (); m_exit_action = file_menu->addAction (tr ("Exit")); + m_exit_action->setMenuRole (QAction::QuitRole); m_exit_action->setShortcutContext (Qt::ApplicationShortcut); connect (m_open_action, SIGNAL (triggered (void)), @@ -2238,6 +2202,13 @@ SLOT (debug_quit (void))); } + void main_window::editor_tabs_changed (bool have_tabs) + { + // Set state of action which depend on the existance of editor tabs + m_editor_has_tabs = have_tabs; + m_debug_step_over->setEnabled (have_tabs); + } + QAction * main_window::construct_window_menu_item (QMenu *p, const QString& item, bool checkable, @@ -2468,8 +2439,12 @@ QSettings *settings = resource_manager::get_settings (); *read_value = settings->value (key).toString (); + interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj (); + + octave_qt_link *qt_link = interp_qobj->qt_link (); + // Wait for worker to suspend - m_octave_qt_link->lock (); + qt_link->lock (); // Some preferences need extra handling QString adjusted_value = gui_preference_adjust (key, value); @@ -2482,8 +2457,8 @@ } // We are done: Unlock and wake the worker thread - m_octave_qt_link->unlock (); - m_octave_qt_link->wake_all (); + qt_link->unlock (); + qt_link->wake_all (); } void main_window::rename_variable_callback (const main_window::name_pair& names) @@ -2497,10 +2472,10 @@ { scope.rename (names.first, names.second); - call_stack& cs - = __get_call_stack__ ("main_window::rename_variable_callback"); - - octave_link::set_workspace (true, cs.get_symbol_info ()); + tree_evaluator& tw + = __get_evaluator__ ("main_window::rename_variable_callback"); + + octave_link::set_workspace (true, tw.get_symbol_info ()); } // FIXME: if this action fails, do we need a way to display that info @@ -2541,13 +2516,13 @@ { // INTERPRETER THREAD - octave::feval ("open", ovl (file)); + feval ("open", ovl (file)); // Update the workspace since open.m may have loaded new variables. - call_stack& cs - = __get_call_stack__ ("main_window::open_any_callback"); - - octave_link::set_workspace (true, cs.get_symbol_info ()); + tree_evaluator& tw + = __get_evaluator__ ("main_window::open_any_callback"); + + octave_link::set_workspace (true, tw.get_symbol_info ()); } void main_window::clear_history_callback (void) @@ -2564,10 +2539,10 @@ { // INTERPRETER THREAD - call_stack& cs - = __get_call_stack__ ("main_window::force_refresh_workspace"); - - octave_link::set_workspace (true, cs.get_symbol_info (), false); + tree_evaluator& tw + = __get_evaluator__ ("main_window::force_refresh_workspace"); + + octave_link::set_workspace (true, tw.get_symbol_info (), false); } bool main_window::focus_console_after_command (void) @@ -2680,420 +2655,4 @@ list.append (static_cast (m_variable_editor_window)); return list; } - - void news_reader::process (void) - { - QString html_text; - - if (m_connect_to_web) - { - // Run this part in a separate thread so Octave can continue to - // run while we wait for the page to load. Then emit the signal - // to display it when we have the page contents. - - QString url = m_base_url + '/' + m_page; - std::ostringstream buf; - url_transfer octave_dot_org (url.toStdString (), buf); - - if (octave_dot_org.is_valid ()) - { - Array param; - octave_dot_org.http_get (param); - - if (octave_dot_org.good ()) - html_text = QString::fromStdString (buf.str ()); - } - - if (html_text.contains ("this-is-the-gnu-octave-community-news-page")) - { - if (m_serial >= 0) - { - QSettings *settings = resource_manager::get_settings (); - - if (settings) - { - settings->setValue ("news/last_time_checked", - QDateTime::currentDateTime ()); - - settings->sync (); - } - - QString tag ("community-news-page-serial="); - - int b = html_text.indexOf (tag); - - if (b) - { - b += tag.length (); - - int e = html_text.indexOf ("\n", b); - - QString tmp = html_text.mid (b, e-b); - - int curr_page_serial = tmp.toInt (); - - if (curr_page_serial > m_serial) - { - if (settings) - { - settings->setValue ("news/last_news_item", - curr_page_serial); - - settings->sync (); - } - } - else - return; - } - else - return; - } - } - else - html_text = QString - (tr ("\n" - "\n" - "

\n" - "Octave's community news source seems to be unavailable.\n" - "

\n" - "

\n" - "For the latest news, please check\n" - "https://octave.org/community-news.html\n" - "when you have a connection to the web (link opens in an external browser).\n" - "

\n" - "

\n" - "— The Octave Developers, ") + OCTAVE_RELEASE_DATE + "\n" - "

\n" - "\n" - "\n"); - } - else - html_text = QString - (tr ("\n" - "\n" - "

\n" - "Connecting to the web to display the latest Octave Community news has been disabled.\n" - "

\n" - "

\n" - "For the latest news, please check\n" - "https://octave.org/community-news.html\n" - "when you have a connection to the web (link opens in an external browser)\n" - "or enable web connections for news in Octave's network settings dialog.\n" - "

\n" - "

\n" - "— The Octave Developers, ") + OCTAVE_RELEASE_DATE + "\n" - "

\n" - "\n" - "\n"); - - emit display_news_signal (html_text); - - emit finished (); - } - - //! Reimplements QApplication::notify. - /*! Octave's own exceptions are caugh and rethrown in the interpreter - thread.*/ - bool - octave_qapplication::notify (QObject *receiver, QEvent *ev) - { - try - { - return QApplication::notify (receiver, ev); - } - catch (octave::execution_exception&) - { - octave_link::post_exception (std::current_exception ()); - } - - return false; - } - - octave_qt_app::octave_qt_app (gui_application& app_context) - : QObject (), m_app_context (app_context), - m_argc (m_app_context.sys_argc ()), - m_argv (m_app_context.sys_argv ()), m_qt_app (nullptr), - m_qt_tr (new QTranslator ()), m_gui_tr (new QTranslator ()), - m_qsci_tr (new QTranslator ()), m_translators_installed (false), - m_octave_qt_link (new octave_qt_link ()), - m_interpreter (new octave_interpreter (m_app_context)), - m_main_thread (new QThread ()), - m_main_window (nullptr) - { - std::string show_gui_msgs = - sys::env::getenv ("OCTAVE_SHOW_GUI_MESSAGES"); - - // Installing our handler suppresses the messages. - - if (show_gui_msgs.empty ()) - { -#if defined (HAVE_QINSTALLMESSAGEHANDLER) - qInstallMessageHandler (message_handler); -#else - qInstallMsgHandler (message_handler); -#endif - } - - // Set the codec for all strings (before wizard or any GUI object) -#if ! defined (Q_OS_WIN32) - QTextCodec::setCodecForLocale (QTextCodec::codecForName ("UTF-8")); -#endif - -#if defined (HAVE_QT4) - QTextCodec::setCodecForCStrings (QTextCodec::codecForName ("UTF-8")); -#endif - - // Initialize global Qt application metadata. - - QCoreApplication::setApplicationName ("GNU Octave"); - QCoreApplication::setApplicationVersion (OCTAVE_VERSION); - - // Register octave_value_list for connecting thread crossing signals. - - qRegisterMetaType ("octave_value_list"); - - // Even if START_GUI is false, we still set up the QApplication so - // that we can use Qt widgets for plot windows. - - m_qt_app = new octave_qapplication (m_argc, m_argv); - - // Force left-to-right alignment (see bug #46204) - m_qt_app->setLayoutDirection (Qt::LeftToRight); - - octave_link::connect_link (m_octave_qt_link); - - connect (m_octave_qt_link, SIGNAL (confirm_shutdown_signal (void)), - this, SLOT (confirm_shutdown_octave (void))); - - connect (m_octave_qt_link, - SIGNAL (copy_image_to_clipboard_signal (const QString&, bool)), - this, SLOT (copy_image_to_clipboard (const QString&, bool))); - - connect_uiwidget_links (); - - connect (m_interpreter, SIGNAL (octave_finished_signal (int)), - this, SLOT (handle_octave_finished (int))); - - connect (m_interpreter, SIGNAL (octave_finished_signal (int)), - m_main_thread, SLOT (quit (void))); - - connect (m_main_thread, SIGNAL (finished (void)), - m_main_thread, SLOT (deleteLater (void))); - - if (m_app_context.start_gui_p ()) - create_main_window (); - else - { - // Get settings file. - resource_manager::reload_settings (); - - // After settings. - config_translators (); - - m_qt_app->setQuitOnLastWindowClosed (false); - } - - // Defer initializing and executing the interpreter until after the main - // window and QApplication are running to prevent race conditions - QTimer::singleShot (0, m_interpreter, SLOT (execute (void))); - - m_interpreter->moveToThread (m_main_thread); - - m_main_thread->start (); - } - - octave_qt_app::~octave_qt_app (void) - { - delete m_main_window; - delete m_interpreter; - delete m_qt_app; - - string_vector::delete_c_str_vec (m_argv); - } - - void octave_qt_app::config_translators (void) - { - if (m_translators_installed) - return; - - resource_manager::config_translators (m_qt_tr, m_qsci_tr, m_gui_tr); - - m_qt_app->installTranslator (m_qt_tr); - m_qt_app->installTranslator (m_gui_tr); - m_qt_app->installTranslator (m_qsci_tr); - - m_translators_installed = true; - } - - void octave_qt_app::create_main_window (void) - { - m_main_window = new main_window (*this, m_octave_qt_link); - - connect (m_interpreter, SIGNAL (octave_ready_signal (void)), - m_main_window, SLOT (handle_octave_ready (void))); - - m_app_context.gui_running (true); - } - - int octave_qt_app::exec (void) - { - return m_qt_app->exec (); - } - - void octave_qt_app::handle_octave_finished (int exit_status) - { - qApp->exit (exit_status); - } - - void octave_qt_app::confirm_shutdown_octave (void) - { - bool closenow = true; - - if (m_main_window) - closenow = m_main_window->confirm_shutdown_octave (); - - // Wait for link thread to go to sleep state. - m_octave_qt_link->lock (); - - m_octave_qt_link->shutdown_confirmation (closenow); - - m_octave_qt_link->unlock (); - - // Awake the worker thread so that it continues shutting down (or not). - m_octave_qt_link->wake_all (); - } - - void octave_qt_app::copy_image_to_clipboard (const QString& file, - bool remove_file) - { - QClipboard *clipboard = QApplication::clipboard (); - - QImage img (file); - - if (img.isNull ()) - { - // Report error? - return; - } - - clipboard->setImage (img); - - if (remove_file) - QFile::remove (file); - } - - // Create a message dialog with specified string, buttons and decorative - // text. - - void octave_qt_app::handle_create_dialog (const QString& message, - const QString& title, - const QString& icon, - const QStringList& button, - const QString& defbutton, - const QStringList& role) - { - MessageDialog *message_dialog = new MessageDialog (message, title, icon, - button, defbutton, role); - message_dialog->setAttribute (Qt::WA_DeleteOnClose); - message_dialog->show (); - } - - // Create a list dialog with specified list, initially selected, mode, - // view size and decorative text. - - void octave_qt_app::handle_create_listview (const QStringList& list, - const QString& mode, - int wd, int ht, - const QIntList& initial, - const QString& name, - const QStringList& prompt, - const QString& ok_string, - const QString& cancel_string) - { - ListDialog *list_dialog = new ListDialog (list, mode, wd, ht, - initial, name, prompt, - ok_string, cancel_string); - - list_dialog->setAttribute (Qt::WA_DeleteOnClose); - list_dialog->show (); - } - - // Create an input dialog with specified prompts and defaults, title and - // row/column size specifications. - void octave_qt_app::handle_create_inputlayout (const QStringList& prompt, - const QString& title, - const QFloatList& nr, - const QFloatList& nc, - const QStringList& defaults) - { - InputDialog *input_dialog = new InputDialog (prompt, title, nr, nc, - defaults); - - input_dialog->setAttribute (Qt::WA_DeleteOnClose); - input_dialog->show (); - } - - void octave_qt_app::handle_create_filedialog (const QStringList& filters, - const QString& title, - const QString& filename, - const QString& dirname, - const QString& multimode) - { - FileDialog *file_dialog = new FileDialog (filters, title, filename, - dirname, multimode); - - file_dialog->setAttribute (Qt::WA_DeleteOnClose); - file_dialog->show (); - } - - // Connect the signals emitted when the Octave thread wants to create - // a dialog box of some sort. Perhaps a better place for this would be - // as part of the QUIWidgetCreator class. However, mainWindow currently - // is not a global variable and not accessible for connecting. - - void octave_qt_app::connect_uiwidget_links (void) - { - connect (&uiwidget_creator, - SIGNAL (create_dialog (const QString&, const QString&, - const QString&, const QStringList&, - const QString&, const QStringList&)), - this, - SLOT (handle_create_dialog (const QString&, const QString&, - const QString&, const QStringList&, - const QString&, const QStringList&))); - - // Register QIntList so that list of ints may be part of a signal. - qRegisterMetaType ("QIntList"); - connect (&uiwidget_creator, - SIGNAL (create_listview (const QStringList&, const QString&, - int, int, const QIntList&, - const QString&, const QStringList&, - const QString&, const QString&)), - this, - SLOT (handle_create_listview (const QStringList&, const QString&, - int, int, const QIntList&, - const QString&, const QStringList&, - const QString&, const QString&))); - - // Register QFloatList so that list of floats may be part of a signal. - qRegisterMetaType ("QFloatList"); - connect (&uiwidget_creator, - SIGNAL (create_inputlayout (const QStringList&, const QString&, - const QFloatList&, const QFloatList&, - const QStringList&)), - this, - SLOT (handle_create_inputlayout (const QStringList&, const QString&, - const QFloatList&, - const QFloatList&, - const QStringList&))); - - connect (&uiwidget_creator, - SIGNAL (create_filedialog (const QStringList &,const QString&, - const QString&, const QString&, - const QString&)), - this, - SLOT (handle_create_filedialog (const QStringList &, const QString&, - const QString&, const QString&, - const QString&))); - } } diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/main-window.h --- a/libgui/src/main-window.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/main-window.h Fri Jul 12 12:14:43 2019 -0400 @@ -52,7 +52,6 @@ #include "history-dock-widget.h" #include "octave-cmd.h" #include "octave-dock-widget.h" -#include "octave-gui.h" #include "octave-qt-link.h" #include "resource-manager.h" #include "terminal-dock-widget.h" @@ -64,36 +63,9 @@ namespace octave { + class base_qobject; class settings_dialog; - class octave_interpreter : public QObject - { - Q_OBJECT - - public: - - octave_interpreter (gui_application& app_context); - - ~octave_interpreter (void) = default; - - signals: - - void octave_ready_signal (void); - void octave_finished_signal (int); - - public slots: - - //! Initialize and execute the octave interpreter. - - void execute (void); - - private: - - gui_application& m_app_context; - }; - - class octave_qt_app; - //! Represents the main window. class main_window : public QMainWindow @@ -105,7 +77,7 @@ typedef std::pair name_pair; typedef std::pair int_pair; - main_window (octave_qt_app& oct_qt_app, octave_qt_link *oct_qt_lnk); + main_window (base_qobject& qapp); ~main_window (void); @@ -123,6 +95,7 @@ void new_file_signal (const QString&); void open_file_signal (const QString&); void open_file_signal (const QString& file, const QString& enc, int line); + void step_into_file_signal (void); void show_doc_signal (const QString&); void register_doc_signal (const QString&); @@ -194,6 +167,7 @@ void debug_step_over (void); void debug_step_out (void); void debug_quit (void); + void editor_tabs_changed (bool); void request_open_file (void); void request_new_script (const QString& commands = QString ()); @@ -332,9 +306,7 @@ QList dock_widget_list (void); - QApplication *m_qt_app; - - octave_qt_link *m_octave_qt_link; + base_qobject& m_octave_qobj; workspace_model *m_workspace_model; @@ -455,6 +427,7 @@ //!@{ bool m_prevent_readline_conflicts; bool m_suppress_dbg_location; + bool m_editor_has_tabs; //! Flag for closing the whole application. @@ -463,127 +436,6 @@ QString m_file_encoding; }; - - class news_reader : public QObject - { - Q_OBJECT - - public: - - news_reader (const QString& base_url, const QString& page, - int serial = -1, bool connect_to_web = false) - : QObject (), m_base_url (base_url), m_page (page), m_serial (serial), - m_connect_to_web (connect_to_web) - { } - - public slots: - - void process (void); - - signals: - - void display_news_signal (const QString& news); - - void finished (void); - - private: - - QString m_base_url; - QString m_page; - int m_serial; - bool m_connect_to_web; - }; - - class octave_qapplication : public QApplication - { - public: - - octave_qapplication (int& argc, char **argv) - : QApplication (argc, argv) - { } - - virtual bool notify (QObject *receiver, QEvent *e) override; - - ~octave_qapplication (void) { }; - }; - - class octave_qt_app : public QObject - { - Q_OBJECT - - public: - - octave_qt_app (gui_application& app_context); - - ~octave_qt_app (void); - - void config_translators (void); - - void create_main_window (void); - - int exec (void); - - QApplication *qt_app (void) { return m_qt_app; }; - - public slots: - - void handle_octave_finished (int); - - void confirm_shutdown_octave (void); - - void copy_image_to_clipboard (const QString& file, bool remove_file); - - void handle_create_dialog (const QString& message, const QString& title, - const QString& icon, const QStringList& button, - const QString& defbutton, - const QStringList& role); - - void handle_create_listview (const QStringList& list, const QString& mode, - int width, int height, - const QIntList& initial, - const QString& name, - const QStringList& prompt, - const QString& ok_string, - const QString& cancel_string); - - void handle_create_inputlayout (const QStringList&, const QString&, - const QFloatList&, const QFloatList&, - const QStringList&); - - void handle_create_filedialog (const QStringList& filters, - const QString& title, - const QString& filename, - const QString& dirname, - const QString& multimode); - - private: - - gui_application& m_app_context; - - // Use these to ensure that argc and argv exist for as long as the - // QApplication object. - - int m_argc; - char **m_argv; - - octave_qapplication *m_qt_app; - - QTranslator *m_qt_tr; - QTranslator *m_gui_tr; - QTranslator *m_qsci_tr; - - bool m_translators_installed; - - octave_qt_link *m_octave_qt_link; - - octave_interpreter *m_interpreter; - - QThread *m_main_thread; - - main_window *m_main_window; - - void connect_uiwidget_links (void); - }; } #endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/module.mk --- a/libgui/src/module.mk Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/module.mk Fri Jul 12 12:14:43 2019 -0400 @@ -1,9 +1,14 @@ octave_gui_ICONS = \ %reldir%/icons/applications-system.png \ + %reldir%/icons/bottom_left_corner.png \ + %reldir%/icons/bottom_right_corner.png \ + %reldir%/icons/bottom_side.png \ %reldir%/icons/bp-next.png \ %reldir%/icons/bp-prev.png \ %reldir%/icons/bp-rm-all.png \ %reldir%/icons/bp-toggle.png \ + %reldir%/icons/circle.png \ + %reldir%/icons/cross.png \ %reldir%/icons/db-cont.png \ %reldir%/icons/db-step-in.png \ %reldir%/icons/db-step-out.png \ @@ -25,6 +30,15 @@ %reldir%/icons/edit-paste.png \ %reldir%/icons/edit-redo.png \ %reldir%/icons/edit-undo.png \ + %reldir%/icons/figure-axes.png \ + %reldir%/icons/figure-grid.png \ + %reldir%/icons/figure-pan.png \ + %reldir%/icons/figure-rotate.png \ + %reldir%/icons/figure-text.png \ + %reldir%/icons/figure-zoom-in.png \ + %reldir%/icons/figure-zoom-original.png \ + %reldir%/icons/figure-zoom-out.png \ + %reldir%/icons/fleur.png \ %reldir%/icons/folder.png \ %reldir%/icons/folder-new.png \ %reldir%/icons/go-down.png \ @@ -52,7 +66,9 @@ %reldir%/icons/graphic_logo_WorkspaceView.svg \ %reldir%/icons/graphic_logo_ReleaseWidget.svg \ %reldir%/icons/graphic_logo_VariableEditor.svg \ + %reldir%/icons/hand2.png \ %reldir%/icons/icons_license \ + %reldir%/icons/left_side.png \ %reldir%/icons/letter_logo_DocumentationDockWidget.png \ %reldir%/icons/letter_logo_FileEditor.png \ %reldir%/icons/letter_logo_FilesDockWidget.png \ @@ -74,7 +90,11 @@ %reldir%/icons/logo.png \ %reldir%/icons/plot-xy-curve.png \ %reldir%/icons/preferences-system.png \ + %reldir%/icons/right_side.png \ %reldir%/icons/system-run.png \ + %reldir%/icons/top_left_corner.png \ + %reldir%/icons/top_right_corner.png \ + %reldir%/icons/top_side.png \ %reldir%/icons/user-home.png \ %reldir%/icons/view-refresh.png \ %reldir%/icons/widget-close.png \ @@ -118,8 +138,11 @@ %reldir%/moc-dw-main-window.cc \ %reldir%/moc-files-dock-widget.cc \ %reldir%/moc-history-dock-widget.cc \ + %reldir%/moc-interpreter-qobject.cc \ %reldir%/moc-main-window.cc \ + %reldir%/moc-news-reader.cc \ %reldir%/moc-octave-cmd.cc \ + %reldir%/moc-octave-qobject.cc \ %reldir%/moc-octave-qt-link.cc \ %reldir%/moc-settings-dialog.cc \ %reldir%/moc-terminal-dock-widget.cc \ @@ -167,6 +190,7 @@ %reldir%/external-editor-interface.h \ %reldir%/files-dock-widget.h \ %reldir%/history-dock-widget.h \ + %reldir%/interpreter-qobject.h \ %reldir%/m-editor/file-editor-interface.h \ %reldir%/m-editor/file-editor-tab.h \ %reldir%/m-editor/file-editor.h \ @@ -175,9 +199,11 @@ %reldir%/m-editor/octave-txt-lexer.h \ %reldir%/m-editor/marker.h \ %reldir%/main-window.h \ - %reldir%/octave-gui.h \ + %reldir%/news-reader.h \ %reldir%/octave-cmd.h \ + %reldir%/octave-qobject.h \ %reldir%/octave-qt-link.h \ + %reldir%/qt-application.h \ %reldir%/resource-manager.h \ %reldir%/settings-dialog.h \ %reldir%/shortcut-manager.h \ @@ -200,6 +226,7 @@ %reldir%/external-editor-interface.cc \ %reldir%/files-dock-widget.cc \ %reldir%/history-dock-widget.cc \ + %reldir%/interpreter-qobject.cc \ %reldir%/m-editor/file-editor-tab.cc \ %reldir%/m-editor/file-editor.cc \ %reldir%/m-editor/find-dialog.cc \ @@ -207,10 +234,12 @@ %reldir%/m-editor/octave-txt-lexer.cc \ %reldir%/m-editor/marker.cc \ %reldir%/main-window.cc \ + %reldir%/news-reader.cc \ %reldir%/octave-cmd.cc \ %reldir%/octave-dock-widget.cc \ - %reldir%/octave-gui.cc \ + %reldir%/octave-qobject.cc \ %reldir%/octave-qt-link.cc \ + %reldir%/qt-application.cc \ %reldir%/resource-manager.cc \ %reldir%/settings-dialog.cc \ %reldir%/shortcut-manager.cc \ diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/news-reader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/news-reader.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,150 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include + +#include +#include + +#include "news-reader.h" +#include "resource-manager.h" + +#include "url-transfer.h" +#include "version.h" + +namespace octave +{ + void news_reader::process (void) + { + QString html_text; + + if (m_connect_to_web) + { + // Run this part in a separate thread so Octave can continue to + // run while we wait for the page to load. Then emit the signal + // to display it when we have the page contents. + + QString url = m_base_url + '/' + m_page; + std::ostringstream buf; + url_transfer octave_dot_org (url.toStdString (), buf); + + if (octave_dot_org.is_valid ()) + { + Array param; + octave_dot_org.http_get (param); + + if (octave_dot_org.good ()) + html_text = QString::fromStdString (buf.str ()); + } + + if (html_text.contains ("this-is-the-gnu-octave-community-news-page")) + { + if (m_serial >= 0) + { + QSettings *settings = resource_manager::get_settings (); + + if (settings) + { + settings->setValue ("news/last_time_checked", + QDateTime::currentDateTime ()); + + settings->sync (); + } + + QString tag ("community-news-page-serial="); + + int b = html_text.indexOf (tag); + + if (b) + { + b += tag.length (); + + int e = html_text.indexOf ("\n", b); + + QString tmp = html_text.mid (b, e-b); + + int curr_page_serial = tmp.toInt (); + + if (curr_page_serial > m_serial) + { + if (settings) + { + settings->setValue ("news/last_news_item", + curr_page_serial); + + settings->sync (); + } + } + else + return; + } + else + return; + } + } + else + html_text = QString + (tr ("\n" + "\n" + "

\n" + "Octave's community news source seems to be unavailable.\n" + "

\n" + "

\n" + "For the latest news, please check\n" + "https://octave.org/community-news.html\n" + "when you have a connection to the web (link opens in an external browser).\n" + "

\n" + "

\n" + "— The Octave Developers, ") + OCTAVE_RELEASE_DATE + "\n" + "

\n" + "\n" + "\n"); + } + else + html_text = QString + (tr ("\n" + "\n" + "

\n" + "Connecting to the web to display the latest Octave Community news has been disabled.\n" + "

\n" + "

\n" + "For the latest news, please check\n" + "https://octave.org/community-news.html\n" + "when you have a connection to the web (link opens in an external browser)\n" + "or enable web connections for news in Octave's network settings dialog.\n" + "

\n" + "

\n" + "— The Octave Developers, ") + OCTAVE_RELEASE_DATE + "\n" + "

\n" + "\n" + "\n"); + + emit display_news_signal (html_text); + + emit finished (); + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/news-reader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/news-reader.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,63 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_news_reader_h) +#define octave_news_reader_h 1 + +#include +#include + +namespace octave +{ + class news_reader : public QObject + { + Q_OBJECT + + public: + + news_reader (const QString& base_url, const QString& page, + int serial = -1, bool connect_to_web = false) + : QObject (), m_base_url (base_url), m_page (page), m_serial (serial), + m_connect_to_web (connect_to_web) + { } + + public slots: + + void process (void); + + signals: + + void display_news_signal (const QString& news); + + void finished (void); + + private: + + QString m_base_url; + QString m_page; + int m_serial; + bool m_connect_to_web; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-cmd.cc --- a/libgui/src/octave-cmd.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-cmd.cc Fri Jul 12 12:14:43 2019 -0400 @@ -65,8 +65,9 @@ } else { - // no valid identifier: use Fsource (), no debug possible - Fsource (ovl (file_path)); + interpreter& interp = __get_interpreter__ ("octave_cmd_eval::execute"); + // no valid identifier: use equivalent of Fsource (), no debug possible + interp.source_file (file_path); command_editor::replace_line (""); } @@ -88,10 +89,10 @@ { case CMD_UPD_WORKSPACE: { - call_stack& cs - = __get_call_stack__ ("octave_cmd_builtin::execute"); + tree_evaluator& tw + = __get_evaluator__ ("octave_cmd_builtin::execute"); - octave_link::set_workspace (true, cs.get_symbol_info ()); + octave_link::set_workspace (true, tw.get_symbol_info ()); } break; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-cmd.h --- a/libgui/src/octave-cmd.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-cmd.h Fri Jul 12 12:14:43 2019 -0400 @@ -98,7 +98,7 @@ * the executing Ff */ octave_cmd_builtin ( - octave_value_list (*Ff) (octave::interpreter&, const octave_value_list&, int), + octave_value_list (*Ff) (interpreter&, const octave_value_list&, int), octave_value_list argin = ovl (), cmd_upd update = CMD_UPD_NO) : octave_cmd (), m_callback_fi (Ff), m_callback_f (nullptr), m_argin (argin), m_nargout (0), m_argout_receiver (nullptr), @@ -129,7 +129,7 @@ * worker thread is busy and can not execute the desired command immediately. */ octave_cmd_builtin ( - octave_value_list (*Ff) (octave::interpreter&, const octave_value_list&, int), + octave_value_list (*Ff) (interpreter&, const octave_value_list&, int), octave_value_list argin, int nargout, QObject *argout_receiver, const char *argout_handler = nullptr, cmd_upd update = CMD_UPD_NO) : octave_cmd (), m_callback_fi (Ff), m_callback_f (nullptr), @@ -160,7 +160,7 @@ protected: - octave_value_list (*m_callback_fi) (octave::interpreter&, + octave_value_list (*m_callback_fi) (interpreter&, const octave_value_list&, int); octave_value_list (*m_callback_f) (const octave_value_list&, int); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-dock-widget.cc --- a/libgui/src/octave-dock-widget.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-dock-widget.cc Fri Jul 12 12:14:43 2019 -0400 @@ -305,6 +305,8 @@ focus (); set_style (true); } + + emit topLevelChanged (true); // Be sure signal is emitted } // dock the widget diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-dock-widget.h --- a/libgui/src/octave-dock-widget.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-dock-widget.h Fri Jul 12 12:14:43 2019 -0400 @@ -150,6 +150,8 @@ protected slots: + virtual void toplevel_change (bool); + //! Slot to steer changing visibility from outside. virtual void handle_visibility_changed (bool visible) @@ -165,7 +167,6 @@ private slots: void change_visibility (bool); - void toplevel_change (bool); private: diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-gui.cc --- a/libgui/src/octave-gui.cc Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - -Copyright (C) 2011-2019 Jacob Dawid - -This file is part of Octave. - -Octave 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. - -Octave 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 Octave; see the file COPYING. If not, see -. - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include "lo-utils.h" -#include "oct-env.h" -#include "oct-syscalls.h" -#include "signal-wrappers.h" - -#include "builtin-defun-decls.h" -#include "display.h" -#include "octave.h" -#include "sysdep.h" - -#include "main-window.h" -#include "octave-gui.h" - -namespace octave -{ - gui_application::gui_application (int argc, char **argv) - : application (argc, argv) - { - // This should probably happen early. - sysdep_init (); - } - - bool gui_application::start_gui_p (void) const - { - return m_options.gui (); - } - - int gui_application::execute (void) - { - octave_block_interrupt_signal (); - - set_application_id (); - - // Create and show main window. - - octave_qt_app oct_qt_app (*this); - - return oct_qt_app.exec (); - } -} diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-gui.h --- a/libgui/src/octave-gui.h Wed Jul 10 20:02:44 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - -Copyright (C) 2012-2019 John W. Eaton - -This file is part of Octave. - -Octave 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. - -Octave 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 Octave; see the file COPYING. If not, see -. - -*/ - -#if ! defined (octave_octave_gui_h) -#define octave_octave_gui_h 1 - -#include "octave.h" - -namespace octave -{ - class OCTGUI_API gui_application : public application - { - public: - - gui_application (int argc, char **argv); - - // No copying, at least not yet. - - gui_application (const gui_application&) = delete; - - gui_application& operator = (const gui_application&) = delete; - - ~gui_application (void) = default; - - // Should we start the GUI or fall back to the CLI? - bool start_gui_p (void) const; - - int execute (void); - - bool gui_running (void) const { return m_gui_running; } - void gui_running (bool arg) { m_gui_running = arg; } - - private: - - int m_argc; - char **m_argv; - - // If TRUE, the GUI should be started. - bool m_gui_running = false; - }; -} - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-qobject.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/octave-qobject.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,371 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "dialog.h" +#include "interpreter-qobject.h" +#include "main-window.h" +#include "octave-qobject.h" +#include "octave-qt-link.h" +#include "qt-application.h" +#include "resource-manager.h" + +#include "oct-env.h" +#include "version.h" + +namespace octave +{ + // Disable all Qt messages by default. + + static void +#if defined (QTMESSAGEHANDLER_ACCEPTS_QMESSAGELOGCONTEXT) + message_handler (QtMsgType, const QMessageLogContext &, const QString &) +#else + message_handler (QtMsgType, const char *) +#endif + { } + + //! Reimplement QApplication::notify. Octave's own exceptions are + //! caught and rethrown in the interpreter thread. + + bool octave_qapplication::notify (QObject *receiver, QEvent *ev) + { + try + { + return QApplication::notify (receiver, ev); + } + catch (execution_exception&) + { + octave_link::post_exception (std::current_exception ()); + } + + return false; + } + + // We will create a QApplication object, even if START_GUI is false, + // so that we can use Qt widgets for plot windows when running in + // command-line mode. Note that we are creating an + // octave_qapplication object but handling it as a QApplication object + // because the octave_qapplication should behave identically to a + // QApplication object except that it overrides the notify method so + // we can handle forward Octave interpreter exceptions from the GUI + // thread to the interpreter thread. + + base_qobject::base_qobject (qt_application& app_context) + : QObject (), m_app_context (app_context), + m_argc (m_app_context.sys_argc ()), + m_argv (m_app_context.sys_argv ()), + m_qapplication (new octave_qapplication (m_argc, m_argv)), + m_qt_tr (new QTranslator ()), m_gui_tr (new QTranslator ()), + m_qsci_tr (new QTranslator ()), m_translators_installed (false), + m_interpreter_qobj (new interpreter_qobject (this)), + m_main_thread (new QThread ()) + { + std::string show_gui_msgs = + sys::env::getenv ("OCTAVE_SHOW_GUI_MESSAGES"); + + // Installing our handler suppresses the messages. + + if (show_gui_msgs.empty ()) + { +#if defined (HAVE_QINSTALLMESSAGEHANDLER) + qInstallMessageHandler (message_handler); +#else + qInstallMsgHandler (message_handler); +#endif + } + + // Set the codec for all strings (before wizard or any GUI object) +#if ! defined (Q_OS_WIN32) + QTextCodec::setCodecForLocale (QTextCodec::codecForName ("UTF-8")); +#endif + +#if defined (HAVE_QT4) + QTextCodec::setCodecForCStrings (QTextCodec::codecForName ("UTF-8")); +#endif + + // Initialize global Qt application metadata. + + QCoreApplication::setApplicationName ("GNU Octave"); + QCoreApplication::setApplicationVersion (OCTAVE_VERSION); + + // Register octave_value_list for connecting thread crossing signals. + + qRegisterMetaType ("octave_value_list"); + + // Force left-to-right alignment (see bug #46204) + m_qapplication->setLayoutDirection (Qt::LeftToRight); + + connect_uiwidget_links (); + + connect (m_interpreter_qobj, SIGNAL (octave_finished_signal (int)), + this, SLOT (handle_octave_finished (int))); + + connect (m_interpreter_qobj, SIGNAL (octave_finished_signal (int)), + m_main_thread, SLOT (quit (void))); + + connect (m_main_thread, SIGNAL (finished (void)), + m_main_thread, SLOT (deleteLater (void))); + } + + base_qobject::~base_qobject (void) + { + // Note that we don't delete m_main_thread here. That is handled by + // deleteLater slot that is called when the m_main_thread issues a + // finished signal. + + delete m_interpreter_qobj; + delete m_qsci_tr; + delete m_gui_tr; + delete m_qt_tr; + delete m_qapplication; + + string_vector::delete_c_str_vec (m_argv); + } + + void base_qobject::config_translators (void) + { + if (m_translators_installed) + return; + + resource_manager::config_translators (m_qt_tr, m_qsci_tr, m_gui_tr); + + m_qapplication->installTranslator (m_qt_tr); + m_qapplication->installTranslator (m_gui_tr); + m_qapplication->installTranslator (m_qsci_tr); + + m_translators_installed = true; + } + + void base_qobject::start_main_thread (void) + { + // Defer initializing and executing the interpreter until after the main + // window and QApplication are running to prevent race conditions + QTimer::singleShot (0, m_interpreter_qobj, SLOT (execute (void))); + + m_interpreter_qobj->moveToThread (m_main_thread); + + m_main_thread->start (); + } + + int base_qobject::exec (void) + { + return m_qapplication->exec (); + } + + void base_qobject::handle_octave_finished (int exit_status) + { + qApp->exit (exit_status); + } + + void base_qobject::confirm_shutdown_octave (void) + { + m_interpreter_qobj->confirm_shutdown (true); + } + + void base_qobject::copy_image_to_clipboard (const QString& file, + bool remove_file) + { + QClipboard *clipboard = QApplication::clipboard (); + + QImage img (file); + + if (img.isNull ()) + { + // Report error? + return; + } + + clipboard->setImage (img); + + if (remove_file) + QFile::remove (file); + } + + // Create a message dialog with specified string, buttons and decorative + // text. + + void base_qobject::handle_create_dialog (const QString& message, + const QString& title, + const QString& icon, + const QStringList& button, + const QString& defbutton, + const QStringList& role) + { + MessageDialog *message_dialog = new MessageDialog (message, title, icon, + button, defbutton, role); + message_dialog->setAttribute (Qt::WA_DeleteOnClose); + message_dialog->show (); + } + + // Create a list dialog with specified list, initially selected, mode, + // view size and decorative text. + + void base_qobject::handle_create_listview (const QStringList& list, + const QString& mode, + int wd, int ht, + const QIntList& initial, + const QString& name, + const QStringList& prompt, + const QString& ok_string, + const QString& cancel_string) + { + ListDialog *list_dialog = new ListDialog (list, mode, wd, ht, + initial, name, prompt, + ok_string, cancel_string); + + list_dialog->setAttribute (Qt::WA_DeleteOnClose); + list_dialog->show (); + } + + // Create an input dialog with specified prompts and defaults, title and + // row/column size specifications. + void base_qobject::handle_create_inputlayout (const QStringList& prompt, + const QString& title, + const QFloatList& nr, + const QFloatList& nc, + const QStringList& defaults) + { + InputDialog *input_dialog = new InputDialog (prompt, title, nr, nc, + defaults); + + input_dialog->setAttribute (Qt::WA_DeleteOnClose); + input_dialog->show (); + } + + void base_qobject::handle_create_filedialog (const QStringList& filters, + const QString& title, + const QString& filename, + const QString& dirname, + const QString& multimode) + { + FileDialog *file_dialog = new FileDialog (filters, title, filename, + dirname, multimode); + + file_dialog->setAttribute (Qt::WA_DeleteOnClose); + file_dialog->show (); + } + + // Connect the signals emitted when the Octave thread wants to create + // a dialog box of some sort. Perhaps a better place for this would be + // as part of the QUIWidgetCreator class. However, mainWindow currently + // is not a global variable and not accessible for connecting. + + void base_qobject::connect_uiwidget_links (void) + { + connect (&uiwidget_creator, + SIGNAL (create_dialog (const QString&, const QString&, + const QString&, const QStringList&, + const QString&, const QStringList&)), + this, + SLOT (handle_create_dialog (const QString&, const QString&, + const QString&, const QStringList&, + const QString&, const QStringList&))); + + // Register QIntList so that list of ints may be part of a signal. + qRegisterMetaType ("QIntList"); + connect (&uiwidget_creator, + SIGNAL (create_listview (const QStringList&, const QString&, + int, int, const QIntList&, + const QString&, const QStringList&, + const QString&, const QString&)), + this, + SLOT (handle_create_listview (const QStringList&, const QString&, + int, int, const QIntList&, + const QString&, const QStringList&, + const QString&, const QString&))); + + // Register QFloatList so that list of floats may be part of a signal. + qRegisterMetaType ("QFloatList"); + connect (&uiwidget_creator, + SIGNAL (create_inputlayout (const QStringList&, const QString&, + const QFloatList&, const QFloatList&, + const QStringList&)), + this, + SLOT (handle_create_inputlayout (const QStringList&, const QString&, + const QFloatList&, + const QFloatList&, + const QStringList&))); + + connect (&uiwidget_creator, + SIGNAL (create_filedialog (const QStringList &,const QString&, + const QString&, const QString&, + const QString&)), + this, + SLOT (handle_create_filedialog (const QStringList &, const QString&, + const QString&, const QString&, + const QString&))); + } + + cli_qobject::cli_qobject (qt_application& app_context) + : base_qobject (app_context) + { + // Get settings file. + resource_manager::reload_settings (); + + // After settings. + config_translators (); + + m_qapplication->setQuitOnLastWindowClosed (false); + + start_main_thread (); + } + + gui_qobject::gui_qobject (qt_application& app_context) + : base_qobject (app_context), m_main_window (new main_window (*this)) + { + connect (m_interpreter_qobj, SIGNAL (octave_ready_signal (void)), + m_main_window, SLOT (handle_octave_ready (void))); + + m_app_context.gui_running (true); + + start_main_thread (); + } + + gui_qobject::~gui_qobject (void) + { + delete m_main_window; + } + + void gui_qobject::confirm_shutdown_octave (void) + { + bool closenow = true; + + if (m_main_window) + closenow = m_main_window->confirm_shutdown_octave (); + + m_interpreter_qobj->confirm_shutdown (closenow); + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-qobject.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/octave-qobject.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,193 @@ +/* + +Copyright (C) 2013-2019 John W. Eaton +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_octave_qobject_h) +#define octave_octave_qobject_h 1 + +#include +#include +#include +#include +#include + +// Defined for purposes of sending QList as part of signal. +typedef QList QIntList; + +// Defined for purposes of sending QList as part of signal. +typedef QList QFloatList; + +namespace octave +{ + class interpreter_qobject; + class main_window; + class qt_application; + + //! This class is a simple wrapper around QApplication so that we can + //! reimplement QApplication::notify. The octave_qapplication object + //! should behave identically to a QApplication object except that it + //! overrides the notify method so we can handle forward Octave + //! octave::execution_exception exceptions from the GUI thread to the + //! interpreter thread. + + class octave_qapplication : public QApplication + { + public: + + octave_qapplication (int& argc, char **argv) + : QApplication (argc, argv) + { } + + virtual bool notify (QObject *receiver, QEvent *e) override; + + ~octave_qapplication (void) { }; + }; + + //! Base class for Octave interfaces that use Qt. There are two + //! classes derived from this one. One provides a command-line + //! interface that may use Qt graphics and another provides the + //! full GUI experience. + + class base_qobject : public QObject + { + Q_OBJECT + + public: + + base_qobject (qt_application& app_context); + + ~base_qobject (void); + + void config_translators (void); + + void start_main_thread (void); + + int exec (void); + + // The Octave application context. + qt_application& app_context (void) { return m_app_context; } + + // The Qt QApplication. + QApplication * qapplication (void) { return m_qapplication; }; + + interpreter_qobject * interpreter_qobj (void) + { + return m_interpreter_qobj; + } + + QThread *main_thread (void) { return m_main_thread; } + + public slots: + + void handle_octave_finished (int); + + virtual void confirm_shutdown_octave (void); + + void copy_image_to_clipboard (const QString& file, bool remove_file); + + void handle_create_dialog (const QString& message, const QString& title, + const QString& icon, const QStringList& button, + const QString& defbutton, + const QStringList& role); + + void handle_create_listview (const QStringList& list, const QString& mode, + int width, int height, + const QIntList& initial, + const QString& name, + const QStringList& prompt, + const QString& ok_string, + const QString& cancel_string); + + void handle_create_inputlayout (const QStringList&, const QString&, + const QFloatList&, const QFloatList&, + const QStringList&); + + void handle_create_filedialog (const QStringList& filters, + const QString& title, + const QString& filename, + const QString& dirname, + const QString& multimode); + + protected: + + qt_application& m_app_context; + + // Use these to ensure that argc and argv exist for as long as the + // QApplication object. + + int m_argc; + char **m_argv; + + QApplication *m_qapplication; + + QTranslator *m_qt_tr; + QTranslator *m_gui_tr; + QTranslator *m_qsci_tr; + + bool m_translators_installed; + + interpreter_qobject *m_interpreter_qobj; + + QThread *m_main_thread; + + void connect_uiwidget_links (void); + }; + + //! This object provides a command-line interface to Octave that may + //! use Qt graphics. + + class cli_qobject : public base_qobject + { + Q_OBJECT + + public: + + cli_qobject (qt_application& app_context); + + ~cli_qobject (void) = default; + }; + + //! This object provides a full GUI interface to Octave that is + //! implemented Qt. + + class gui_qobject : public base_qobject + { + Q_OBJECT + + public: + + gui_qobject (qt_application& app_context); + + ~gui_qobject (void); + + public slots: + + void confirm_shutdown_octave (void); + + private: + + main_window *m_main_window; + }; +} + +#endif + diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-qt-link.cc --- a/libgui/src/octave-qt-link.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-qt-link.cc Fri Jul 12 12:14:43 2019 -0400 @@ -46,7 +46,6 @@ #include "syminfo.h" #include "utils.h" -#include "octave-gui.h" #include "octave-qt-link.h" #include "resource-manager.h" @@ -100,7 +99,7 @@ if (! settings || settings->value ("editor/create_new_file",false).toBool ()) return true; - std::string abs_fname = octave::sys::env::make_absolute (file); + std::string abs_fname = sys::env::make_absolute (file); QStringList btn; QStringList role; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/octave-qt-link.h --- a/libgui/src/octave-qt-link.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/octave-qt-link.h Fri Jul 12 12:14:43 2019 -0400 @@ -34,7 +34,6 @@ #include #include -#include "octave-gui.h" #include "octave-link.h" // Defined for purposes of sending QList as part of signal. diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/qt-application.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/qt-application.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,74 @@ +/* + +Copyright (C) 2011-2019 Jacob Dawid + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "lo-utils.h" +#include "oct-env.h" +#include "oct-syscalls.h" +#include "signal-wrappers.h" + +#include "builtin-defun-decls.h" +#include "display.h" +#include "octave.h" +#include "sysdep.h" + +#include "main-window.h" +#include "octave-qobject.h" +#include "qt-application.h" + +namespace octave +{ + qt_application::qt_application (int argc, char **argv) + : application (argc, argv) + { + // This should probably happen early. + sysdep_init (); + } + + bool qt_application::start_gui_p (void) const + { + return m_options.gui (); + } + + int qt_application::execute (void) + { + octave_block_interrupt_signal (); + + set_application_id (); + + // Create and show main window. + + if (start_gui_p ()) + { + gui_qobject gui_interface (*this); + return gui_interface.exec (); + } + else + { + cli_qobject cli_interface (*this); + return cli_interface.exec (); + } + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/qt-application.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/qt-application.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2012-2019 John W. Eaton + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_octave_gui_h) +#define octave_octave_gui_h 1 + +#include "octave.h" + +namespace octave +{ + // Programming Note: This file must not include any Qt headers. Any + // Qt header files required by the qt_application::execute function + // must be included only in the corresponding .cc file. + + //! This class inherits from the pure-virtual base class + //! octave::application and provides an implementation of the + //! application::execute method that starts an interface to Octave + //! that is based on Qt. It may start a command-line interface that + //! allows Qt graphics to be used or it may start an interface that + //! provides the full GUI experience. + + class OCTGUI_API qt_application : public application + { + public: + + qt_application (int argc, char **argv); + + // No copying, at least not yet. + + qt_application (const qt_application&) = delete; + + qt_application& operator = (const qt_application&) = delete; + + ~qt_application (void) = default; + + // Should we start the GUI or fall back to the CLI? + bool start_gui_p (void) const; + + int execute (void); + + bool gui_running (void) const { return m_gui_running; } + void gui_running (bool arg) { m_gui_running = arg; } + + private: + + // If TRUE, the GUI should be started. + bool m_gui_running = false; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/resource-manager.cc --- a/libgui/src/resource-manager.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/resource-manager.cc Fri Jul 12 12:14:43 2019 -0400 @@ -70,20 +70,54 @@ : m_settings_directory (), m_settings_file (), m_settings (nullptr), m_default_settings (nullptr) { + // Let QSettings decide where to put the ini file with gui preferences + m_default_settings + = new QSettings(QSettings::IniFormat, QSettings::UserScope, + "octave", "octave-gui"); + + m_settings_file = m_default_settings->fileName (); + + QFileInfo sfile (m_settings_file); + m_settings_directory = sfile.absolutePath (); + + QString xdg_config_home + = QString::fromLocal8Bit (qgetenv ("XDG_CONFIG_HOME")); + + if ((! sfile.exists ()) && xdg_config_home.isEmpty ()) + { + // File does not exist yet: Look for a settings file at the old + // location ($HOME/.config/octave/qt-settings) for impoting all + // available keys into the new settings file. + // Do not look for an old settings file if XDG_CONFIG_HOME is set, + // since then a non-existin new settings file does not necessarily + // indicate a first run of octave with new config file locations. #if defined (HAVE_QSTANDARDPATHS) - QString home_path - = QStandardPaths::writableLocation (QStandardPaths::HomeLocation); + QString home_path + = QStandardPaths::writableLocation (QStandardPaths::HomeLocation); #else - QString home_path - = QDesktopServices::storageLocation (QDesktopServices::HomeLocation); + QString home_path + = QDesktopServices::storageLocation (QDesktopServices::HomeLocation); #endif - m_settings_directory = home_path + "/.config/octave"; + QString old_settings_directory = home_path + "/.config/octave"; + QString old_settings_file = old_settings_directory + "/qt-settings"; + + QFile ofile (old_settings_file); - m_settings_file = m_settings_directory + "/qt-settings"; + if (ofile.exists ()) + { + // Old settings file exists, create a QSettings object related + // to it and copy all available keys to the new settings + QSettings old_settings (old_settings_file, QSettings::IniFormat); - m_default_settings = new QSettings (default_qt_settings_file (), - QSettings::IniFormat); + QStringList keys = old_settings.allKeys (); + for (int i = 0; i < keys.count(); i++) + m_default_settings->setValue (keys.at(i), + old_settings.value(keys.at(i))); + + m_default_settings->sync (); // Done, make sure keys are written + } + } } resource_manager::~resource_manager (void) @@ -120,23 +154,31 @@ language = settings->value ("language", "SYSTEM").toString (); } + // load the translations depending on the settings if (language == "SYSTEM") - language = QLocale::system ().name (); // get system wide locale - - // load the translator file for qt strings - loaded = qt_tr->load ("qt_" + language, qt_trans_dir); - - if (! loaded) // try lower case - qt_tr->load ("qt_" + language.toLower (), qt_trans_dir); + { + // get the system locale and pass it to the translators for loading + // the suitable translation files + QLocale sys_locale = QLocale::system (); - // load the translator file for qscintilla settings - loaded = qsci_tr->load ("qscintilla_" + language, qt_trans_dir); + qt_tr->load (sys_locale, "qt", "_", qt_trans_dir); + qsci_tr->load (sys_locale, "qscintilla", "_", qt_trans_dir); + gui_tr->load (sys_locale, "", "", get_gui_translation_dir ()); + } + else + { + // load the translation files depending on the given locale name + loaded = qt_tr->load ("qt_" + language, qt_trans_dir); + if (! loaded) // try lower case + qt_tr->load ("qt_" + language.toLower (), qt_trans_dir); - if (! loaded) // try lower case - qsci_tr->load ("qscintilla_" + language.toLower (), qt_trans_dir); + loaded = qsci_tr->load ("qscintilla_" + language, qt_trans_dir); + if (! loaded) // try lower case + qsci_tr->load ("qscintilla_" + language.toLower (), qt_trans_dir); - // load the translator file for gui strings - gui_tr->load (language, get_gui_translation_dir ()); + gui_tr->load (language, get_gui_translation_dir ()); + } + } QStringList resource_manager::storage_class_names (void) @@ -176,13 +218,6 @@ if (! instance) instance = new resource_manager (); - if (! instance) - { - error ("unable to create resource_manager object!"); - - retval = false; - } - return retval; } @@ -294,8 +329,7 @@ delete m_settings; m_settings = new QSettings (file, QSettings::IniFormat); - if (! (m_settings - && QFile::exists (m_settings->fileName ()) + if (! (QFile::exists (m_settings->fileName ()) && m_settings->isWritable () && m_settings->status () == QSettings::NoError)) { diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/resource-manager.h --- a/libgui/src/resource-manager.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/resource-manager.h Fri Jul 12 12:14:43 2019 -0400 @@ -120,7 +120,7 @@ return instance_ok () ? instance->do_is_first_run () : true; } - static QString storage_class_chars (void) { return "afghip"; } + static QString storage_class_chars (void) { return "agp"; } static QStringList storage_class_names (void); static QList storage_class_default_colors (void); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/resource.qrc --- a/libgui/src/resource.qrc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/resource.qrc Fri Jul 12 12:14:43 2019 -0400 @@ -5,6 +5,11 @@ icons/bp-rm-all.png icons/bp-prev.png icons/bp-next.png + icons/bottom_left_corner.png + icons/bottom_right_corner.png + icons/bottom_side.png + icons/circle.png + icons/cross.png icons/db-cont.png icons/db-step.png icons/db-step-in.png @@ -26,6 +31,14 @@ icons/edit-paste.png icons/edit-redo.png icons/edit-undo.png + icons/figure-axes.png + icons/figure-grid.png + icons/figure-pan.png + icons/figure-rotate.png + icons/figure-text.png + icons/figure-zoom-in.png + icons/figure-zoom-original.png + icons/figure-zoom-out.png icons/folder.png icons/folder-new.png icons/go-down.png @@ -44,6 +57,9 @@ icons/graphic_logo_DocumentationDockWidget.png icons/graphic_logo_ReleaseWidget.png icons/graphic_logo_VariableEditor.png + icons/fleur.png + icons/hand2.png + icons/left_side.png icons/letter_logo_FilesDockWidget.png icons/letter_logo_FileEditor.png icons/letter_logo_NewsDockWidget.png @@ -56,7 +72,11 @@ icons/logo.png icons/plot-xy-curve.png icons/preferences-system.png + icons/right_side.png icons/system-run.png + icons/top_left_corner.png + icons/top_right_corner.png + icons/top_side.png icons/user-home.png icons/view-refresh.png icons/widget-close.png diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/settings-dialog.cc --- a/libgui/src/settings-dialog.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/settings-dialog.cc Fri Jul 12 12:14:43 2019 -0400 @@ -293,6 +293,10 @@ cb_show_eol->setChecked (settings->value ("editor/show_eol_chars", false).toBool ()); cb_show_hscrollbar->setChecked (settings->value ("editor/show_hscroll_bar", true).toBool ()); + editor_combox_tab_pos->insertItems (0, ed_tab_position_names); + editor_combox_tab_pos->setCurrentIndex ( + settings->value (ed_tab_position.key, ed_tab_position.def).toInt ()); + int selected_comment_string, selected_uncomment_string; if (settings->contains (ed_comment_str.key)) // new version (radio buttons) @@ -492,7 +496,7 @@ read_lexer_settings (lexer, settings); delete lexer; - lexer = new octave::octave_txt_lexer (); + lexer = new octave_txt_lexer (); read_lexer_settings (lexer, settings); delete lexer; @@ -887,6 +891,8 @@ settings->setValue ("editor/show_hscroll_bar", cb_show_hscrollbar->isChecked ()); settings->setValue ("editor/default_eol_mode", combo_eol_mode->currentIndex ()); + settings->setValue (ed_tab_position.key, editor_combox_tab_pos->currentIndex ()); + // Comment strings int rb_uncomment = 0; for (int i = 0; i < ed_comment_strings_count; i++) @@ -990,7 +996,7 @@ write_lexer_settings (lexer, settings); delete lexer; - lexer = new octave::octave_txt_lexer (); + lexer = new octave_txt_lexer (); write_lexer_settings (lexer, settings); delete lexer; @@ -1157,8 +1163,8 @@ void settings_dialog::read_varedit_colors (QSettings *settings) { - QList default_colors = octave::variable_editor::default_colors (); - QStringList class_names = octave::variable_editor::color_names (); + QList default_colors = variable_editor::default_colors (); + QStringList class_names = variable_editor::color_names (); QString class_chars = resource_manager::varedit_color_chars (); int nr_of_classes = class_chars.length (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/settings-dialog.ui --- a/libgui/src/settings-dialog.ui Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/settings-dialog.ui Fri Jul 12 12:14:43 2019 -0400 @@ -32,7 +32,7 @@ - 0 + 2 @@ -53,7 +53,7 @@ 0 0 658 - 571 + 576 @@ -69,6 +69,12 @@ + + + 135 + 0 + + QComboBox::InsertAtBottom @@ -425,7 +431,14 @@ - + + + + 135 + 0 + + + @@ -549,7 +562,7 @@ 0 0 658 - 571 + 576 @@ -829,8 +842,8 @@ 0 0 - 645 - 1213 + 644 + 1322 @@ -845,120 +858,7 @@ 0 - - - - - - false - - - Tab width min. - - - - - - - - 0 - 0 - - - - 80 - - - 600 - - - 20 - - - 160 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - - 0 - 0 - - - - 180 - - - 600 - - - 20 - - - 300 - - - - - - - false - - - max. - - - - - - - - - - 0 - 0 - - - - Show complete path in title - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 0 - - - - - + false @@ -968,7 +868,7 @@ - + @@ -984,7 +884,7 @@ - + true @@ -1000,7 +900,7 @@ - + @@ -1013,7 +913,7 @@ - + @@ -1029,7 +929,7 @@ - + @@ -1045,7 +945,7 @@ - + true @@ -1064,7 +964,7 @@ - + @@ -1107,7 +1007,7 @@ - + @@ -1170,7 +1070,7 @@ - + @@ -1183,6 +1083,22 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 0 + + + + @@ -1256,6 +1172,162 @@ + + + Tabs + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 0 + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Tab position + + + + + + + 0 + + + + + false + + + Tab width min. + + + + + + + + 0 + 0 + + + + 80 + + + 600 + + + 20 + + + 160 + + + + + + + false + + + max. + + + + + + + false + + + + 0 + 0 + + + + 180 + + + 600 + + + 20 + + + 300 + + + + + + + + + + 0 + 0 + + + + Show complete path in title + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Comments (Octave) @@ -2156,7 +2228,7 @@ 0 0 658 - 571 + 576 @@ -2306,7 +2378,7 @@ 0 0 658 - 571 + 576 @@ -2367,8 +2439,8 @@ 0 0 - 669 - 558 + 658 + 576 @@ -2570,7 +2642,7 @@ 0 0 658 - 571 + 576 @@ -2769,7 +2841,7 @@ 0 0 658 - 571 + 576 diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/shortcut-manager.cc --- a/libgui/src/shortcut-manager.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/shortcut-manager.cc Fri Jul 12 12:14:43 2019 -0400 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ enter_shortcut::enter_shortcut (QWidget *p) : QLineEdit (p) { m_direct_shortcut = true; // the shortcut is directly entered + m_shift_modifier = false; // the shift modifier is not added } // new keyPressEvent @@ -68,9 +70,13 @@ if (key == Qt::Key_unknown || key == 0) return; - Qt::KeyboardModifiers modifiers = e->modifiers (); +#if defined (HAVE_QGUIAPPLICATION) + Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers (); //e->modifiers (); +#else + Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers (); //e->modifiers (); +#endif - if (modifiers & Qt::ShiftModifier) + if (m_shift_modifier || (modifiers & Qt::ShiftModifier)) key += Qt::SHIFT; if (modifiers & Qt::ControlModifier) key += Qt::CTRL; @@ -92,6 +98,16 @@ m_direct_shortcut = false; // the shortcut has to be written as text } + // slot for checkbox whether the shift modifier should be added + void enter_shortcut::handle_shift_modifier (int state) + { + if (state) + m_shift_modifier = true; // the shortcut is directly entered + else + m_shift_modifier = false; // the shortcut has to be written as text + } + + shortcut_manager *shortcut_manager::instance = nullptr; shortcut_manager::shortcut_manager (void) @@ -172,13 +188,6 @@ if (! instance) instance = new shortcut_manager (); - if (! instance) - { - error ("unable to create shortcut_manager object!"); - - retval = false; - } - return retval; } @@ -726,6 +735,8 @@ m_dialog->setWindowTitle (tr ("Enter new Shortcut")); QVBoxLayout *box = new QVBoxLayout (m_dialog); + box->setSpacing (2); + box->setContentsMargins (12, 12, 12, 12); QLabel *help = new QLabel (tr ("Apply the desired shortcut or click " "on the right button to reset the " @@ -735,8 +746,20 @@ QCheckBox *direct = new QCheckBox ( tr ("Enter shortcut directly by performing it")); + QCheckBox *shift = new QCheckBox ( + tr ("Add Shift modifier\n" + "(allows to enter number keys)")); + shift->setStyleSheet ("QCheckBox::indicator { subcontrol-position: left top; }"); + + connect (direct, SIGNAL (clicked (bool)), + shift, SLOT (setEnabled (bool))); + direct->setCheckState (Qt::Checked); + box->addWidget (direct); + box->addWidget (shift); + + box->addSpacing (15); QGridLayout *grid = new QGridLayout (); @@ -759,6 +782,8 @@ box->addLayout (grid); + box->addSpacing (18); + QDialogButtonBox *button_box = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QList buttons = button_box->buttons (); @@ -772,6 +797,8 @@ connect (direct, SIGNAL (stateChanged (int)), m_edit_actual, SLOT (handle_direct_shortcut (int))); + connect (shift, SIGNAL (stateChanged (int)), + m_edit_actual, SLOT (handle_shift_modifier (int))); connect (m_dialog, SIGNAL (finished (int)), this, SLOT (shortcut_dialog_finished (int))); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/shortcut-manager.h --- a/libgui/src/shortcut-manager.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/shortcut-manager.h Fri Jul 12 12:14:43 2019 -0400 @@ -48,10 +48,12 @@ public slots: void handle_direct_shortcut (int); + void handle_shift_modifier (int); private: bool m_direct_shortcut; + bool m_shift_modifier; }; diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/variable-editor-model.cc --- a/libgui/src/variable-editor-model.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/variable-editor-model.cc Fri Jul 12 12:14:43 2019 -0400 @@ -46,48 +46,48 @@ #include "utils.h" #include "variables.h" -static bool -cell_is_editable (const octave_value& val) -{ - if ((val.isnumeric () || val.islogical ()) && val.numel () == 1) - return true; - - if (val.is_string () && (val.rows () == 1 || val.is_zero_by_zero ())) - return true; - - return false; -} - -static char -get_quote_char (const octave_value& val) -{ - if (val.is_sq_string ()) - return '\''; - - if (val.is_dq_string ()) - return '"'; - - return 0; -} - -static float_display_format -get_edit_display_format (const octave_value& val) -{ - // FIXME: make this limit configurable. - - return (val.numel () > 250000 - ? float_display_format () : val.get_edit_display_format ()); -} - -static bool -do_requires_sub_editor_sub (const octave_value& elt) -{ - return (! ((elt.numel () == 1 && (elt.isnumeric () || elt.islogical ())) - || (elt.is_string () && (elt.rows () == 1 || elt.isempty ())))); -} - namespace octave { + static bool + cell_is_editable (const octave_value& val) + { + if ((val.isnumeric () || val.islogical ()) && val.numel () == 1) + return true; + + if (val.is_string () && (val.rows () == 1 || val.is_zero_by_zero ())) + return true; + + return false; + } + + static char + get_quote_char (const octave_value& val) + { + if (val.is_sq_string ()) + return '\''; + + if (val.is_dq_string ()) + return '"'; + + return 0; + } + + static float_display_format + get_edit_display_format (const octave_value& val) + { + // FIXME: make this limit configurable. + + return (val.numel () > 250000 + ? float_display_format () : val.get_edit_display_format ()); + } + + static bool + do_requires_sub_editor_sub (const octave_value& elt) + { + return (! ((elt.numel () == 1 && (elt.isnumeric () || elt.islogical ())) + || (elt.is_string () && (elt.rows () == 1 || elt.isempty ())))); + } + base_ve_model::base_ve_model (const QString& expr, const octave_value& val) : m_name (expr.toStdString ()), m_value (val), @@ -129,8 +129,8 @@ float_format r_fmt = m_display_fmt.real_format (); float_format i_fmt = m_display_fmt.imag_format (); - int rw = r_fmt.fw; - int iw = i_fmt.fw; + int rw = r_fmt.width (); + int iw = i_fmt.width (); if (rw > 0) { diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/variable-editor.cc --- a/libgui/src/variable-editor.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/variable-editor.cc Fri Jul 12 12:14:43 2019 -0400 @@ -54,36 +54,36 @@ #include "variable-editor-model.h" #include "gui-preferences.h" -// Code reuse functions - -static QString -idx_to_expr (int32_t from, int32_t to) -{ - return (from == to - ? QString ("%1").arg (from) - : QString ("%1:%2").arg (from).arg (to)); -} - -static QSignalMapper * -make_plot_mapper (QMenu *menu) -{ - QList list; - list << "plot" << "bar" << "stem" << "stairs" << "area" << "pie" << "hist"; - - QSignalMapper *plot_mapper = new QSignalMapper (menu); - - for (int i = 0; i < list.size(); ++i) - { - plot_mapper->setMapping - (menu->addAction (list.at (i), plot_mapper, SLOT (map ())), - "figure (); " + list.at (i) + " (%1); title (\"%1\");"); - } - - return plot_mapper; -} - namespace octave { + // Code reuse functions + + static QString + idx_to_expr (int32_t from, int32_t to) + { + return (from == to + ? QString ("%1").arg (from) + : QString ("%1:%2").arg (from).arg (to)); + } + + static QSignalMapper * + make_plot_mapper (QMenu *menu) + { + QList list; + list << "plot" << "bar" << "stem" << "stairs" << "area" << "pie" << "hist"; + + QSignalMapper *plot_mapper = new QSignalMapper (menu); + + for (int i = 0; i < list.size(); ++i) + { + plot_mapper->setMapping + (menu->addAction (list.at (i), plot_mapper, SLOT (map ())), + "figure (); " + list.at (i) + " (%1); title (\"%1\");"); + } + + return plot_mapper; + } + // Variable dock widget variable_dock_widget::variable_dock_widget (QWidget *p) diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/welcome-wizard.cc --- a/libgui/src/welcome-wizard.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/welcome-wizard.cc Fri Jul 12 12:14:43 2019 -0400 @@ -39,21 +39,22 @@ #include "welcome-wizard.h" #include "resource-manager.h" -static QLabel * -make_octave_logo (QWidget *p = nullptr, int height = 100) -{ - QLabel *logo = new QLabel (p); - QPixmap logo_pixmap (":/actions/icons/logo.png"); - logo->setPixmap (logo_pixmap.scaledToHeight (height)); - return logo; -}; - namespace octave { + static QLabel * + make_octave_logo (QWidget *p = nullptr, int height = 100) + { + QLabel *logo = new QLabel (p); + QPixmap logo_pixmap (":/actions/icons/logo.png"); + logo->setPixmap (logo_pixmap.scaledToHeight (height)); + return logo; + }; + welcome_wizard::welcome_wizard (QWidget *p) : QDialog (p), m_page_ctor_list (), m_page_list_iterator (), m_current_page (initial_page::create (this)), - m_allow_web_connect_state (false) + m_allow_web_connect_state (false), + m_max_height (0), m_max_width (0) { m_page_ctor_list.push_back (initial_page::create); m_page_ctor_list.push_back (setup_community_news::create); @@ -65,19 +66,21 @@ setEnabled (true); - QDesktopWidget *m_desktop = QApplication::desktop (); - int screen = m_desktop->screenNumber (this); // screen of the main window - QRect screen_geo = m_desktop->availableGeometry (screen); + setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - int win_x = screen_geo.width (); // width of the screen - int win_y = screen_geo.height (); // height of the screen - int ww_x = std::max (win_x/2, 600); // desired width - int ww_y = std::max (win_y*2/3, 480); // desired height + // Create all pages for pre-setting the minimal required size for all pages + show_page (); + adjust_size (); + next_page (); + adjust_size (); + next_page (); + adjust_size (); + // now go back to the first page + previous_page (); + previous_page (); - resize (ww_x, ww_y); - setMinimumSize (QSize (ww_x, ww_y)); - - show_page (); + // Set the size determined above + resize (m_max_width, m_max_height); #if defined (OCTAVE_USE_WINDOWS_API) // HACK to forceshow of dialog if started minimized @@ -85,6 +88,21 @@ #endif } + void welcome_wizard::adjust_size (void) + { + // Get adjusted size for the current page + adjustSize (); + QSize sz = size (); + + // Update the max. size of the three pages if required + + if (sz.height () > m_max_height) + m_max_height = sz.height (); + + if (sz.width () > m_max_width) + m_max_width = sz.width (); + } + void welcome_wizard::handle_web_connect_option (int state) { m_allow_web_connect_state = state == Qt::Checked; @@ -180,8 +198,11 @@ page_layout->addLayout (message_and_logo); page_layout->addStretch (10); + page_layout->addSpacing (20); page_layout->addLayout (button_bar); + setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_next->setDefault (true); m_next->setFocus (); @@ -267,8 +288,11 @@ page_layout->addLayout (message_logo_and_checkbox); page_layout->addStretch (10); + page_layout->addSpacing (20); page_layout->addLayout (button_bar); + setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_next->setDefault (true); m_next->setFocus (); @@ -345,8 +369,11 @@ page_layout->addSpacing (20); page_layout->addWidget (m_links); page_layout->addStretch (10); + page_layout->addSpacing (20); page_layout->addLayout (button_bar); + setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_finish->setDefault (true); m_finish->setFocus (); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/welcome-wizard.h --- a/libgui/src/welcome-wizard.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/welcome-wizard.h Fri Jul 12 12:14:43 2019 -0400 @@ -42,12 +42,16 @@ ~welcome_wizard (void) = default; + void adjust_size (void); + private: QList m_page_ctor_list; QList::iterator m_page_list_iterator; QWidget *m_current_page; bool m_allow_web_connect_state; + int m_max_height; + int m_max_width; private slots: diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/workspace-model.cc --- a/libgui/src/workspace-model.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/workspace-model.cc Fri Jul 12 12:14:43 2019 -0400 @@ -48,7 +48,12 @@ m_columnNames.append (tr ("Value")); m_columnNames.append (tr ("Attribute")); - for (int i = 0; i < resource_manager::storage_class_chars ().length (); i++) + // Initialize the bachground and foreground colors of special + // classes in the workspace view. The structure is + // m_storage_class_colors(1,2,...,colors): background colors + // m_storage_class_colors(colors+1,...,2*colors): foreground colors + int colors = resource_manager::storage_class_chars ().length (); + for (int i = 0; i < 2*colors; i++) m_storage_class_colors.append (QColor (Qt::white)); } @@ -61,10 +66,7 @@ if (colors.isEmpty ()) { colors << QColor (190, 255, 255) - << QColor (220, 255, 220) - << QColor (220, 220, 255) << QColor (255, 255, 190) - << QColor (255, 220, 220) << QColor (255, 190, 255); } @@ -78,11 +80,8 @@ if (names.isEmpty ()) { - names << QObject::tr ("automatic") - << QObject::tr ("function") + names << QObject::tr ("argument") << QObject::tr ("global") - << QObject::tr ("hidden") - << QObject::tr ("inherited") << QObject::tr ("persistent"); } @@ -134,13 +133,21 @@ if (idx.isValid ()) { - if (role == Qt::BackgroundColorRole && m_enable_colors) + if ((role == Qt::BackgroundColorRole || role == Qt::ForegroundRole) + && m_enable_colors) { QString class_chars = resource_manager::storage_class_chars (); int actual_class = class_chars.indexOf (m_scopes[idx.row ()].toLatin1 ()); if (actual_class >= 0) - return QVariant (m_storage_class_colors.at (actual_class)); + { + // Valid class: Get backgorund (normal indexes) or foreground + // color (indexes with offset) + if (role == Qt::ForegroundRole) + actual_class += class_chars.length (); + + return QVariant (m_storage_class_colors.at (actual_class)); + } else return retval; } @@ -206,31 +213,6 @@ return retval; } - bool - workspace_model::setData (const QModelIndex& idx, const QVariant& value, - int role) - { - bool retval = false; - - if (idx.column () == 0 && role == Qt::EditRole) - { - QString qold_name = m_symbols[idx.row ()]; - - QString qnew_name = value.toString (); - - std::string new_name = qnew_name.toStdString (); - - if (valid_identifier (new_name)) - { - emit rename_variable (qold_name, qnew_name); - - retval = true; - } - } - - return retval; - } - void workspace_model::set_workspace (bool top_level, bool /* debug */, const symbol_info_list& syminfo) @@ -266,7 +248,13 @@ QColor setting_color = settings->value ("workspaceview/color_" + class_chars.mid (i,1), default_var).value (); + + QPalette p (setting_color); m_storage_class_colors.replace (i,setting_color); + + QColor fg_color = p.color (QPalette::WindowText); + m_storage_class_colors.replace (i + class_chars.length (), fg_color); + } } @@ -301,14 +289,12 @@ dv(i) = sz(i); char storage = ' '; - if (syminfo.is_global ()) + if (syminfo.is_formal ()) + storage = 'a'; + else if (syminfo.is_global ()) storage = 'g'; else if (syminfo.is_persistent ()) storage = 'p'; - else if (syminfo.is_automatic ()) - storage = 'a'; - else if (syminfo.is_formal ()) - storage = 'f'; std::ostringstream buf; val.short_disp (buf); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/workspace-model.h --- a/libgui/src/workspace-model.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/workspace-model.h Fri Jul 12 12:14:43 2019 -0400 @@ -66,9 +66,6 @@ QVariant data (const QModelIndex& index, int role) const; - bool setData (const QModelIndex& index, const QVariant& value, - int role = Qt::EditRole); - bool is_top_level (void) const { return m_top_level; } QColor storage_class_color (int s_class) @@ -92,8 +89,6 @@ void model_changed (void); void prompt_variable_editor(void); - void rename_variable (const QString& old_name, const QString& new_name); - private: void clear_data (void); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/workspace-view.cc --- a/libgui/src/workspace-view.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/workspace-view.cc Fri Jul 12 12:14:43 2019 -0400 @@ -44,6 +44,7 @@ #include "resource-manager.h" #include "interpreter-private.h" +#include "interpreter.h" #include "syminfo.h" namespace octave @@ -198,11 +199,13 @@ { tool_tip = QString (tr ("View the variables in the active workspace.
")); tool_tip += QString (tr ("Colors for variable attributes:")); - for (i = 0; i < resource_manager::storage_class_chars ().length (); i++) + int colors = resource_manager::storage_class_chars ().length (); + for (i = 0; i < colors; i++) { tool_tip += - QString (R"(
%2
)") + QString (R"(
%3
)") .arg (m_model->storage_class_color (i).name ()) + .arg (m_model->storage_class_color (i + colors).name ()) .arg (resource_manager::storage_class_names ().at (i)); } } @@ -354,10 +357,9 @@ QAction *rename = menu.addAction (tr ("Rename"), this, SLOT (handle_contextmenu_rename ())); - QAbstractItemModel *m = m_view->model (); - const workspace_model *wm = static_cast (m); - - if (! wm->is_top_level ()) + // Use m_model here instead of using "m_view->model ()" because + // that points to the proxy model. + if (! m_model->is_top_level ()) { rename->setDisabled (true); rename->setToolTip (tr ("Only top-level symbols may be renamed")); @@ -415,10 +417,13 @@ { QString var_name = get_var_name (index); - symbol_scope scope - = __get_current_scope__ ("workspace_view::handle_contextmenu_copy_value"); + // FIXME: this looks suspciously unsafe. + interpreter& interp + = __get_interpreter__ ("workspace_view::handle_contextmenu_copy_value"); - octave_value val = scope ? scope.varval (var_name.toStdString ()) : 0; + octave_value val = interp.varval (var_name.toStdString ()); + if (val.is_undefined ()) + val = 0; std::ostringstream buf; val.print_raw (buf, true); @@ -447,10 +452,7 @@ QLineEdit::Normal, var_name, &ok); if (ok && ! new_name.isEmpty ()) - { - QAbstractItemModel *m = m_view->model (); - m->setData (index, new_name, Qt::EditRole); - } + emit rename_variable_signal (var_name, new_name); } } @@ -464,7 +466,6 @@ QString var_name = get_var_name (index); symbol_info_list syminfo = m_model->get_symbol_info (); - octave_value val = syminfo.varval (var_name.toStdString ()); emit edit_variable_signal (var_name, val); diff -r 940c1b6e3453 -r 61701d1317a1 libgui/src/workspace-view.h --- a/libgui/src/workspace-view.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libgui/src/workspace-view.h Fri Jul 12 12:14:43 2019 -0400 @@ -63,6 +63,10 @@ void command_requested (const QString& cmd); + //! Signal that user wants to rename a variable. + + void rename_variable_signal (const QString&, const QString&); + //! Signal that user wants to edit a variable. void edit_variable_signal (const QString&, const octave_value&); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/build-env.h --- a/libinterp/build-env.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/build-env.h Fri Jul 12 12:14:43 2019 -0400 @@ -161,6 +161,9 @@ extern const char *SUNDIALS_NVECSERIAL_CPPFLAGS; extern const char *SUNDIALS_NVECSERIAL_LDFLAGS; extern const char *SUNDIALS_NVECSERIAL_LIBS; + extern const char *SUNDIALS_SUNLINSOLKLU_CPPFLAGS; + extern const char *SUNDIALS_SUNLINSOLKLU_LDFLAGS; + extern const char *SUNDIALS_SUNLINSOLKLU_LIBS; extern const char *TERM_LIBS; extern const char *UMFPACK_CPPFLAGS; extern const char *UMFPACK_LDFLAGS; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/build-env.in.cc --- a/libinterp/build-env.in.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/build-env.in.cc Fri Jul 12 12:14:43 2019 -0400 @@ -289,6 +289,12 @@ const char *SUNDIALS_NVECSERIAL_LIBS = %OCTAVE_CONF_SUNDIALS_NVECSERIAL_LIBS%; + const char *SUNDIALS_SUNLINSOLKLU_CPPFLAGS = %OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_CPPFLAGS%; + + const char *SUNDIALS_SUNLINSOLKLU_LDFLAGS = %OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LDFLAGS%; + + const char *SUNDIALS_SUNLINSOLKLU_LIBS = %OCTAVE_CONF_SUNDIALS_SUNLINSOLKLU_LIBS%; + const char *TERM_LIBS = %OCTAVE_CONF_TERM_LIBS%; const char *UMFPACK_CPPFLAGS = %OCTAVE_CONF_UMFPACK_CPPFLAGS%; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/Cell.cc --- a/libinterp/corefcn/Cell.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/Cell.cc Fri Jul 12 12:14:43 2019 -0400 @@ -166,6 +166,7 @@ switch (n) { case 0: + warn_empty_index ("cell array"); retval = *this; break; @@ -211,9 +212,11 @@ } /* -%!test +%% This behavior is required for Matlab compatibility. +%!shared a %! a = {"foo", "bar"}; -%! assert (a(), a); +%!assert (a(), a); +%!error a{} */ void diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/Cell.h --- a/libinterp/corefcn/Cell.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/Cell.h Fri Jul 12 12:14:43 2019 -0400 @@ -40,8 +40,13 @@ { public: - Cell (void) - : Array (dim_vector (0, 0)) { } + Cell (void) = default; + + Cell (const Cell& c) = default; + + Cell& operator = (const Cell& c) = default; + + ~Cell (void) = default; Cell (const octave_value& val) : Array (dim_vector (1, 1), val) { } @@ -87,9 +92,6 @@ Cell (const dim_vector& dv, const string_vector& sv, bool trim = false); - Cell (const Cell& c) - : Array (c) { } - bool iscellstr (void) const; Array cellstr_value (void) const; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/__ichol__.cc --- a/libinterp/corefcn/__ichol__.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/__ichol__.cc Fri Jul 12 12:14:43 2019 -0400 @@ -453,7 +453,7 @@ } /* -%!test <51736> +%!test <*51736> %! k = 4; %! n = 2^k; %! Afull = diag (ones (n,1)) / ... diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/__qp__.cc --- a/libinterp/corefcn/__qp__.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/__qp__.cc Fri Jul 12 12:14:43 2019 -0400 @@ -101,15 +101,13 @@ qp (const Matrix& H, const ColumnVector& q, const Matrix& Aeq, const ColumnVector& beq, const Matrix& Ain, const ColumnVector& bin, - int maxit, + int maxit, double rtol, ColumnVector& x, ColumnVector& lambda, int& iter) { int info = 0; iter = 0; - double rtol = sqrt (std::numeric_limits::epsilon ()); - // Problem dimension. octave_idx_type n = x.numel (); @@ -489,7 +487,7 @@ Undocumented internal function. @end deftypefn */) { - if (args.length () != 8) + if (args.length () != 9) print_usage (); const ColumnVector x0 (args(0).vector_value ()); @@ -500,6 +498,7 @@ const Matrix Ain (args(5).matrix_value ()); const ColumnVector bin (args(6).vector_value ()); const int maxit (args(7).int_value ()); + const double rtol (args(8).double_value()); int iter = 0; @@ -509,7 +508,7 @@ // Reordering the Lagrange multipliers ColumnVector lambda; - int info = qp (H, q, Aeq, beq, Ain, bin, maxit, x, lambda, iter); + int info = qp (H, q, Aeq, beq, Ain, bin, maxit, rtol, x, lambda, iter); return ovl (x, lambda, info, iter); } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/base-text-renderer.h --- a/libinterp/corefcn/base-text-renderer.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/base-text-renderer.h Fri Jul 12 12:14:43 2019 -0400 @@ -52,6 +52,9 @@ virtual ~base_text_renderer (void) = default; + virtual void + set_anti_aliasing (bool val) = 0; + virtual Matrix get_extent (text_element *elt, double rotation) = 0; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/call-stack.cc --- a/libinterp/corefcn/call-stack.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/call-stack.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,12 +24,15 @@ # include "config.h" #endif +#include + #include "lo-regexp.h" #include "str-vec.h" #include "call-stack.h" #include "defun.h" #include "interpreter.h" +#include "interpreter-private.h" #include "oct-map.h" #include "ov.h" #include "ov-fcn.h" @@ -37,291 +40,157 @@ #include "ov-usr-fcn.h" #include "pager.h" #include "parse.h" +#include "stack-frame.h" +#include "stack-frame-walker.h" #include "syminfo.h" +#include "syminfo-accumulator.h" #include "symrec.h" #include "symscope.h" #include "variables.h" -// Use static fields for the best efficiency. -// NOTE: C++0x will allow these two to be merged into one. -static const char *bt_fieldnames[] = - { "file", "name", "line", "column", "scope", "context", nullptr }; -static const octave_fields bt_fields (bt_fieldnames); - namespace octave { - std::string - call_stack::stack_frame::fcn_file_name (void) const - { - return m_fcn ? m_fcn->fcn_file_name () : ""; - } - - std::string - call_stack::stack_frame::fcn_name (bool print_subfn) const - { - std::string retval; + // Use static fields for the best efficiency. + // NOTE: C++0x will allow these two to be merged into one. + static const char *bt_fieldnames[] = + { "file", "name", "line", "column", "scope", nullptr }; - if (m_fcn) - { - std::string parent_fcn_name = m_fcn->parent_fcn_name (); - - if (print_subfn && ! parent_fcn_name.empty ()) - retval = parent_fcn_name + '>'; + static const octave_fields bt_fields (bt_fieldnames); - if (m_fcn->is_anonymous_function ()) - retval += octave_fcn_handle::anonymous; - else - retval += m_fcn->name (); - } - else - retval = ""; - - return retval; + call_stack::call_stack (tree_evaluator& evaluator) + : m_evaluator (evaluator), m_cs (), m_curr_frame (0), + m_max_stack_depth (1024), m_global_values () + { + push (symbol_scope ("top scope")); } - bool - call_stack::stack_frame::operator == (const call_stack::stack_frame& rhs) const - { - if (this->line () != rhs.line ()) - return false; - else if (this->column () != rhs.column ()) - return false; - else if (this->fcn_file_name () != rhs.fcn_file_name ()) - return false; - else if (this->fcn_name () != rhs.fcn_name ()) - return false; - else - return true; - } - - symbol_info_list - call_stack::stack_frame::make_symbol_info_list - (const std::list& symrec_list) const - { - symbol_info_list symbol_stats; - - for (const auto& sr : symrec_list) - { - octave_value value = sr.varval (m_context); - - if (value.is_defined ()) - { - symbol_info syminf (sr.name (), value, sr.is_automatic (), - value.iscomplex (), sr.is_formal (), - sr.is_global (), sr.is_persistent ()); - - symbol_stats.append (syminf); - } - } - - return symbol_stats; - } - - symbol_info_list - call_stack::stack_frame::glob_symbol_info (const std::string& pat) const - { - return make_symbol_info_list (m_scope.glob (pat, true)); - } - - symbol_info_list - call_stack::stack_frame::regexp_symbol_info (const std::string& pat) const - { - return make_symbol_info_list (m_scope.regexp (pat, true)); - } - - symbol_info_list call_stack::stack_frame::get_symbol_info (void) const - { - return make_symbol_info_list (m_scope.all_variables ()); - } - - call_stack::call_stack (interpreter& interp) - : cs (), curr_frame (0), m_max_stack_depth (1024), m_interpreter (interp) - { - symbol_table& symtab = m_interpreter.get_symbol_table (); - - push (nullptr, nullptr, symtab.top_scope (), 0); - } - - int - call_stack::current_line (void) const + int call_stack::current_line (void) const { int retval = -1; - if (! cs.empty ()) + if (! m_cs.empty ()) { - const stack_frame& elt = cs[curr_frame]; - retval = elt.m_line; + const stack_frame *elt = m_cs[m_curr_frame]; + retval = elt->line (); } return retval; } - int - call_stack::current_column (void) const + int call_stack::current_column (void) const { int retval = -1; - if (! cs.empty ()) + if (! m_cs.empty ()) { - const stack_frame& elt = cs[curr_frame]; - retval = elt.m_column; + const stack_frame *elt = m_cs[m_curr_frame]; + retval = elt->column (); } return retval; } - size_t - call_stack::num_user_code_frames (octave_idx_type& curr_user_frame) const + octave_user_code * call_stack::current_user_code (void) const { - size_t retval = 0; - - curr_user_frame = 0; - - // Look for the caller of dbstack. - size_t xframe = cs[curr_frame].m_prev; + // Start at current frame. - bool found = false; - - size_t k = cs.size (); - - for (auto p = cs.crbegin (); p != cs.crend (); p++) - { - octave_function *f = (*p).m_fcn; - - if (--k == xframe) - found = true; + size_t xframe = find_current_user_frame (); - if (f && f->is_user_code ()) - { - if (! found) - curr_user_frame++; - - retval++; - } - } - - // We counted how many user frames were not the one, in reverse. - // Now set curr_user_frame to be the index in the other direction. - curr_user_frame = retval - curr_user_frame - 1; + if (xframe > 0) + { + const stack_frame *elt = m_cs[xframe]; - return retval; - } - - octave_user_code * - call_stack::caller_user_code (size_t nskip) const - { - octave_user_code *retval = nullptr; - - auto p = cs.cend (); - - while (p != cs.cbegin ()) - { - const stack_frame& elt = *(--p); - - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && f->is_user_code ()) - { - if (nskip > 0) - nskip--; - else - { - retval = dynamic_cast (f); - break; - } - } - } - - return retval; - } - - int - call_stack::caller_user_code_line (void) const - { - int retval = -1; - - auto p = cs.cend (); - - while (p != cs.cbegin ()) - { - const stack_frame& elt = *(--p); - - octave_function *f = elt.m_fcn; - - if (f && f->is_user_code ()) - { - if (elt.m_line > 0) - { - retval = elt.m_line; - break; - } - } - } - - return retval; - } - - unwind_protect * - call_stack::curr_fcn_unwind_protect_frame (void) const - { - auto p = cs.cend (); - - while (p != cs.cbegin ()) - { - const stack_frame& elt = *(--p); - - octave_function *f = elt.m_fcn; - - if (f && f->is_user_code ()) - return elt.m_unwind_protect_frame; + return dynamic_cast (f); } return nullptr; } - int - call_stack::caller_user_code_column (void) const + int call_stack::current_user_code_line (void) const { - int retval = -1; + // Start at current frame. + + size_t xframe = find_current_user_frame (); - auto p = cs.cend (); - - while (p != cs.cbegin ()) + if (xframe > 0) { - const stack_frame& elt = *(--p); + const stack_frame *elt = m_cs[xframe]; - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && f->is_user_code ()) { - if (elt.m_column) - { - retval = elt.m_column; - break; - } + int line = elt->line (); + + if (line > 0) + return line; } } - return retval; + return -1; } - octave_user_code * - call_stack::debug_user_code (void) const + int call_stack::current_user_code_column (void) const + { + // Start at current frame. + + size_t xframe = find_current_user_frame (); + + if (xframe > 0) + { + const stack_frame *elt = m_cs[xframe]; + + octave_function *f = elt->function (); + + if (f && f->is_user_code ()) + { + int column = elt->column (); + + if (column > 0) + return column; + } + } + + return -1; + } + + unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const + { + // Start at current frame. + + size_t xframe = find_current_user_frame (); + + if (xframe > 0) + { + const stack_frame *elt = m_cs[xframe]; + + octave_function *f = elt->function (); + + if (f && f->is_user_code ()) + return elt->unwind_protect_frame (); + } + + return nullptr; + } + + octave_user_code * call_stack::debug_user_code (void) const { octave_user_code *retval = nullptr; // This should never happen... - if (curr_frame == 0) + if (m_curr_frame == 0) return retval; - // Start looking with the caller of the calling debug function. - size_t i = cs[curr_frame].m_prev; + size_t i = m_curr_frame; while (i != 0) { - const stack_frame& elt = cs[i--]; + const stack_frame *elt = m_cs[i--]; - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && f->is_user_code ()) { @@ -333,29 +202,27 @@ return retval; } - int - call_stack::debug_user_code_line (void) const + int call_stack::debug_user_code_line (void) const { int retval = -1; // This should never happen... - if (curr_frame == 0) + if (m_curr_frame == 0) return retval; - // Start looking with the caller of the calling debug function. - size_t i = cs[curr_frame].m_prev; + size_t i = m_curr_frame; while (i != 0) { - const stack_frame& elt = cs[i--]; + const stack_frame *elt = m_cs[i--]; - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && f->is_user_code ()) { - if (elt.m_line) + if (elt->line ()) { - retval = elt.m_line; + retval = elt->line (); break; } } @@ -364,29 +231,28 @@ return retval; } - int - call_stack::debug_user_code_column (void) const + int call_stack::debug_user_code_column (void) const { int retval = -1; // This should never happen... - if (curr_frame == 0) + if (m_curr_frame == 0) return retval; // Start looking with the caller of the calling debug function. - size_t i = cs[curr_frame].m_prev; + size_t i = m_curr_frame; while (i != 0) { - const stack_frame& elt = cs[i--]; + const stack_frame *elt = m_cs[i--]; - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && f->is_user_code ()) { - if (elt.m_column) + if (elt->column ()) { - retval = elt.m_column; + retval = elt->column (); break; } } @@ -395,18 +261,55 @@ return retval; } - bool - call_stack::all_scripts (void) const + std::string call_stack::get_dispatch_class (void) const + { + return m_cs[m_curr_frame]->get_dispatch_class (); + } + + void call_stack::set_dispatch_class (const std::string& class_name) + { + m_cs[m_curr_frame]->set_dispatch_class (class_name); + } + + bool call_stack::is_class_method_executing (std::string& dispatch_class) const + { + dispatch_class = ""; + + octave_function *f = current (); + + bool retval = (f && f->is_class_method ()); + + if (retval) + dispatch_class = f->dispatch_class (); + + return retval; + } + + bool call_stack::is_class_constructor_executing (std::string& dispatch_class) const + { + dispatch_class = ""; + + octave_function *f = current (); + + bool retval = (f && f->is_class_constructor ()); + + if (retval) + dispatch_class = f->dispatch_class (); + + return retval; + } + + bool call_stack::all_scripts (void) const { bool retval = true; - auto p = cs.cend (); + auto p = m_cs.cend (); - while (p != cs.cbegin ()) + while (p != m_cs.cbegin ()) { - const stack_frame& elt = *(--p); + const stack_frame *elt = *(--p); - octave_function *f = elt.m_fcn; + octave_function *f = elt->function (); if (f && ! f->is_user_script ()) { @@ -418,229 +321,310 @@ return retval; } - void - call_stack::push (octave_function *fcn, unwind_protect *up_frame) + stack_frame * call_stack::get_static_link (size_t prev_frame) const { - symbol_table& symtab = m_interpreter.get_symbol_table (); + // FIXME: is there a better way? + + stack_frame *slink = nullptr; + + if (m_curr_frame > 0) + { + octave_function *t_fcn = m_cs[prev_frame]->function (); - push (fcn, up_frame, symtab.current_scope (), symtab.current_context ()); + slink = (t_fcn + ? (t_fcn->is_user_code () + ? m_cs[prev_frame] : m_cs[prev_frame]->static_link ()) + : m_cs[prev_frame]); + } + + return slink; } - void - call_stack::push (octave_function *fcn, unwind_protect *up_frame, - const symbol_scope& scope, - symbol_record::context_id context) + void call_stack::push (const symbol_scope& scope) { - size_t prev_frame = curr_frame; - curr_frame = cs.size (); + size_t prev_frame = m_curr_frame; + m_curr_frame = m_cs.size (); // m_max_stack_depth should never be less than zero. - if (curr_frame > static_cast (m_max_stack_depth)) + if (m_curr_frame > static_cast (m_max_stack_depth)) error ("max_stack_depth exceeded"); - cs.push_back (stack_frame (fcn, up_frame, scope, context, prev_frame)); + stack_frame *slink = get_static_link (prev_frame); - symbol_table& symtab = m_interpreter.get_symbol_table (); + stack_frame *new_frame + = new scope_stack_frame (m_evaluator, scope, m_curr_frame, slink); - symtab.set_scope_and_context (scope, context); + m_cs.push_back (new_frame); } - bool - call_stack::goto_frame (size_t n, bool verbose) + void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame, + stack_frame *closure_frames) { - bool retval = false; - - if (n < cs.size ()) - { - retval = true; - - curr_frame = n; - - const stack_frame& elt = cs[n]; + size_t prev_frame = m_curr_frame; + m_curr_frame = m_cs.size (); - symbol_table& symtab = m_interpreter.get_symbol_table (); + // m_max_stack_depth should never be less than zero. + if (m_curr_frame > static_cast (m_max_stack_depth)) + error ("max_stack_depth exceeded"); - symtab.set_scope_and_context (elt.m_scope, elt.m_context); + stack_frame *slink = get_static_link (prev_frame); - if (verbose) - octave_stdout << "stopped in " << elt.fcn_name () - << " at line " << elt.m_line - << " column " << elt.m_column - << " [" << elt.fcn_file_name () << "] " - << "[context = " << elt.m_context << "])" - << std::endl; - } + stack_frame *new_frame + = new user_fcn_stack_frame (m_evaluator, fcn, up_frame, m_curr_frame, + slink, closure_frames); - return retval; + m_cs.push_back (new_frame); } - bool - call_stack::goto_frame_relative (int nskip, bool verbose) + void call_stack::push (octave_user_script *script, unwind_protect *up_frame) + { + size_t prev_frame = m_curr_frame; + m_curr_frame = m_cs.size (); + + // m_max_stack_depth should never be less than zero. + if (m_curr_frame > static_cast (m_max_stack_depth)) + error ("max_stack_depth exceeded"); + + stack_frame *slink = get_static_link (prev_frame); + + stack_frame *new_frame + = new script_stack_frame (m_evaluator, script, up_frame, m_curr_frame, + slink); + + m_cs.push_back (new_frame); + } + + void call_stack::push (octave_function *fcn) + { + size_t prev_frame = m_curr_frame; + m_curr_frame = m_cs.size (); + + // m_max_stack_depth should never be less than zero. + if (m_curr_frame > static_cast (m_max_stack_depth)) + error ("max_stack_depth exceeded"); + + stack_frame *slink = get_static_link (prev_frame); + + stack_frame *new_frame + = new compiled_fcn_stack_frame (m_evaluator, fcn, m_curr_frame, slink); + + m_cs.push_back (new_frame); + } + + bool call_stack::goto_frame (size_t n, bool verbose) { bool retval = false; - int incr = 0; - - if (nskip < 0) - incr = -1; - else if (nskip > 0) - incr = 1; - - // Start looking with the caller of dbup/dbdown/keyboard. - size_t xframe = cs[curr_frame].m_prev; - - while (true) + if (n < m_cs.size ()) { - if ((incr < 0 && xframe == 0) || (incr > 0 && xframe == cs.size () - 1)) - break; - - xframe += incr; - - const stack_frame& elt = cs[xframe]; - - octave_function *f = elt.m_fcn; - - if (xframe == 0 || (f && f->is_user_code ())) - { - if (nskip > 0) - nskip--; - else if (nskip < 0) - nskip++; - - if (nskip == 0) - { - curr_frame = xframe; - cs[cs.size () - 1].m_prev = curr_frame; - - symbol_table& symtab = m_interpreter.get_symbol_table (); - - symtab.set_scope_and_context (elt.m_scope, elt.m_context); + retval = true; - if (verbose) - { - std::ostringstream buf; - - if (f) - buf << "stopped in " << elt.fcn_name () - << " at line " << elt.m_line - << " [" << elt.fcn_file_name () << "] " - << std::endl; - else - buf << "at top level" << std::endl; - - octave_stdout << buf.str (); - } - - retval = true; - break; - } - } - else if (incr == 0) // Break out of infinite loop by choosing an incr. - incr = -1; - - // There is no need to set scope and context here. That will - // happen when the dbup/dbdown/keyboard frame is popped and we - // jump to the new "prev" frame set above. - } - - return retval; - } - - void - call_stack::goto_caller_frame (void) - { - size_t xframe = curr_frame; - - bool skipped = false; + m_curr_frame = n; - while (xframe != 0) - { - xframe = cs[xframe].m_prev; - - const stack_frame& elt = cs[xframe]; - - octave_function *f = elt.m_fcn; - - if (elt.m_scope == cs[0].m_scope || (f && f->is_user_code ())) + if (verbose) { - if (! skipped) - // We found the current user code frame, so skip it. - skipped = true; - else - { - // We found the caller user code frame. - stack_frame tmp (elt); - tmp.m_prev = curr_frame; - - curr_frame = cs.size (); - - cs.push_back (tmp); - - symbol_table& symtab = m_interpreter.get_symbol_table (); - - symtab.set_scope_and_context (tmp.m_scope, tmp.m_context); - - break; - } - } - } - } + const stack_frame *elt = m_cs[n]; - void - call_stack::goto_base_frame (void) - { - stack_frame tmp (cs[0]); - tmp.m_prev = curr_frame; - - curr_frame = cs.size (); - - cs.push_back (tmp); - - symbol_table& symtab = m_interpreter.get_symbol_table (); - - symtab.set_scope_and_context (tmp.m_scope, tmp.m_context); - } - - std::list - call_stack::backtrace_frames (size_t nskip, - octave_idx_type& curr_user_frame) const - { - std::list retval; - - size_t user_code_frames = num_user_code_frames (curr_user_frame); - - size_t nframes = (nskip <= user_code_frames ? user_code_frames - nskip : 0); - - // Our list is reversed. - curr_user_frame = nframes - curr_user_frame - 1; - - if (nframes > 0) - { - for (auto p = cs.crbegin (); p != cs.crend (); p++) - { - const stack_frame& elt = *p; - - octave_function *f = elt.m_fcn; - - if (f && f->is_user_code ()) - { - if (nskip > 0) - nskip--; - else - retval.push_back (elt); - } + elt->display_stopped_in_message (octave_stdout); } } return retval; } - octave_map - call_stack::backtrace (size_t nskip, octave_idx_type& curr_user_frame, - bool print_subfn) const + size_t call_stack::find_current_user_frame (void) const + { + size_t user_frame = m_curr_frame; + + stack_frame *frm = m_cs[user_frame]; + + if (! (frm->is_user_fcn_frame () || frm->is_user_script_frame () + || frm->is_scope_frame ())) + { + frm = frm->static_link (); + + user_frame = frm->index (); + } + + return user_frame; + } + + stack_frame *call_stack::current_user_frame (void) const + { + size_t frame = find_current_user_frame (); + + return m_cs[frame]; + } + + // Go to the Nth frame (up if N is negative or down if positive) in + // the call stack that corresponds to a script, function, or scope + // beginning with the frame indexed by START. + + size_t call_stack::dbupdown (size_t start, int n, bool verbose) { - std::list frames - = backtrace_frames (nskip, curr_user_frame); + if (start >= m_cs.size ()) + error ("invalid stack frame"); + + // Can't go up from here. + + if (start == 0 && n < 0) + { + if (verbose) + m_cs[start]->display_stopped_in_message (octave_stdout); + + return start; + } + + stack_frame *frm = m_cs[start]; + + if (! (frm && (frm->is_user_fcn_frame () + || frm->is_user_script_frame () + || frm->is_scope_frame ()))) + error ("call_stack::dbupdown: invalid initial frame in call stack!"); + + // Use index into the call stack to begin the search. At this point + // we iterate up or down using indexing instead of static links + // because ... FIXME: it's a bit complicated, but deserves + // explanation. May be easiest with some pictures of the call stack + // for an example or two. + + size_t xframe = frm->index (); + + if (n == 0) + { + if (verbose) + frm->display_stopped_in_message (octave_stdout); + + return xframe; + } + + int incr = 0; + + if (n < 0) + { + incr = -1; + n = -n; + } + else if (n > 0) + incr = 1; + + size_t last_good_frame = 0; + + while (true) + { + frm = m_cs[xframe]; + + if (frm->is_user_fcn_frame () || frm->is_user_script_frame () + || frm->is_scope_frame ()) + { + last_good_frame = xframe; + + if (n == 0) + break; + + n--; + } + + xframe += incr; + + if (xframe == 0) + { + last_good_frame = 0; + break; + } + + if (xframe == m_cs.size ()) + break; + } + + if (verbose) + m_cs[last_good_frame]->display_stopped_in_message (octave_stdout); + + return last_good_frame; + } + + // Like dbupdown above but find the starting frame automatically from + // the current frame. If the current frame is already a user + // function, script, or scope frame, use that. Otherwise, follow + // the static link for the current frame. If that is not a user + // function, script or scope frame then there is an error in the + // implementation. + + size_t call_stack::dbupdown (int n, bool verbose) + { + size_t start = find_current_user_frame (); + + return dbupdown (start, n, verbose); + } + + // May be used to temporarily change the value ov m_curr_frame inside + // a function like evalin. If used in a function like dbup, the new + // value of m_curr_frame would be wiped out when dbup returns and the + // stack frame for dbup is popped. + + void call_stack::goto_caller_frame (void) + { + size_t start = find_current_user_frame (); + + // FIXME: is this supposed to be an error? + if (start == 0) + error ("already at top level"); + + m_curr_frame = dbupdown (start, -1, false); + } + + void call_stack::goto_base_frame (void) + { + if (m_curr_frame > 0) + m_curr_frame = 0; + } + + std::list + call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const + { + std::list frames; + + // curr_frame is the index to the current frame in the overall call + // stack, which includes any compiled function frames and scope + // frames. The curr_user_frame value we set is the index into the + // subset of frames returned in the octave_map object. + + size_t curr_frame = find_current_user_frame (); + + // Don't include top-level stack frame in the list. + + for (size_t n = m_cs.size () - 1; n > 0; n--) + { + stack_frame *frm = m_cs[n]; + + if (frm->is_user_script_frame () || frm->is_user_fcn_frame () + || frm->is_scope_frame ()) + { + if (frm->index () == curr_frame) + curr_user_frame = frames.size (); + + frames.push_back (frm); + } + + if (n == 0) + break; + } + + return frames; + } + + std::list + call_stack::backtrace_frames (void) const + { + octave_idx_type curr_user_frame = -1; + + return backtrace_frames (curr_user_frame); + } + + octave_map call_stack::backtrace (octave_idx_type& curr_user_frame, + bool print_subfn) const + { + std::list frames = backtrace_frames (curr_user_frame); size_t nframes = frames.size (); @@ -650,82 +634,460 @@ Cell& name = retval.contents (1); Cell& line = retval.contents (2); Cell& column = retval.contents (3); - Cell& context = retval.contents (5); octave_idx_type k = 0; - for (const auto& frm : frames) + for (const auto *frm : frames) { - context(k) = frm.m_context; - file(k) = frm.fcn_file_name (); - name(k) = frm.fcn_name (print_subfn); - line(k) = frm.m_line; - column(k) = frm.m_column; + if (frm->is_user_script_frame () || frm->is_user_fcn_frame () + || frm->is_scope_frame ()) + { + file(k) = frm->fcn_file_name (); + name(k) = frm->fcn_name (print_subfn); + line(k) = frm->line (); + column(k) = frm->column (); - k++; + k++; + } } return retval; } - octave_map - call_stack::backtrace (size_t nskip) + octave_map call_stack::backtrace (void) const { octave_idx_type curr_user_frame = -1; - return backtrace (nskip, curr_user_frame, true); + return backtrace (curr_user_frame, true); } - octave_map - call_stack::empty_backtrace (void) const + octave_map call_stack::empty_backtrace (void) const { return octave_map (dim_vector (0, 1), bt_fields); } - void - call_stack::pop (void) + void call_stack::pop (void) { - if (cs.size () > 1) + // Never pop top scope. + // FIXME: is it possible for this case to happen? + + if (m_cs.size () > 1) { - const stack_frame& elt = cs.back (); - curr_frame = elt.m_prev; - cs.pop_back (); - const stack_frame& new_elt = cs[curr_frame]; + stack_frame *elt = m_cs.back (); + + stack_frame *caller = elt->static_link (); - symbol_table& symtab = m_interpreter.get_symbol_table (); + m_curr_frame = caller->index (); - symtab.set_scope_and_context (new_elt.m_scope, new_elt.m_context); + m_cs.pop_back (); + + delete elt; } } - symbol_info_list - call_stack::glob_symbol_info (const std::string& pat) const + void call_stack::clear (void) + { + while (! m_cs.empty ()) + pop (); + } + + symbol_info_list call_stack::all_variables (void) + { + return m_cs[m_curr_frame]->all_variables (); + } + + std::list call_stack::glob (const std::string& pattern) const + { + return m_cs[m_curr_frame]->glob (pattern); + } + + std::list call_stack::regexp (const std::string& pattern) const + { + return m_cs[m_curr_frame]->regexp (pattern); + } + + std::list call_stack::variable_names (void) const + { + return m_cs[m_curr_frame]->variable_names (); + } + + std::list call_stack::global_variable_names (void) const { - return cs[curr_frame].glob_symbol_info (pat); + std::list retval; + + for (const auto& nm_ov : m_global_values) + { + if (nm_ov.second.is_defined ()) + retval.push_back (nm_ov.first); + } + + retval.sort (); + + return retval; + } + + void call_stack::clear_global_variable (const std::string& name) + { + auto p = m_global_values.find (name); + + if (p != m_global_values.end ()) + p->second = octave_value (); + } + + void call_stack::clear_global_variable_pattern (const std::string& pattern) + { + glob_match pat (pattern); + + for (auto& nm_ov : m_global_values) + { + if (pat.match (nm_ov.first)) + nm_ov.second = octave_value (); + } + } + + void call_stack::clear_global_variable_regexp (const std::string& pattern) + { + octave::regexp pat (pattern); + + for (auto& nm_ov : m_global_values) + { + if (pat.is_match (nm_ov.first)) + nm_ov.second = octave_value (); + } + } + + void call_stack::clear_global_variables (void) + { + for (auto& nm_ov : m_global_values) + nm_ov.second = octave_value (); } symbol_info_list - call_stack::regexp_symbol_info (const std::string& pat) const + call_stack::glob_symbol_info (const std::string& pattern) const { - return cs[curr_frame].glob_symbol_info (pat); + return m_cs[m_curr_frame]->glob_symbol_info (pattern); } - symbol_info_list call_stack::get_symbol_info (void) const + symbol_info_list + call_stack::regexp_symbol_info (const std::string& pattern) const { - return cs[curr_frame].get_symbol_info (); + return m_cs[m_curr_frame]->glob_symbol_info (pattern); + } + + symbol_info_list call_stack::get_symbol_info (void) + { + return m_cs[m_curr_frame]->get_symbol_info (); } symbol_info_list call_stack::top_scope_symbol_info (void) const { - return cs[0].get_symbol_info (); + return m_cs[0]->get_symbol_info (); } - octave_value - call_stack::max_stack_depth (const octave_value_list& args, int nargout) + octave_value call_stack::max_stack_depth (const octave_value_list& args, + int nargout) { return set_internal_variable (m_max_stack_depth, args, nargout, "max_stack_depth", 0); } + + void call_stack::make_persistent (const symbol_record& sym) + { + m_cs[m_curr_frame]->make_persistent (sym); + } + + void call_stack::make_global (const symbol_record& sym) + { + m_cs[m_curr_frame]->make_global (sym); + } + + octave_value call_stack::global_varval (const std::string& name) const + { + auto p = m_global_values.find (name); + + return p == m_global_values.end () ? octave_value () : p->second; + } + + octave_value& call_stack::global_varref (const std::string& name) + { + return m_global_values[name]; + } + + octave_value call_stack::get_top_level_value (const std::string& name) const + { + return m_cs[0]->varval (name); + } + + void call_stack::set_top_level_value (const std::string& name, + const octave_value& value) + { + m_cs[0]->assign (name, value); + } + + octave_value call_stack::do_who (int argc, const string_vector& argv, + bool return_list, bool verbose) + { + octave_value retval; + + std::string my_name = argv[0]; + + std::string file_name; + + bool from_file = false; + bool global_only = false; + bool have_regexp = false; + + int i = 1; + while (i < argc) + { + if (argv[i] == "-file") + { + if (from_file) + error ("%s: -file option may only be specified once", + my_name.c_str ()); + + from_file = true; + + if (i == argc - 1) + error ("%s: -file argument must be followed by a filename", + my_name.c_str ()); + + file_name = argv[++i]; + } + else if (argv[i] == "-regexp") + { + have_regexp = true; + } + else if (argv[i] == "global") + global_only = true; + else if (argv[i][0] == '-') + warning ("%s: unrecognized option '%s'", my_name.c_str (), + argv[i].c_str ()); + else + break; + + i++; + } + + int npatterns = argc - i; + string_vector patterns; + if (npatterns > 0) + { + patterns.resize (npatterns); + for (int j = 0; j < npatterns; j++) + patterns[j] = argv[i+j]; + } + else + { + patterns.resize (1); + patterns[0] = "*"; + } + + if (from_file) + { + // FIXME: This is an inefficient manner to implement this as the + // variables are loaded in to a temporary context and then treated. + // It would be better to refactor symbol_info_list to not store the + // symbol records and then use it in load-save.cc (do_load) to + // implement this option there so that the variables are never + // stored at all. + + unwind_protect frame; + + // Set up temporary scope. + + symbol_scope tmp_scope ("$dummy_scope$"); + + push (tmp_scope); + frame.add_method (*this, &call_stack::pop); + + feval ("load", octave_value (file_name), 0); + + std::string newmsg = "Variables in the file " + file_name + ":\n\n"; + + if (global_only) + return do_global_who_two (patterns, have_regexp, return_list, + verbose, newmsg); + else + return do_who_two (patterns, have_regexp, return_list, verbose, + newmsg); + } + else + { + if (global_only) + return do_global_who_two (patterns, have_regexp, return_list, + verbose); + else + return do_who_two (patterns, have_regexp, return_list, verbose); + } + } + + octave_value call_stack::do_who_two (const string_vector& patterns, + bool have_regexp, bool return_list, + bool verbose, const std::string& msg) + { + symbol_info_accumulator sym_inf_accum (patterns, have_regexp); + + m_cs[m_curr_frame]->accept (sym_inf_accum); + + if (return_list) + { + if (verbose) + return sym_inf_accum.map_value (); + else + return Cell (string_vector (sym_inf_accum.names ())); + } + else if (! sym_inf_accum.is_empty ()) + { + + if (msg.empty ()) + octave_stdout << "Variables visible from the current scope:\n"; + else + octave_stdout << msg; + + if (verbose) + sym_inf_accum.display (octave_stdout, + m_evaluator.whos_line_format ()); + else + { + octave_stdout << "\n"; + string_vector names (sym_inf_accum.names ()); + names.list_in_columns (octave_stdout); + } + + octave_stdout << "\n"; + } + + return octave_value (); + } + + octave_value call_stack::do_global_who_two (const string_vector& patterns, + bool have_regexp, + bool return_list, bool verbose, + const std::string& msg) + { + symbol_info_list symbol_stats; + std::list symbol_names; + + octave_idx_type npatterns = patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = patterns[j]; + + std::list tmp; + + if (have_regexp) + { + octave::regexp pat (pattern); + + for (auto& nm_ov : m_global_values) + { + if (pat.is_match (nm_ov.first)) + tmp.push_back (nm_ov.first); + } + } + else + { + glob_match pat (pattern); + + for (auto& nm_ov : m_global_values) + { + if (pat.match (nm_ov.first)) + tmp.push_back (nm_ov.first); + } + } + + for (const auto& nm : tmp) + { + octave_value value = m_global_values[nm]; + + if (value.is_defined ()) + { + if (verbose) + { + bool is_formal = false; + bool is_global = true; + bool is_persistent = false; + + symbol_info + syminf (nm, value, is_formal, + is_global, is_persistent); + + symbol_stats.append (syminf); + } + else + symbol_names.push_back (nm); + } + } + } + + if (return_list) + { + if (verbose) + { + std::string caller_function_name; + octave_function *caller_function = caller (); + if (caller_function) + caller_function_name = caller_function->name (); + + return symbol_stats.map_value (caller_function_name, 1); + } + else + return Cell (string_vector (symbol_names)); + } + else if (! (symbol_stats.empty () && symbol_names.empty ())) + { + if (msg.empty ()) + octave_stdout << "Global variables:\n\n"; + else + octave_stdout << msg; + + if (verbose) + symbol_stats.display (octave_stdout, + m_evaluator.whos_line_format ()); + else + { + string_vector names (symbol_names); + + names.list_in_columns (octave_stdout); + } + + octave_stdout << "\n"; + } + + return octave_value (); + } + + void call_stack::clear_current_frame_values (void) + { + m_cs[m_curr_frame]->clear_values (); + } + + void call_stack::display (void) const + { + std::ostream& os = octave_stdout; + + size_t nframes = size (); + + for (size_t i = 0; i < nframes; i++) + { + m_cs[i]->display (false); + if (i < nframes - 1) + os << std::endl; + } + } + + void call_stack::set_auto_fcn_var (stack_frame::auto_var_type avt, + const octave_value& val) + { + m_cs[m_curr_frame]->set_auto_fcn_var (avt, val); + } + + octave_value call_stack::get_auto_fcn_var (stack_frame::auto_var_type avt) const + { + return m_cs[m_curr_frame]->get_auto_fcn_var (avt); + } } DEFMETHOD (max_stack_depth, interp, args, nargout, @@ -746,9 +1108,9 @@ @seealso{max_recursion_depth} @end deftypefn */) { - octave::call_stack& cs = interp.get_call_stack (); + octave::tree_evaluator& tw = interp.get_evaluator (); - return cs.max_stack_depth (args, nargout); + return tw.max_stack_depth (args, nargout); } /* @@ -763,193 +1125,6 @@ %!error (max_stack_depth (1, 2)) */ -static octave_value -do_who_two (octave::interpreter& interp, const string_vector& pats, - bool global_only, bool have_regexp, bool return_list, - bool verbose = false, std::string msg = "") -{ - octave::symbol_info_list symbol_stats; - std::list symbol_names; - - octave::tree_evaluator& tw = interp.get_evaluator (); - octave::symbol_table& symtab = interp.get_symbol_table (); - - octave::symbol_scope scope = symtab.current_scope (); - - octave::symbol_record::context_id context = scope.current_context (); - - octave_idx_type npats = pats.numel (); - - for (octave_idx_type j = 0; j < npats; j++) - { - std::string pat = pats[j]; - - std::list tmp - = (have_regexp - ? (global_only - ? symtab.regexp_global_variables (pat) - : symtab.regexp_variables (pat)) - : (global_only - ? symtab.glob_global_variables (pat) - : symtab.glob_variables (pat))); - - for (const auto& sr : tmp) - { - octave_value value = sr.varval (context); - - if (value.is_defined ()) - { - if (verbose) - { - octave::symbol_info - syminf (sr.name (), value, sr.is_automatic (), - value.iscomplex (), sr.is_formal (), - sr.is_global (), sr.is_persistent ()); - - symbol_stats.append (syminf); - } - else - symbol_names.push_back (sr.name ()); - } - } - } - - if (return_list) - { - if (verbose) - { - octave::call_stack& cs = interp.get_call_stack (); - - std::string caller_function_name; - octave_function *caller = cs.caller (); - if (caller) - caller_function_name = caller->name (); - - return symbol_stats.map_value (caller_function_name, 1); - } - else - return Cell (string_vector (symbol_names)); - } - else if (! (symbol_stats.empty () && symbol_names.empty ())) - { - if (msg.empty ()) - if (global_only) - octave_stdout << "Global variables:\n\n"; - else - octave_stdout << "Variables in the current scope:\n\n"; - else - octave_stdout << msg; - - if (verbose) - symbol_stats.display (octave_stdout, tw.whos_line_format ()); - else - { - string_vector names (symbol_names); - - names.list_in_columns (octave_stdout); - } - - octave_stdout << "\n"; - } - - return octave_value (); -} - -static octave_value -do_who (octave::interpreter& interp, int argc, const string_vector& argv, - bool return_list, bool verbose = false) -{ - octave_value retval; - - octave::symbol_table& symtab = interp.get_symbol_table (); - octave::call_stack& cs = interp.get_call_stack (); - - std::string my_name = argv[0]; - - std::string file_name; - - bool from_file = false; - bool global_only = false; - bool have_regexp = false; - - int i = 1; - while (i < argc) - { - if (argv[i] == "-file") - { - if (from_file) - error ("%s: -file option may only be specified once", - my_name.c_str ()); - - from_file = true; - - if (i == argc - 1) - error ("%s: -file argument must be followed by a filename", - my_name.c_str ()); - - file_name = argv[++i]; - } - else if (argv[i] == "-regexp") - { - have_regexp = true; - } - else if (argv[i] == "global") - global_only = true; - else if (argv[i][0] == '-') - warning ("%s: unrecognized option '%s'", my_name.c_str (), - argv[i].c_str ()); - else - break; - - i++; - } - - int npats = argc - i; - string_vector pats; - if (npats > 0) - { - pats.resize (npats); - for (int j = 0; j < npats; j++) - pats[j] = argv[i+j]; - } - else - { - pats.resize (1); - pats[0] = "*"; - } - - if (from_file) - { - // FIXME: This is an inefficient manner to implement this as the - // variables are loaded in to a temporary context and then treated. - // It would be better to refactor symbol_info_list to not store the - // symbol records and then use it in load-save.cc (do_load) to - // implement this option there so that the variables are never - // stored at all. - - octave::unwind_protect frame; - - // Set up temporary scope. - - octave::symbol_scope tmp_scope ("$dummy_scope$"); - - symtab.set_scope (tmp_scope); - - cs.push (tmp_scope, 0); - frame.add_method (cs, &octave::call_stack::pop); - - octave::feval ("load", octave_value (file_name), 0); - - std::string newmsg = "Variables in the file " + file_name + ":\n\n"; - - return do_who_two (interp, pats, global_only, have_regexp, - return_list, verbose, newmsg); - } - else - return do_who_two (interp, pats, global_only, have_regexp, - return_list, verbose); -} - DEFMETHOD (who, interp, args, nargout, doc: /* -*- texinfo -*- @deftypefn {} {} who @@ -989,13 +1164,16 @@ string_vector argv = args.make_argv ("who"); - return do_who (interp, argc, argv, nargout == 1); + octave::tree_evaluator& tw = interp.get_evaluator (); + + return tw.do_who (argc, argv, nargout == 1); } /* %!test %! avar = magic (4); %! ftmp = [tempname() ".mat"]; +%! save_default_options ("-binary", "local"); %! unwind_protect %! save (ftmp, "avar"); %! vars = whos ("-file", ftmp); @@ -1032,10 +1210,6 @@ @item blank Variable in local scope -@item @code{a} -Automatic variable. An automatic variable is one created by the -interpreter, for example @code{argn}. - @item @code{c} Variable of complex type. @@ -1078,5 +1252,7 @@ string_vector argv = args.make_argv ("whos"); - return do_who (interp, argc, argv, nargout == 1, true); + octave::tree_evaluator& tw = interp.get_evaluator (); + + return tw.do_who (argc, argv, nargout == 1, true); } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/call-stack.h --- a/libinterp/corefcn/call-stack.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/call-stack.h Fri Jul 12 12:14:43 2019 -0400 @@ -35,11 +35,12 @@ class octave_value; class octave_value_list; +#include "stack-frame.h" #include "symscope.h" namespace octave { - class interpreter; + class tree_evaluator; class symbol_info_list; class unwind_protect; @@ -49,76 +50,25 @@ { public: - class stack_frame - { - public: - - friend class call_stack; + typedef std::deque stack_frames; - stack_frame (octave_function *fcn = nullptr, - unwind_protect *up_frame = nullptr, - const symbol_scope& scope = symbol_scope (), - symbol_record::context_id context = 0, size_t prev = 0) - : m_fcn (fcn), m_unwind_protect_frame (up_frame), - m_line (-1), m_column (-1), m_scope (scope), - m_context (context), m_prev (prev) - { } - - stack_frame (const stack_frame& elt) - : m_fcn (elt.m_fcn), - m_unwind_protect_frame (elt.m_unwind_protect_frame), - m_line (elt.m_line), m_column (elt.m_column), - m_scope (elt.m_scope), m_context (elt.m_context), - m_prev (elt.m_prev) - { } - - int line (void) const { return m_line; } - - int column (void) const { return m_column; } - - std::string fcn_file_name (void) const; + typedef stack_frames::iterator iterator; + typedef stack_frames::const_iterator const_iterator; - std::string fcn_name (bool print_subfn = true) const; - - bool operator == (const stack_frame& rhs) const; - - symbol_info_list - make_symbol_info_list (const std::list& srl) const; - - symbol_info_list glob_symbol_info (const std::string& pat) const; - - symbol_info_list regexp_symbol_info (const std::string& pat) const; - - symbol_info_list get_symbol_info (void) const; - - private: + typedef stack_frames::reverse_iterator reverse_iterator; + typedef stack_frames::const_reverse_iterator const_reverse_iterator; - octave_function *m_fcn; - unwind_protect *m_unwind_protect_frame; - int m_line; - int m_column; - symbol_scope m_scope; - symbol_record::context_id m_context; - size_t m_prev; - }; - - typedef std::deque::iterator iterator; - typedef std::deque::const_iterator const_iterator; - - typedef std::deque::reverse_iterator reverse_iterator; - typedef std::deque::const_reverse_iterator const_reverse_iterator; - - call_stack (interpreter& interp); + call_stack (tree_evaluator& evaluator); // Current function (top of stack). octave_function * current (void) const { octave_function *retval = nullptr; - if (! cs.empty ()) + if (! m_cs.empty ()) { - const stack_frame& elt = cs[curr_frame]; - retval = elt.m_fcn; + const stack_frame *elt = m_cs[m_curr_frame]; + retval = elt->function (); } return retval; @@ -134,25 +84,39 @@ octave_function * caller (void) const { - return curr_frame > 1 ? cs[curr_frame-1].m_fcn : cs[0].m_fcn; + return (m_curr_frame > 1 + ? m_cs[m_curr_frame-1]->function () : m_cs[0]->function ()); } - size_t current_frame (void) const { return curr_frame; } + size_t current_frame (void) const { return m_curr_frame; } + + size_t size (void) const { return m_cs.size (); } + + const stack_frame& get_current_stack_frame (void) const + { + return *(m_cs[m_curr_frame]); + } - size_t size (void) const { return cs.size (); } + stack_frame& get_current_stack_frame (void) + { + return *(m_cs[m_curr_frame]); + } - size_t num_user_code_frames (octave_idx_type& curr_user_frame) const; + symbol_scope top_scope (void) const + { + return m_cs[0]->get_scope (); + } symbol_scope current_scope (void) const { - return (curr_frame > 0 && curr_frame < cs.size () - ? cs[curr_frame].m_scope : symbol_scope ()); + // FIXME: Can m_curr_frame ever be invalid? + return (m_curr_frame < m_cs.size () + ? m_cs[m_curr_frame]->get_scope () : symbol_scope ()); } - symbol_record::context_id current_context (void) const + bool at_top_level (void) const { - return (curr_frame > 0 && curr_frame < cs.size () - ? cs[curr_frame].m_context : 0); + return current_scope () == top_scope (); } // Function at location N on the call stack (N == 0 is current), may @@ -161,25 +125,25 @@ { octave_function *retval = nullptr; - if (cs.size () > n) + if (m_cs.size () > n) { - stack_frame& elt = cs[n]; - retval = elt.m_fcn; + stack_frame *elt = m_cs[n]; + retval = elt->function (); } return retval; } // User code caller. - octave_user_code * caller_user_code (size_t nskip = 0) const; + octave_user_code * current_user_code (void) const; - unwind_protect *curr_fcn_unwind_protect_frame (void) const; + unwind_protect * curr_fcn_unwind_protect_frame (void) const; // Line in user code caller. - int caller_user_code_line (void) const; + int current_user_code_line (void) const; // Column in user code caller. - int caller_user_code_column (void) const; + int current_user_code_column (void) const; // Current function that we are debugging. octave_user_code * debug_user_code (void) const; @@ -190,48 +154,56 @@ // Column number in current function that we are debugging. int debug_user_code_column (void) const; + std::string get_dispatch_class (void) const; + + void set_dispatch_class (const std::string& class_name); + + bool is_class_method_executing (std::string& dispatch_class) const; + + bool is_class_constructor_executing (std::string& dispatch_class) const; + // Return TRUE if all elements on the call stack are scripts. bool all_scripts (void) const; - void push (octave_function *fcn = nullptr, - unwind_protect *up_frame = nullptr); + stack_frame * get_static_link (size_t prev_frame) const; + + void push (const symbol_scope& scope); - void push (octave_function *fcn, unwind_protect *up_frame, - const symbol_scope& scope, symbol_record::context_id context); + void push (octave_user_function *fcn, unwind_protect *up_frame, + stack_frame *closure_frames = nullptr); - void push (const symbol_scope& scope, symbol_record::context_id context) - { - push (nullptr, nullptr, scope, context); - } + void push (octave_user_script *script, unwind_protect *up_frame); + + void push (octave_function *fcn); void set_location (int l, int c) { - if (! cs.empty ()) + if (! m_cs.empty ()) { - stack_frame& elt = cs.back (); + stack_frame *elt = m_cs.back (); - elt.m_line = l; - elt.m_column = c; + elt->line (l); + elt->column (c); } } void set_line (int l) { - if (! cs.empty ()) + if (! m_cs.empty ()) { - stack_frame& elt = cs.back (); + stack_frame *elt = m_cs.back (); - elt.m_line = l; + elt->line (l); } } void set_column (int c) { - if (! cs.empty ()) + if (! m_cs.empty ()) { - stack_frame& elt = cs.back (); + stack_frame *elt = m_cs.back (); - elt.m_column = c; + elt->column (c); } } @@ -242,62 +214,113 @@ goto_frame (n); } - bool goto_frame_relative (int n, bool verbose = false); + size_t find_current_user_frame (void) const; + stack_frame *current_user_frame (void) const; + + size_t dbupdown (size_t start, int n, bool verbose); + size_t dbupdown (int n = -1, bool verbose = false); void goto_caller_frame (void); void goto_base_frame (void); - std::list - backtrace_frames (size_t nskip, octave_idx_type& curr_user_frame) const; + std::list + backtrace_frames (octave_idx_type& curr_user_frame) const; - std::list - backtrace_frames (size_t nskip = 0) const - { - octave_idx_type curr_user_frame = -1; + std::list backtrace_frames (void) const; - return backtrace_frames (nskip, curr_user_frame); - } - - octave_map backtrace (size_t nskip, octave_idx_type& curr_user_frame, + octave_map backtrace (octave_idx_type& curr_user_frame, bool print_subfn = true) const; - octave_map backtrace (size_t nskip = 0); + octave_map backtrace (void) const; octave_map empty_backtrace (void) const; void pop (void); - void clear (void) { cs.clear (); } + void clear (void); + + symbol_info_list all_variables (void); + + std::list glob (const std::string& pattern) const; - symbol_info_list glob_symbol_info (const std::string& pat) const; + std::list regexp (const std::string& pattern) const; + + std::list variable_names (void) const; + + std::list global_variable_names (void) const; - symbol_info_list regexp_symbol_info (const std::string& pat) const; + void clear_global_variable (const std::string& name); + + void clear_global_variable_pattern (const std::string& pattern); + + void clear_global_variable_regexp(const std::string& pattern); - symbol_info_list get_symbol_info (void) const; + void clear_global_variables (void); + + symbol_info_list glob_symbol_info (const std::string& pattern) const; + + symbol_info_list regexp_symbol_info (const std::string& pattern) const; + + symbol_info_list get_symbol_info (void); symbol_info_list top_scope_symbol_info (void) const; octave_value max_stack_depth (const octave_value_list& args, int nargout); + void make_persistent (const symbol_record& sym); + + void make_global (const symbol_record& sym); + + octave_value global_varval (const std::string& name) const; + + octave_value& global_varref (const std::string& name); + + octave_value get_top_level_value (const std::string& name) const; + + void set_top_level_value (const std::string& name, + const octave_value& value); + + octave_value do_who (int argc, const string_vector& argv, + bool return_list, bool verbose = false); + + octave_value do_who_two (const string_vector& patterns, bool have_regexp, + bool return_list, bool verbose, + const std::string& msg = ""); + + octave_value do_global_who_two (const string_vector& patterns, + bool have_regexp, bool return_list, + bool verbose, const std::string& msg = ""); + + void clear_current_frame_values (void); + + void display (void) const; + + void set_auto_fcn_var (stack_frame::auto_var_type avt, + const octave_value& val); + + octave_value get_auto_fcn_var (stack_frame::auto_var_type avt) const; + private: + tree_evaluator& m_evaluator; + // The current call stack. - std::deque cs; + // FIXME: maybe we should be using a std::shared_ptr to manage the + // individual stack frames? + stack_frames m_cs; - size_t curr_frame; + // FIXME: Could we eliminate this varaible and manage the current + // frame in the evaluator class instead? The current frame might + // always be the top of the stack. Restoring the previous/current + // frame would be managed by other means, such as an + // unwind_protect frame. + size_t m_curr_frame; int m_max_stack_depth; - interpreter& m_interpreter; + std::map m_global_values; }; } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::call_stack' instead") -typedef octave::call_stack octave_call_stack; - #endif - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/cdisplay.c --- a/libinterp/corefcn/cdisplay.c Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/cdisplay.c Fri Jul 12 12:14:43 2019 -0400 @@ -55,6 +55,9 @@ *dpy_avail = 0; + double ht_mm = 0.0; + double wd_mm = 0.0; + #if defined (OCTAVE_USE_WINDOWS_API) octave_unused_parameter (dpy_name); @@ -68,11 +71,8 @@ *ht = GetDeviceCaps (hdc, VERTRES); *wd = GetDeviceCaps (hdc, HORZRES); - double ht_mm = GetDeviceCaps (hdc, VERTSIZE); - double wd_mm = GetDeviceCaps (hdc, HORZSIZE); - - *rx = *wd * 25.4 / wd_mm; - *ry = *ht * 25.4 / ht_mm; + ht_mm = GetDeviceCaps (hdc, VERTSIZE); + wd_mm = GetDeviceCaps (hdc, HORZSIZE); *dpy_avail = 1; } @@ -118,11 +118,8 @@ values, but the CGFloat typedef is not present on older systems, so use double instead. */ - double ht_mm = sz_mm.height; - double wd_mm = sz_mm.width; - - *rx = *wd * 25.4 / wd_mm; - *ry = *ht * 25.4 / ht_mm; + ht_mm = sz_mm.height; + wd_mm = sz_mm.width; *dpy_avail = 1; } @@ -149,11 +146,8 @@ int screen_number = XScreenNumberOfScreen (screen); - double ht_mm = DisplayHeightMM (display, screen_number); - double wd_mm = DisplayWidthMM (display, screen_number); - - *rx = *wd * 25.4 / wd_mm; - *ry = *ht * 25.4 / ht_mm; + ht_mm = DisplayHeightMM (display, screen_number); + wd_mm = DisplayWidthMM (display, screen_number); } else msg = "X11 display has no default screen"; @@ -178,5 +172,26 @@ #endif + if (*dpy_avail) + { + if (wd_mm == 0 || ht_mm == 0) + { + msg = "screen width or height reported to be zero"; + + // Sizes reported as zero have been found on some systems. + // For example, X/Wayland running inside virtualbox. + + // Guess a DPI. + + *rx = 96.0; + *ry = 96.0; + } + else + { + *rx = *wd * 25.4 / wd_mm; + *ry = *ht * 25.4 / ht_mm; + } + } + return msg; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/cellfun.cc --- a/libinterp/corefcn/cellfun.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/cellfun.cc Fri Jul 12 12:14:43 2019 -0400 @@ -39,6 +39,7 @@ #include "Cell.h" #include "oct-map.h" #include "defun.h" +#include "interpreter-private.h" #include "interpreter.h" #include "parse.h" #include "variables.h" @@ -65,7 +66,8 @@ #include "ov-fcn-handle.h" static octave_value_list -get_output_list (octave_idx_type count, octave_idx_type nargout, +get_output_list (octave::error_system& es, + octave_idx_type count, octave_idx_type nargout, const octave_value_list& inputlist, octave_value& func, octave_value& error_handler) @@ -95,8 +97,8 @@ if (error_handler.is_defined ()) { octave_scalar_map msg; - msg.assign ("identifier", last_error_id ()); - msg.assign ("message", last_error_message ()); + msg.assign ("identifier", es.last_error_id ()); + msg.assign ("message", es.last_error_message ()); msg.assign ("index", static_cast (count + static_cast (1))); @@ -104,7 +106,7 @@ octave_value_list errlist = inputlist; errlist.prepend (msg); - buffer_error_messages--; + es.buffer_error_messages (es.buffer_error_messages () - 1); tmp = octave::feval (error_handler, errlist, nargout); } @@ -426,17 +428,7 @@ std::string name = args(0).string_value (); if (! octave::valid_identifier (name)) - { - std::string fcn_name = unique_symbol_name ("__cellfun_fcn__"); - std::string fname = "function y = " + fcn_name + "(x) y = "; - - octave_function *ptr_func - = extract_function (args(0), "cellfun", fcn_name, - fname, "; endfunction"); - - if (ptr_func) - func = octave_value (ptr_func, true); - } + func = octave::get_function_handle (interp, args(0), "x"); else { func = symtab.find_function (name); @@ -461,6 +453,11 @@ { if (func.is_function_handle ()) { + // We can't check for overloads now. Is there something else we + // should be doing instead? + goto nevermind; + +#if 0 octave_fcn_handle *f = func.fcn_handle_value (); // Overloaded function handles need to check the type of the @@ -468,6 +465,7 @@ // optimized this way. if (f -> is_overloaded ()) goto nevermind; +#endif } std::string name = func.function_value () -> name (); @@ -544,11 +542,15 @@ } } + octave::error_system& es = interp.get_error_system (); + octave::unwind_protect frame; - frame.protect_var (buffer_error_messages); + + int bem = es.buffer_error_messages (); + frame.add_method (es, &octave::error_system::set_buffer_error_messages, bem); if (error_handler.is_defined ()) - buffer_error_messages++; + es.buffer_error_messages (bem + 1); // Apply functions. @@ -569,7 +571,7 @@ } const octave_value_list tmp - = get_output_list (count, nargout, inputlist, func, + = get_output_list (es, count, nargout, inputlist, func, error_handler); if (nargout > 0 && tmp.length () < nargout) @@ -650,7 +652,7 @@ } const octave_value_list tmp - = get_output_list (count, nargout, inputlist, func, + = get_output_list (es, count, nargout, inputlist, func, error_handler); if (nargout > 0 && tmp.length () < nargout) @@ -1141,17 +1143,7 @@ std::string name = args(0).string_value (); if (! octave::valid_identifier (name)) - { - std::string fcn_name = unique_symbol_name ("__arrayfun_fcn__"); - std::string fname = "function y = " + fcn_name + "(x) y = "; - - octave_function *ptr_func - = extract_function (args(0), "arrayfun", fcn_name, - fname, "; endfunction"); - - if (ptr_func) - func = octave_value (ptr_func, true); - } + func = octave::get_function_handle (interp, args(0), "x"); else { func = symtab.find_function (name); @@ -1176,6 +1168,11 @@ { if (func.is_function_handle ()) { + // We can't check for overloads now. Is there something + // else we should be doing instead? + goto nevermind; + +#if 0 octave_fcn_handle *f = func.fcn_handle_value (); // Overloaded function handles need to check the type of the @@ -1183,7 +1180,9 @@ // optimized this way. if (f -> is_overloaded ()) goto nevermind; +#endif } + octave_value f = symtab.find_function (func.function_value () -> name ()); @@ -1236,11 +1235,16 @@ } } + octave::error_system& es = interp.get_error_system (); + octave::unwind_protect frame; - frame.protect_var (buffer_error_messages); + + int bem = es.buffer_error_messages (); + frame.add_method (es, &octave::error_system::set_buffer_error_messages, + bem); if (error_handler.is_defined ()) - buffer_error_messages++; + es.buffer_error_messages (bem + 1); // Apply functions. @@ -1263,7 +1267,7 @@ } const octave_value_list tmp - = get_output_list (count, nargout, inputlist, func, + = get_output_list (es, count, nargout, inputlist, func, error_handler); if (nargout > 0 && tmp.length () < nargout) @@ -1355,7 +1359,7 @@ } const octave_value_list tmp - = get_output_list (count, nargout, inputlist, func, + = get_output_list (es, count, nargout, inputlist, func, error_handler); if (nargout > 0 && tmp.length () < nargout) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/daspk.cc --- a/libinterp/corefcn/daspk.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/daspk.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,6 +24,7 @@ # include "config.h" #endif +#include #include #include "DASPK.h" @@ -31,6 +32,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter-private.h" #include "ovl.h" #include "ov-fcn.h" #include "ov-cell.h" @@ -43,10 +45,10 @@ #include "DASPK-opts.cc" // Global pointer for user defined function required by daspk. -static octave_function *daspk_fcn; +static octave_value daspk_fcn; // Global pointer for optional user defined jacobian function. -static octave_function *daspk_jac; +static octave_value daspk_jac; // Have we warned about imaginary values returned from user function? static bool warned_fcn_imaginary = false; @@ -69,7 +71,7 @@ args(1) = xdot; args(0) = x; - if (daspk_fcn) + if (daspk_fcn.is_defined ()) { octave_value_list tmp; @@ -119,7 +121,7 @@ args(1) = xdot; args(0) = x; - if (daspk_jac) + if (daspk_jac.is_defined ()) { octave_value_list tmp; @@ -269,17 +271,19 @@ frame.protect_var (call_depth); call_depth++; - octave::symbol_table& symtab = interp.get_symbol_table (); - if (call_depth > 1) error ("daspk: invalid recursive call"); std::string fcn_name, fname, jac_name, jname; - daspk_fcn = nullptr; - daspk_jac = nullptr; + + daspk_fcn = octave_value (); + daspk_jac = octave_value (); octave_value f_arg = args(0); + std::list fcn_param_names ({"x", "xdot", "t"}); + std::list jac_param_names ({"x", "xdot", "t", "cj"}); + if (f_arg.iscell ()) { Cell c = f_arg.cell_value (); @@ -287,99 +291,61 @@ f_arg = c(0); else if (c.numel () == 2) { - if (c(0).is_function_handle () || c(0).is_inline_function ()) - daspk_fcn = c(0).function_value (); - else - { - fcn_name = unique_symbol_name ("__daspk_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - daspk_fcn = extract_function (c(0), "daspk", fcn_name, - fname, "; endfunction"); - } + daspk_fcn = octave::get_function_handle (interp, c(0), + fcn_param_names); - if (daspk_fcn) + if (daspk_fcn.is_defined ()) { - if (c(1).is_function_handle () || c(1).is_inline_function ()) - daspk_jac = c(1).function_value (); - else - { - jac_name = unique_symbol_name ("__daspk_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - daspk_jac = extract_function (c(1), "daspk", jac_name, - jname, "; endfunction"); + daspk_jac = octave::get_function_handle (interp, c(1), + jac_param_names); - if (! daspk_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - daspk_fcn = nullptr; - } - } + if (daspk_jac.is_undefined ()) + daspk_fcn = octave_value (); } } else error ("daspk: incorrect number of elements in cell array"); } - if (! daspk_fcn && ! f_arg.iscell ()) + if (daspk_fcn.is_undefined () && ! f_arg.iscell ()) { if (f_arg.is_function_handle () || f_arg.is_inline_function ()) - daspk_fcn = f_arg.function_value (); + daspk_fcn = f_arg; else { switch (f_arg.rows ()) { case 1: - do - { - fcn_name = unique_symbol_name ("__daspk_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - daspk_fcn = extract_function (f_arg, "daspk", fcn_name, - fname, "; endfunction"); - } - while (0); + daspk_fcn = octave::get_function_handle (interp, f_arg, + fcn_param_names); break; case 2: { string_vector tmp = f_arg.string_vector_value (); - fcn_name = unique_symbol_name ("__daspk_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - daspk_fcn = extract_function (tmp(0), "daspk", fcn_name, - fname, "; endfunction"); + daspk_fcn = octave::get_function_handle (interp, tmp(0), + fcn_param_names); - if (daspk_fcn) + if (daspk_fcn.is_defined ()) { - jac_name = unique_symbol_name ("__daspk_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - daspk_jac = extract_function (tmp(1), "daspk", jac_name, - jname, "; endfunction"); + daspk_jac = octave::get_function_handle (interp, tmp(1), + jac_param_names); - if (! daspk_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - daspk_fcn = nullptr; - } + if (daspk_jac.is_undefined ()) + daspk_fcn = octave_value (); } } + break; + + default: + error ("daspk: first arg should be a string or 2-element string array"); } } } - if (! daspk_fcn) - return retval; + if (daspk_fcn.is_undefined ()) + error ("daspk: FCN argument is not a valid function name or handle"); ColumnVector state = args(1).xvector_value ("daspk: initial state X_0 must be a vector"); @@ -402,7 +368,7 @@ double tzero = out_times (0); DAEFunc func (daspk_user_function); - if (daspk_jac) + if (daspk_jac.is_defined ()) func.set_jacobian_function (daspk_user_jacobian); DASPK dae (state, deriv, tzero, func); @@ -416,11 +382,6 @@ else output = dae.integrate (out_times, deriv_output); - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - if (jac_name.length ()) - symtab.clear_function (jac_name); - std::string msg = dae.error_message (); if (dae.integration_ok ()) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/dasrt.cc --- a/libinterp/corefcn/dasrt.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/dasrt.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,6 +24,7 @@ # include "config.h" #endif +#include #include #include "DASRT.h" @@ -32,6 +33,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter-private.h" #include "ovl.h" #include "ov-fcn.h" #include "ov-cell.h" @@ -44,9 +46,9 @@ #include "DASRT-opts.cc" // Global pointers for user defined function required by dasrt. -static octave_function *dasrt_f; -static octave_function *dasrt_j; -static octave_function *dasrt_cf; +static octave_value dasrt_fcn; +static octave_value dasrt_jac; +static octave_value dasrt_cf; // Have we warned about imaginary values returned from user function? static bool warned_fcn_imaginary = false; @@ -70,13 +72,13 @@ args(1) = xdot; args(0) = x; - if (dasrt_f) + if (dasrt_fcn.is_defined ()) { octave_value_list tmp; try { - tmp = octave::feval (dasrt_f, args, 1); + tmp = octave::feval (dasrt_fcn, args, 1); } catch (octave::execution_exception& e) { @@ -111,7 +113,7 @@ args(1) = t; args(0) = x; - if (dasrt_cf) + if (dasrt_cf.is_defined ()) { octave_value_list tmp; @@ -157,13 +159,13 @@ args(1) = xdot; args(0) = x; - if (dasrt_j) + if (dasrt_jac.is_defined ()) { octave_value_list tmp; try { - tmp = octave::feval (dasrt_j, args, 1); + tmp = octave::feval (dasrt_jac, args, 1); } catch (octave::execution_exception& e) { @@ -353,11 +355,10 @@ int argp = 0; std::string fcn_name, fname, jac_name, jname; - dasrt_f = nullptr; - dasrt_j = nullptr; - dasrt_cf = nullptr; - octave::symbol_table& symtab = interp.get_symbol_table (); + dasrt_fcn = octave_value (); + dasrt_jac = octave_value (); + dasrt_cf = octave_value (); // Check all the arguments. Are they the right animals? @@ -365,6 +366,9 @@ octave_value f_arg = args(0); + std::list fcn_param_names ({"x", "xdot", "t"}); + std::list jac_param_names ({"x", "xdot", "t", "cj"}); + if (f_arg.iscell ()) { Cell c = f_arg.cell_value (); @@ -372,83 +376,49 @@ f_arg = c(0); else if (c.numel () == 2) { - if (c(0).is_function_handle () || c(0).is_inline_function ()) - dasrt_f = c(0).function_value (); - else - { - fcn_name = unique_symbol_name ("__dasrt_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dasrt_f = extract_function (c(0), "dasrt", fcn_name, fname, - "; endfunction"); - } + dasrt_fcn = octave::get_function_handle (interp, c(0), + fcn_param_names); - if (dasrt_f) + if (dasrt_fcn.is_defined ()) { - if (c(1).is_function_handle () || c(1).is_inline_function ()) - dasrt_j = c(1).function_value (); - else - { - jac_name = unique_symbol_name ("__dasrt_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - dasrt_j = extract_function (c(1), "dasrt", jac_name, jname, - "; endfunction"); + dasrt_jac = octave::get_function_handle (interp, c(1), + jac_param_names); - if (! dasrt_j) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - dasrt_f = nullptr; - } - } + if (dasrt_jac.is_undefined ()) + dasrt_fcn = octave_value (); } } else error ("dasrt: incorrect number of elements in cell array"); } - if (! dasrt_f && ! f_arg.iscell ()) + if (dasrt_fcn.is_undefined () && ! f_arg.iscell ()) { if (f_arg.is_function_handle () || f_arg.is_inline_function ()) - dasrt_f = f_arg.function_value (); + dasrt_fcn = f_arg; else { switch (f_arg.rows ()) { case 1: - fcn_name = unique_symbol_name ("__dasrt_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dasrt_f = extract_function (f_arg, "dasrt", fcn_name, fname, - "; endfunction"); + dasrt_fcn = octave::get_function_handle (interp, f_arg, + fcn_param_names); break; case 2: { - string_vector tmp = args(0).string_vector_value (); + string_vector tmp = f_arg.string_vector_value (); - fcn_name = unique_symbol_name ("__dasrt_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dasrt_f = extract_function (tmp(0), "dasrt", fcn_name, - fname, "; endfunction"); + dasrt_fcn = octave::get_function_handle (interp, tmp(0), + fcn_param_names); - if (dasrt_f) + if (dasrt_fcn.is_defined ()) { - jac_name = unique_symbol_name ("__dasrt_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - dasrt_j = extract_function (tmp(1), "dasrt", jac_name, - jname, "; endfunction"); + dasrt_jac = octave::get_function_handle (interp, tmp(1), + jac_param_names); - if (! dasrt_j) - dasrt_f = nullptr; + if (dasrt_jac.is_undefined ()) + dasrt_fcn = octave_value (); } } break; @@ -459,8 +429,8 @@ } } - if (! dasrt_f) - return retval; + if (dasrt_fcn.is_undefined ()) + error ("dasrt: FCN argument is not a valid function name or handle"); DAERTFunc func (dasrt_user_f); @@ -475,19 +445,15 @@ } else { - if (args(1).is_function_handle () || args(1).is_inline_function ()) - dasrt_cf = args(1).function_value (); - else if (args(1).is_string ()) + if (args(1).is_function_handle () || args(1).is_inline_function () + || args(1).is_string ()) { - fcn_name = unique_symbol_name ("__dasrt_constraint_fcn__"); - fname = "function g_out = "; - fname.append (fcn_name); - fname.append (" (x, t) g_out = "); - dasrt_cf = extract_function (args(1), "dasrt", fcn_name, fname, - "; endfunction"); + std::list cf_param_names ({"x", "t"}); + + dasrt_cf = octave::get_function_handle (interp, args(1), cf_param_names); } - if (dasrt_cf) + if (dasrt_cf.is_defined ()) { argp++; @@ -518,7 +484,7 @@ crit_times_set = true; } - if (dasrt_j) + if (dasrt_jac.is_defined ()) func.set_jacobian_function (dasrt_user_j); DASRT_result output; @@ -532,11 +498,6 @@ else output = dae.integrate (out_times); - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - if (jac_name.length ()) - symtab.clear_function (jac_name); - std::string msg = dae.error_message (); if (dae.integration_ok ()) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/dassl.cc --- a/libinterp/corefcn/dassl.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/dassl.cc Fri Jul 12 12:14:43 2019 -0400 @@ -31,6 +31,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter-private.h" #include "ovl.h" #include "ov-fcn.h" #include "ov-cell.h" @@ -43,10 +44,10 @@ #include "DASSL-opts.cc" // Global pointer for user defined function required by dassl. -static octave_function *dassl_fcn; +static octave_value dassl_fcn; // Global pointer for optional user defined jacobian function. -static octave_function *dassl_jac; +static octave_value dassl_jac; // Have we warned about imaginary values returned from user function? static bool warned_fcn_imaginary = false; @@ -69,7 +70,7 @@ args(1) = xdot; args(0) = x; - if (dassl_fcn) + if (dassl_fcn.is_defined ()) { octave_value_list tmp; @@ -119,7 +120,7 @@ args(1) = xdot; args(0) = x; - if (dassl_jac) + if (dassl_jac.is_defined ()) { octave_value_list tmp; @@ -273,14 +274,16 @@ if (call_depth > 1) error ("dassl: invalid recursive call"); - octave::symbol_table& symtab = interp.get_symbol_table (); + std::string fcn_name, fname, jac_name, jname; - std::string fcn_name, fname, jac_name, jname; - dassl_fcn = nullptr; - dassl_jac = nullptr; + dassl_fcn = octave_value (); + dassl_jac = octave_value (); octave_value f_arg = args(0); + std::list fcn_param_names ({"x", "xdot", "t"}); + std::list jac_param_names ({"x", "xdot", "t", "cj"}); + if (f_arg.iscell ()) { Cell c = f_arg.cell_value (); @@ -288,100 +291,61 @@ f_arg = c(0); else if (c.numel () == 2) { - if (c(0).is_function_handle () || c(0).is_inline_function ()) - dassl_fcn = c(0).function_value (); - else - { - fcn_name = unique_symbol_name ("__dassl_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dassl_fcn = extract_function (c(0), "dassl", fcn_name, fname, - "; endfunction"); - } + dassl_fcn = octave::get_function_handle (interp, c(0), + fcn_param_names); - if (dassl_fcn) + if (dassl_fcn.is_defined ()) { - if (c(1).is_function_handle () || c(1).is_inline_function ()) - dassl_jac = c(1).function_value (); - else - { - jac_name = unique_symbol_name ("__dassl_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - dassl_jac = extract_function (c(1), "dassl", jac_name, - jname, "; endfunction"); + dassl_jac = octave::get_function_handle (interp, c(1), + jac_param_names); - if (! dassl_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - dassl_fcn = nullptr; - } - } + if (dassl_jac.is_undefined ()) + dassl_fcn = octave_value (); } } else error ("dassl: incorrect number of elements in cell array"); } - if (! dassl_fcn && ! f_arg.iscell ()) + if (dassl_fcn.is_undefined () && ! f_arg.iscell ()) { if (f_arg.is_function_handle () || f_arg.is_inline_function ()) - dassl_fcn = f_arg.function_value (); + dassl_fcn = f_arg; else { switch (f_arg.rows ()) { case 1: - do - { - fcn_name = unique_symbol_name ("__dassl_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dassl_fcn = extract_function (f_arg, "dassl", fcn_name, - fname, "; endfunction"); - } - while (0); + dassl_fcn = octave::get_function_handle (interp, f_arg, + fcn_param_names); break; case 2: { string_vector tmp = f_arg.string_vector_value (); - fcn_name = unique_symbol_name ("__dassl_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, xdot, t) y = "); - dassl_fcn = extract_function (tmp(0), "dassl", fcn_name, - fname, "; endfunction"); + dassl_fcn = octave::get_function_handle (interp, tmp(0), + fcn_param_names); - if (dassl_fcn) + if (dassl_fcn.is_defined ()) { - jac_name = unique_symbol_name ("__dassl_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, xdot, t, cj) jac = "); - dassl_jac = extract_function (tmp(1), "dassl", - jac_name, jname, - "; endfunction"); + dassl_jac = octave::get_function_handle (interp, tmp(1), + jac_param_names); - if (! dassl_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - dassl_fcn = nullptr; - } + if (dassl_jac.is_undefined ()) + dassl_fcn = octave_value (); } } + break; + + default: + error ("dassl: first arg should be a string or 2-element string array"); } } } - if (! dassl_fcn) - return retval; + if (dassl_fcn.is_undefined ()) + error ("dassl: FCN argument is not a valid function name or handle"); ColumnVector state = args(1).xvector_value ("dassl: initial state X_0 must be a vector"); @@ -404,7 +368,7 @@ double tzero = out_times (0); DAEFunc func (dassl_user_function); - if (dassl_jac) + if (dassl_jac.is_defined ()) func.set_jacobian_function (dassl_user_jacobian); DASSL dae (state, deriv, tzero, func); @@ -419,11 +383,6 @@ else output = dae.integrate (out_times, deriv_output); - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - if (jac_name.length ()) - symtab.clear_function (jac_name); - std::string msg = dae.error_message (); if (dae.integration_ok ()) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/debug.cc --- a/libinterp/corefcn/debug.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/debug.cc Fri Jul 12 12:14:43 2019 -0400 @@ -36,7 +36,6 @@ #include "dNDArray.h" #include "bp-table.h" -#include "call-stack.h" #include "defun.h" #include "error.h" #include "errwarn.h" @@ -90,10 +89,13 @@ @deftypefnx {} {} dbstop in @var{func} @deftypefnx {} {} dbstop in @var{func} at @var{line} @deftypefnx {} {} dbstop in @var{func} at @var{line} if "@var{condition}" +@deftypefnx {} {} dbstop in @var{class} at @var{method} @deftypefnx {} {} dbstop if @var{event} @deftypefnx {} {} dbstop if @var{event} @var{ID} @deftypefnx {} {} dbstop (@var{bp_struct}) @deftypefnx {} {@var{rline} =} dbstop @dots{} +dbstop in waveClass at waveClass.plotEta +dbstop waveClass at waveClass.plotEta Set breakpoints for the built-in debugger. @@ -173,6 +175,7 @@ { octave::bp_table::intmap retmap; std::string symbol_name = ""; // stays empty for "dbstop if error" etc + std::string class_name = ""; octave::bp_table::intmap lines; std::string condition = ""; octave_value retval; @@ -185,14 +188,15 @@ { // explicit function / line / condition bptab.parse_dbfunction_params ("dbstop", args, symbol_name, - lines, condition); + class_name, lines, condition); if (lines.size () == 0) lines[0] = 1; if (symbol_name != "") { - retmap = bptab.add_breakpoint (symbol_name, lines, condition); + retmap = bptab.add_breakpoint (symbol_name, class_name, + lines, condition); retval = intmap_to_ov (retmap); } } @@ -243,7 +247,7 @@ for (octave_idx_type i = 0; i < line.numel (); i++) { lines [0] = line(i).double_value (); - bptab.add_breakpoint (name(i).string_value (), lines, + bptab.add_breakpoint (name(i).string_value (), "", lines, (use_cond ? cond(i).string_value () : unconditional)); @@ -252,6 +256,9 @@ } } + // If we add a breakpoint, we also need to reset debug_mode. + tw.reset_debug_state (); + return retval; } @@ -300,6 +307,7 @@ @end deftypefn */) { std::string symbol_name = ""; // stays empty for "dbclear if error" etc + std::string class_name = ""; octave::bp_table::intmap lines; std::string dummy; // "if" condition -- only used for dbstop @@ -309,7 +317,7 @@ octave::bp_table& bptab = tw.get_bp_table (); - bptab.parse_dbfunction_params ("dbclear", args, symbol_name, lines, dummy); + bptab.parse_dbfunction_params ("dbclear", args, symbol_name, class_name, lines, dummy); if (nargin == 1 && symbol_name == "all") { @@ -322,6 +330,9 @@ bptab.remove_breakpoint (symbol_name, lines); } + // If we remove a breakpoint, we also need to reset debug_mode. + tw.reset_debug_state (); + return ovl (); } @@ -395,7 +406,7 @@ else { /* - if (Vdebugging) + if (tw.in_debug_repl ()) { octave_user_code *dbg_fcn = tw.get_user_code (); if (dbg_fcn) @@ -557,40 +568,7 @@ { octave::tree_evaluator& tw = interp.get_evaluator (); - octave_user_code *dbg_fcn = tw.get_user_code (); - - if (! dbg_fcn) - { - octave_stdout << "stopped at top level" << std::endl; - return ovl (); - } - - octave_stdout << "stopped in " << dbg_fcn->name () << " at "; - - octave::call_stack& cs = interp.get_call_stack (); - - int l = cs.debug_user_code_line (); - - if (l > 0) - { - octave_stdout << "line " << l; - - std::string file_name = dbg_fcn->fcn_file_name (); - - if (! file_name.empty ()) - { - octave_stdout << " [" << file_name << ']' << std::endl; - - std::string line = dbg_fcn->get_code_line (l); - - if (! line.empty ()) - octave_stdout << l << ": " << line << std::endl; - } - else - octave_stdout << std::endl; - } - else - octave_stdout << "" << std::endl; + tw.debug_where (octave_stdout); return ovl (); } @@ -824,9 +802,7 @@ name = dbg_fcn->name (); } - octave::call_stack& cs = interp.get_call_stack (); - - int l = cs.debug_user_code_line (); + int l = tw.debug_user_code_line (); if (l > 0) { @@ -899,14 +875,14 @@ nskip = n; } - octave::call_stack& cs = interp.get_call_stack (); + octave::tree_evaluator& tw = interp.get_evaluator (); if (nargout == 0) { - octave_map stk = cs.backtrace (nskip, curr_frame); - octave_idx_type nframes_to_display = stk.numel (); + octave_map stk = tw.backtrace (curr_frame); + octave_idx_type nframes = stk.numel (); - if (nframes_to_display > 0) + if (nframes > 0) { octave::preserve_stream_state stream_state (os); @@ -920,14 +896,14 @@ size_t max_name_len = 0; - for (octave_idx_type i = 0; i < nframes_to_display; i++) + for (octave_idx_type i = nskip; i < nframes; i++) { std::string name = names(i).string_value (); max_name_len = std::max (name.length (), max_name_len); } - for (octave_idx_type i = 0; i < nframes_to_display; i++) + for (octave_idx_type i = nskip; i < nframes; i++) { std::string name = names(i).string_value (); std::string file = files(i).string_value (); @@ -943,15 +919,20 @@ << std::endl; } - if (show_top_level) + if (tw.at_top_level () && show_top_level) os << " --> top level" << std::endl; } } else { - octave_map stk = cs.backtrace (nskip, curr_frame, false); + octave_map stk = tw.backtrace (curr_frame, false); - retval = ovl (stk, curr_frame < 0 ? 1 : curr_frame + 1); + // If current stack frame is not in the list curr_frame will be + // -1 and either nskip caused us to skip it or we are at the top + // level, which is not included in the list of frames. So in the + // interpreter, 0 will be our invalid frame index value. + + retval = ovl (stk, curr_frame + 1); } return retval; @@ -1039,10 +1020,9 @@ if (who == "dbup") n = -n; - octave::call_stack& cs = interp.get_call_stack (); + octave::tree_evaluator& tw = interp.get_evaluator (); - if (! cs.goto_frame_relative (n, true)) - error ("%s: invalid stack frame", who.c_str ()); + tw.dbupdown (n, true); } DEFMETHOD (dbup, interp, args, , @@ -1098,7 +1078,9 @@ @seealso{dbcont, dbquit} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbstep: can only be called in debug mode"); int nargin = args.length (); @@ -1106,45 +1088,37 @@ if (nargin > 1) print_usage (); - octave::tree_evaluator& tw = interp.get_evaluator (); + int n = 0; if (nargin == 1) { - std::string arg = args(0).xstring_value ("dbstep: input argument must be a string"); + std::string arg + = args(0).xstring_value ("dbstep: input argument must be a string"); if (arg == "in") - { - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (-1); - } + n = -1; else if (arg == "out") - { - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (-2); - } + n = -2; else { - int n = atoi (arg.c_str ()); + n = atoi (arg.c_str ()); if (n < 1) error ("dbstep: invalid argument"); - - Vdebugging = false; - Vtrack_line_num = true; - - tw.set_dbstep_flag (n); } } else + n = 1; + + if (n != 0) { - Vdebugging = false; Vtrack_line_num = true; - tw.set_dbstep_flag (1); + tw.set_dbstep_flag (n); + + // If we set the dbstep flag, we also need to reset debug_mode. + tw.reset_debug_state (); + } return ovl (); @@ -1159,18 +1133,17 @@ @seealso{dbstep, dbquit} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbcont: can only be called in debug mode"); if (args.length () != 0) print_usage (); - Vdebugging = false; Vtrack_line_num = true; - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.reset_debug_state (); + tw.exit_debug_repl (true); return ovl (); } @@ -1178,26 +1151,37 @@ DEFMETHOD (dbquit, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {} dbquit -Quit debugging mode immediately without further code execution and return to -the Octave prompt. +@deftypefnx {} {} dbquit all +Quit debugging mode immediately without further code execution. With no +arguments, exit the current debugging level. With argument @code{all}, +exit all debugging levels and return to the Octave prompt. @seealso{dbcont, dbstep} @end deftypefn */) { - if (! Vdebugging) + octave::tree_evaluator& tw = interp.get_evaluator (); + + if (! tw.in_debug_repl ()) error ("dbquit: can only be called in debug mode"); - if (args.length () != 0) + int nargin = args.length (); + + if (nargin > 1) print_usage (); - // FIXME: there are too many debug mode flags! - - Vdebugging = false; + if (nargin == 1) + { + std::string arg + = args(0).xstring_value ("dbquit: input argument must be a string"); - octave::tree_evaluator& tw = interp.get_evaluator (); + if (arg == "all") + tw.abort_debug_repl (true); + else + error ("dbquit: unrecognized argument '%s'", arg.c_str ()); + } + else + tw.exit_debug_repl (true); - tw.reset_debug_state (false); - - throw octave::interrupt_exception (); + tw.debug_mode (false); return ovl (); } @@ -1212,7 +1196,9 @@ if (args.length () != 0) print_usage (); - return ovl (Vdebugging); + octave::tree_evaluator& tw = octave::__get_evaluator__ ("Fisdebugmode"); + + return ovl (tw.in_debug_repl ()); } DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, , diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/defun-dld.h --- a/libinterp/corefcn/defun-dld.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/defun-dld.h Fri Jul 12 12:14:43 2019 -0400 @@ -36,13 +36,13 @@ //! For detailed information, see \ref Macros. //! //! @param name The **unqouted** name of the function that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the function name is prepended by an -//! `F`. +//! 'F'. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this function. If this value is //! omitted, the function cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this function is expected to //! produce from the caller. If this value is //! omitted, the function cannot access this number. @@ -70,17 +70,17 @@ //! For detailed information, see \ref Macros. //! //! @param name The **unqouted** name of the method that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the method name is prepended by an -//! `F`. -//! @param interp_name The name of the `octave::interpreter` reference that can +//! 'F'. +//! @param interp_name The name of the 'octave::interpreter' reference that can //! be used by this method. If this value is omitted, //! there is no access to the interpreter and one should //! use #DEFUN to define a function instead. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this method. If this value is //! omitted, the method cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this method is expected to //! produce from the caller. If this value is //! omitted, the method cannot access this number. diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/defun-int.h --- a/libinterp/corefcn/defun-int.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/defun-int.h Fri Jul 12 12:14:43 2019 -0400 @@ -80,41 +80,6 @@ }; } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::auto_shlib' instead") -typedef octave::auto_shlib octave_auto_shlib; - -OCTAVE_DEPRECATED (4.4, "use 'tree_evaluator::isargout' instead") -extern OCTINTERP_API bool -defun_isargout (int, int); - -OCTAVE_DEPRECATED (4.4, "use 'tree_evaluator::isargout' instead") -extern OCTINTERP_API void -defun_isargout (int, int, bool *); - -OCTAVE_DEPRECATED (4.4, "use 'octave::symbol_table::install_built_in_function' instead") -extern OCTINTERP_API void -install_builtin_function (octave_builtin::fcn f, const std::string& name, - const std::string& file, const std::string& doc, - bool can_hide_function = true); - -OCTAVE_DEPRECATED (4.4, "use 'octave::symbol_table::install_built_in_function' instead") -extern OCTINTERP_API void -install_builtin_function (octave_builtin::meth m, const std::string& name, - const std::string& file, const std::string& doc, - bool can_hide_function = true); - -OCTAVE_DEPRECATED (4.4, "use 'octave::symbol_table::alias_built_in_function' instead") -extern OCTINTERP_API void -alias_builtin (const std::string& alias, const std::string& name); - -OCTAVE_DEPRECATED (4.4, "use 'octave::symbol_table::install_built_in_dispatch' instead") -extern OCTINTERP_API void -install_builtin_dispatch (const std::string& name, const std::string& klass); - -#endif - #define FORWARD_DECLARE_FUNX(name) \ extern OCTAVE_EXPORT octave_value_list \ name (const octave_value_list&, int) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/defun.cc --- a/libinterp/corefcn/defun.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/defun.cc Fri Jul 12 12:14:43 2019 -0400 @@ -26,7 +26,6 @@ #include -#include "call-stack.h" #include "defun.h" #include "dynamic-ld.h" #include "error.h" @@ -40,7 +39,6 @@ #include "ovl.h" #include "oct-lvalue.h" #include "pager.h" -#include "pt-eval.h" #include "interpreter-private.h" #include "interpreter.h" #include "symtab.h" @@ -51,9 +49,9 @@ void print_usage (void) { - octave::call_stack& cs = octave::__get_call_stack__ ("print_usage"); + octave::tree_evaluator& tw = octave::__get_evaluator__ ("print_usage"); - const octave_function *cur = cs.current (); + const octave_function *cur = tw.current_function (); if (cur) print_usage (cur->name ()); @@ -83,32 +81,6 @@ // Install variables and functions in the symbol tables. void -install_builtin_function (octave_builtin::fcn f, const std::string& name, - const std::string& file, const std::string& doc, - bool /* can_hide_function -- not yet implemented */) -{ - octave_value fcn (new octave_builtin (f, name, file, doc)); - - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("install_builtin_function"); - - symtab.install_built_in_function (name, fcn); -} - -void -install_builtin_function (octave_builtin::meth m, const std::string& name, - const std::string& file, const std::string& doc, - bool /* can_hide_function -- not yet implemented */) -{ - octave_value fcn (new octave_builtin (m, name, file, doc)); - - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("install_builtin_function"); - - symtab.install_built_in_function (name, fcn); -} - -void install_dld_function (octave_dld_function::fcn f, const std::string& name, const octave::dynamic_library& shl, const std::string& doc, bool relative) @@ -161,31 +133,14 @@ symtab.install_built_in_function (name, fval); } -void -alias_builtin (const std::string& alias, const std::string& name) -{ - octave::symbol_table& symtab = octave::__get_symbol_table__ ("alias_builtin"); - - symtab.alias_built_in_function (alias, name); -} - -void -install_builtin_dispatch (const std::string& name, const std::string& klass) -{ - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("install_builtin_dispatch"); - - symtab.install_built_in_dispatch (name, klass); -} - octave::dynamic_library get_current_shlib (void) { octave::dynamic_library retval; - octave::call_stack& cs = octave::__get_call_stack__ ("get_current_shlib"); + octave::tree_evaluator& tw = octave::__get_evaluator__ ("get_current_shlib"); - octave_function *curr_fcn = cs.current (); + octave_function *curr_fcn = tw.current_function (); if (curr_fcn) { @@ -205,19 +160,3 @@ return retval; } - -bool -defun_isargout (int nargout, int iout) -{ - octave::tree_evaluator& tw = octave::__get_evaluator__ ("defun_isargout"); - - return tw.isargout (nargout, iout); -} - -void -defun_isargout (int nargout, int nout, bool *isargout) -{ - octave::tree_evaluator& tw = octave::__get_evaluator__ ("defun_isargout"); - - return tw.isargout (nargout, nout, isargout); -} diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/defun.h --- a/libinterp/corefcn/defun.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/defun.h Fri Jul 12 12:14:43 2019 -0400 @@ -36,13 +36,13 @@ //! For detailed information, see \ref Macros. //! //! @param name The **unqouted** name of the function that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the function name is prepended by an -//! `F`. +//! 'F'. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this function. If this value is //! omitted, the function cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this function is expected to //! produce from the caller. If this value is //! omitted, the function cannot access this number. @@ -67,11 +67,11 @@ //! @param name The **qouted** name of the function that should be callable //! by the interpreter. //! @param fname The internal **unqouted** name of the function. This internal -//! name is by convention prepended by an `F`. +//! name is by convention prepended by an 'F'. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this function. If this value is //! omitted, the function cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this function is expected to //! produce from the caller. If this value is //! omitted, the function cannot access this number. @@ -88,17 +88,17 @@ //! //! For detailed information, see \ref Macros. //! -//! The function gets installed to the `octave::symbol_table` in a way, such +//! The function gets installed to the 'octave::symbol_table' in a way, such //! that no variable is allowed to hide this function name. //! //! @param name The **unqouted** name of the function that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the function name is prepended by an -//! `F`. +//! 'F'. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this function. If this value is //! omitted, the function cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this function is expected to //! produce from the caller. If this value is //! omitted, the function cannot access this number. @@ -114,17 +114,17 @@ //! For detailed information, see \ref Macros. //! //! @param name The **unqouted** name of the method that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the method name is prepended by an -//! `F`. -//! @param interp_name The name of the `octave::interpreter` reference that can +//! 'F'. +//! @param interp_name The name of the 'octave::interpreter' reference that can //! be used by this method. If this value is omitted, //! there is no access to the interpreter and one should //! use #DEFUN to define a function instead. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this method. If this value is //! omitted, the method cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this method is expected to //! produce from the caller. If this value is //! omitted, the method cannot access this number. @@ -149,15 +149,15 @@ //! @param name The **qouted** name of the method that should be callable //! by the interpreter. //! @param fname The internal **unqouted** name of the method. This internal -//! name is by convention prepended by an `F`. -//! @param interp_name The name of the `octave::interpreter` reference that can +//! name is by convention prepended by an 'F'. +//! @param interp_name The name of the 'octave::interpreter' reference that can //! be used by this method. If this value is omitted, //! there is no access to the interpreter and one should //! use #DEFUNX to define a function instead. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this method. If this value is //! omitted, the method cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this method is expected to //! produce from the caller. If this value is //! omitted, the method cannot access this number. @@ -174,21 +174,21 @@ //! //! For detailed information, see \ref Macros. //! -//! The method gets installed to the `octave::symbol_table` in a way, such +//! The method gets installed to the 'octave::symbol_table' in a way, such //! that no variable is allowed to hide this method name. //! //! @param name The **unqouted** name of the method that should be installed -//! on the `octave::symbol_table` and can be called by the +//! on the 'octave::symbol_table' and can be called by the //! interpreter. Internally, the method name is prepended by an -//! `F`. -//! @param interp_name The name of the `octave::interpreter` reference that can +//! 'F'. +//! @param interp_name The name of the 'octave::interpreter' reference that can //! be used by this method. If this value is omitted, //! there is no access to the interpreter and one should //! use #DEFCONSTFUN to define a function instead. //! @param args_name The name of the octave_value_list variable used to pass //! the argument list to this method. If this value is //! omitted, the method cannot access the argument list. -//! @param nargout_name The name of the `int` variable used to pass the number +//! @param nargout_name The name of the 'int' variable used to pass the number //! of output arguments this method is expected to //! produce from the caller. If this value is //! omitted, the method cannot access this number. diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/dirfns.cc --- a/libinterp/corefcn/dirfns.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/dirfns.cc Fri Jul 12 12:14:43 2019 -0400 @@ -97,12 +97,13 @@ doc: /* -*- texinfo -*- @deftypefn {} {} cd @var{dir} @deftypefnx {} {} cd +@deftypefnx {} {@var{old_dir} =} cd @deftypefnx {} {@var{old_dir} =} cd (@var{dir}) @deftypefnx {} {} chdir @dots{} Change the current working directory to @var{dir}. -If @var{dir} is omitted, the current directory is changed to the user's home -directory (@qcode{"~"}). +If called with no input or output arguments, the current directory is +changed to the user's home directory (@qcode{"~"}). For example, @@ -140,7 +141,7 @@ if (! dirname.empty ()) octave_change_to_directory (dirname); } - else + else if (nargout == 0) { std::string home_dir = octave::sys::env::get_home_directory (); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/display.cc --- a/libinterp/corefcn/display.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/display.cc Fri Jul 12 12:14:43 2019 -0400 @@ -66,14 +66,9 @@ if (! instance) { instance = new display_info (query); - - if (instance) - singleton_cleanup_list::add (cleanup_instance); + singleton_cleanup_list::add (cleanup_instance); } - if (! instance) - error ("unable to create display_info object!"); - return retval; } } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/dlmread.cc --- a/libinterp/corefcn/dlmread.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/dlmread.cc Fri Jul 12 12:14:43 2019 -0400 @@ -148,7 +148,10 @@ } else if (range_spec.is_real_matrix () && range_spec.numel () == 4) { - ColumnVector range (range_spec.vector_value ()); + NDArray range (range_spec.array_value ()); + if (range.any_element_is_nan ()) + error ("dlmread: NaN is not a valid row or column specifier"); + // double --> unsigned int avoiding any overflow rlo = static_cast (std::min (range(0), idx_max_dbl)); clo = static_cast (std::min (range(1), idx_max_dbl)); @@ -181,10 +184,11 @@ The @var{range} parameter specifies exactly which data elements are read. The first form of the parameter is a 4-element vector containing the upper left and lower right corners @code{[@var{R0},@var{C0},@var{R1},@var{C1}]} -where the indices are zero-based. Alternatively, a spreadsheet style -form such as @qcode{"A2..Q15"} or @qcode{"T1:AA5"} can be used. The -lowest alphabetical index @qcode{'A'} refers to the first column. The -lowest row index is 1. +where the indices are zero-based. To specify the last column---the equivalent +of @code{end} when indexing---use the specifier @code{Inf}. Alternatively, a +spreadsheet style form such as @qcode{"A2..Q15"} or @qcode{"T1:AA5"} can be +used. The lowest alphabetical index @qcode{'A'} refers to the first column. +The lowest row index is 1. @var{file} should be a filename or a file id given by @code{fopen}. In the latter case, the file is read until end of file is reached. @@ -274,7 +278,7 @@ } if (r0 < 0 || c0 < 0) - error ("dlmread: left & top must be positive"); + error ("dlmread: left (R0) and top (C0) must be positive"); // Short-circuit and return if range is empty if (r1 < r0 || c1 < c0) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/dot.cc --- a/libinterp/corefcn/dot.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/dot.cc Fri Jul 12 12:14:43 2019 -0400 @@ -26,8 +26,10 @@ #include "lo-blas-proto.h" #include "mx-base.h" + +#include "builtin-defun-decls.h" +#include "defun.h" #include "error.h" -#include "defun.h" #include "parse.h" static void @@ -75,11 +77,12 @@ If the optional argument @var{dim} is given, calculate the dot products along this dimension. -This is equivalent to -@code{sum (conj (@var{X}) .* @var{Y}, @var{dim})}, -but avoids forming a temporary array and is faster. When @var{X} and -@var{Y} are column vectors, the result is equivalent to -@code{@var{X}' * @var{Y}}. +Implementation Note: This is equivalent to +@code{sum (conj (@var{X}) .* @var{Y}, @var{dim})}, but avoids forming a +temporary array and is faster. When @var{X} and @var{Y} are column vectors, +the result is equivalent to @code{@var{X}' * @var{Y}}. Although, @code{dot} +is defined for integer arrays, the output may differ from the expected result +due to the limited range of integer objects. @seealso{cross, divergence} @end deftypefn */) { @@ -177,11 +180,16 @@ else { // Non-optimized evaluation. + // FIXME: This may *not* do what the user expects. + // It might be more useful to issue a warning, or even an error, instead + // of calculating possibly garbage results. + // Think of the dot product of two int8 vectors where the multiplications + // exceed intmax. octave_value_list tmp; tmp(1) = dim + 1; tmp(0) = do_binary_op (octave_value::op_el_mul, argx, argy); - tmp = octave::feval ("sum", tmp, 1); + tmp = Fsum (tmp, 1); if (! tmp.empty ()) retval = tmp(0); } @@ -204,17 +212,23 @@ %! assert (dot (single (x), single (x)), single ([4, 20])); %!test -%! x = int8 ([1 2]); -%! y = int8 ([2 3]); +%! x = int8 ([1, 2]); +%! y = int8 ([2, 3]); %! assert (dot (x, y), 8); %!test -%! x = int8 ([1 2; 3 4]); -%! y = int8 ([5 6; 7 8]); +%! x = int8 ([1, 2; 3, 4]); +%! y = int8 ([5, 6; 7, 8]); %! assert (dot (x, y), [26 44]); %! assert (dot (x, y, 2), [17; 53]); %! assert (dot (x, y, 3), [5 12; 21 32]); +## This is, perhaps, surprising. Integer maximums and saturation mechanics +## prevent accurate value from being calculated. +%!test +%! x = int8 ([127]); +%! assert (dot (x, x), 127); + ## Test input validation %!error dot () %!error dot (1) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/environment.cc --- a/libinterp/corefcn/environment.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/environment.cc Fri Jul 12 12:14:43 2019 -0400 @@ -38,35 +38,35 @@ #include "interpreter.h" #include "variables.h" -static void append_to_shell_path (const std::string& exec_path) -{ - // FIXME: should there be a way to remove a previous setting from - // PATH? - - if (exec_path.empty ()) - return; - - // FIXME: should we really be modifying PATH in the environment? - - std::string shell_path = octave::sys::env::getenv ("PATH"); - - if (shell_path.empty ()) - octave::sys::env::putenv ("PATH", exec_path); - else - { - // If PATH doesn't already have exec_path, append it. - // FIXME: should we search for the elements individually, and - // only append those that are missing? - - std::string path_sep = octave::directory_path::path_sep_str (); - - if (shell_path.find (exec_path) == std::string::npos) - octave::sys::env::putenv ("PATH", shell_path + path_sep + exec_path); - } -} - namespace octave { + static void append_to_shell_path (const std::string& exec_path) + { + // FIXME: should there be a way to remove a previous setting from + // PATH? + + if (exec_path.empty ()) + return; + + // FIXME: should we really be modifying PATH in the environment? + + std::string shell_path = sys::env::getenv ("PATH"); + + if (shell_path.empty ()) + sys::env::putenv ("PATH", exec_path); + else + { + // If PATH doesn't already have exec_path, append it. + // FIXME: should we search for the elements individually, and + // only append those that are missing? + + std::string path_sep = directory_path::path_sep_str (); + + if (shell_path.find (exec_path) == std::string::npos) + sys::env::putenv ("PATH", shell_path + path_sep + exec_path); + } + } + octave_value environment::editor (const octave_value_list& args, int nargout) { diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/error.cc --- a/libinterp/corefcn/error.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/error.cc Fri Jul 12 12:14:43 2019 -0400 @@ -35,7 +35,6 @@ #include "bp-table.h" #include "builtin-defun-decls.h" -#include "call-stack.h" #include "defun.h" #include "error.h" #include "input.h" @@ -52,193 +51,17 @@ #include "utils.h" #include "variables.h" -// TRUE means that Octave will try to beep obnoxiously before printing -// error messages. -static bool Vbeep_on_error = false; - -// TRUE means that Octave will try to enter the debugger when an error -// is encountered. This will also inhibit printing of the normal -// traceback message (you will only see the top-level error message). -bool Vdebug_on_error = false; - -// TRUE means that Octave will try to enter the debugger when an error -// is encountered within the 'try' section of a 'try' / 'catch' block. -bool Vdebug_on_caught = false; - -// TRUE means that Octave will try to enter the debugger when a warning -// is encountered. -bool Vdebug_on_warning = false; - -// TRUE means that Octave will try to display a stack trace when a -// warning is encountered. -static bool Vbacktrace_on_warning = true; - -// TRUE means that Octave will print a verbose warning. Currently unused. -static bool Vverbose_warning; - -// TRUE means that Octave will print no warnings, but lastwarn will be updated -static bool Vquiet_warning = false; - -// A structure containing (most of) the current state of warnings. -static octave_map warning_options; - -// The text of the last error message. -static std::string Vlast_error_message; - -// The text of the last warning message. -static std::string Vlast_warning_message; - -// The last warning message id. -static std::string Vlast_warning_id; - -// The last error message id. -static std::string Vlast_error_id; - -// The last file in which an error occurred -static octave_map Vlast_error_stack; - -// Current error state. -// -// Valid values: -// -// 0: no error -// 1: an error has occurred -// -int error_state = 0; - -// Tell the error handler whether to print messages, or just store -// them for later. Used for handling errors in eval() and -// the 'unwind_protect' statement. -int buffer_error_messages = 0; - -// The number of layers of try / catch blocks we're in. Used to print -// "caught error" instead of error when "dbstop if caught error" is on. -int in_try_catch = 0; - -// TRUE means error messages are turned off. -bool discard_error_messages = false; - -// TRUE means warning messages are turned off. -bool discard_warning_messages = false; - -void -reset_error_handler (void) +static void verror (bool save_last_error, std::ostream& os, const char *name, + const char *id, const char *fmt, va_list args, + bool with_cfn = false) { - buffer_error_messages = 0; - in_try_catch = 0; - discard_error_messages = false; + octave::error_system& es = octave::__get_error_system__ ("verror"); + + es.verror (save_last_error, os, name, id, fmt, args, with_cfn); } static void -initialize_warning_options (const std::string& state) -{ - octave_scalar_map initw; - - initw.setfield ("identifier", "all"); - initw.setfield ("state", state); - - warning_options = initw; -} - -static octave_map -initialize_last_error_stack (void) -{ - octave::call_stack& cs - = octave::__get_call_stack__ ("initialize_last_error_stack"); - - return cs.empty_backtrace (); -} - -static void -verror (bool save_last_error, std::ostream& os, - const char *name, const char *id, const char *fmt, va_list args, - bool with_cfn = false) -{ - if (discard_error_messages && ! Vdebug_on_caught) - return; - - if (! buffer_error_messages || Vdebug_on_caught) - octave::flush_stdout (); - - // FIXME: we really want to capture the message before it has all the - // formatting goop attached to it. We probably also want just the - // message, not the traceback information. - - std::ostringstream output_buf; - - octave::vformat (output_buf, fmt, args); - - std::string base_msg = output_buf.str (); - - bool to_beep_or_not_to_beep_p = Vbeep_on_error; - - std::string msg_string; - - if (to_beep_or_not_to_beep_p) - msg_string = "\a"; - - if (name) - { - if (in_try_catch && ! strcmp (name, "error")) - msg_string += "caught error: "; - else - msg_string += std::string (name) + ": "; - } - - octave::call_stack& cs = octave::__get_call_stack__ ("verror"); - - // If with_fcn is specified, we'll attempt to prefix the message with the name - // of the current executing function. But we'll do so only if: - // 1. the name is not empty (anonymous function) - // 2. it is not already there (including the following colon) - if (with_cfn) - { - octave_function *curfcn = cs.current (); - if (curfcn) - { - std::string cfn = curfcn->name (); - if (! cfn.empty ()) - { - cfn += ':'; - if (cfn.length () > base_msg.length () - || base_msg.compare (0, cfn.length (), cfn) != 0) - { - msg_string += cfn + ' '; - } - } - } - } - - msg_string += base_msg + '\n'; - - if (save_last_error) - { - // This is the first error in a possible series. - - Vlast_error_id = id; - Vlast_error_message = base_msg; - - octave_user_code *fcn = cs.caller_user_code (); - - if (fcn) - { - octave_idx_type curr_frame = -1; - - Vlast_error_stack = cs.backtrace (0, curr_frame); - } - else - Vlast_error_stack = initialize_last_error_stack (); - } - - if (! buffer_error_messages || Vdebug_on_caught) - { - octave_diary << msg_string; - os << msg_string; - } -} - -static void -pr_where_2 (std::ostream& os, const char *fmt, va_list args) +vpr_where (std::ostream& os, const char *fmt, va_list args) { if (fmt) { @@ -262,15 +85,15 @@ } } else - panic ("pr_where_2: invalid format"); + panic ("vpr_where: invalid format"); } static void -pr_where_1 (std::ostream& os, const char *fmt, ...) +pr_where_internal (std::ostream& os, const char *fmt, ...) { va_list args; va_start (args, fmt); - pr_where_2 (os, fmt, args); + vpr_where (os, fmt, args); va_end (args); } @@ -289,7 +112,7 @@ size_t nframes = frames.size (); if (nframes > 0) - pr_where_1 (os, "%s: called from\n", who); + pr_where_internal (os, "%s: called from\n", who); for (const auto& frm : frames) { @@ -300,36 +123,36 @@ if (line > 0) { if (column > 0) - pr_where_1 (os, " %s at line %d column %d\n", + pr_where_internal (os, " %s at line %d column %d\n", fcn_name.c_str (), line, column); else - pr_where_1 (os, " %s at line %d\n", fcn_name.c_str (), line); + pr_where_internal (os, " %s at line %d\n", + fcn_name.c_str (), line); } else - pr_where_1 (os, " %s\n", fcn_name.c_str ()); + pr_where_internal (os, " %s\n", fcn_name.c_str ()); } } static void pr_where (std::ostream& os, const char *who) { - octave::call_stack& cs = octave::__get_call_stack__ ("pr_where"); - - std::list call_stack_frames - = cs.backtrace_frames (); + octave::tree_evaluator& tw = octave::__get_evaluator__ ("pr_where"); + + std::list call_stack_frames = tw.backtrace_frames (); // Print the error message only if it is different from the previous one; // Makes the output more concise and readable. call_stack_frames.unique (); std::list frames; - for (const auto& frm : call_stack_frames) + for (const auto *frm : call_stack_frames) { error_stack_frame frame; - frame.name = frm.fcn_name (); - frame.line = frm.line (); - frame.column = frm.column (); + frame.name = frm->fcn_name (); + frame.line = frm->line (); + frame.column = frm->column (); frames.push_back (frame); } @@ -337,6 +160,999 @@ pr_where (os, who, frames); } +static void +maybe_enter_debugger (octave::execution_exception& e, + bool show_stack_trace = false) +{ + octave::error_system& es + = octave::__get_error_system__ ("maybe_enter_debugger"); + + es.maybe_enter_debugger (e, show_stack_trace); +} + +OCTAVE_NORETURN +static void +vusage (octave::execution_exception& e, const char *id, + const char *fmt, va_list args) +{ + verror (true, std::cerr, "usage", id, fmt, args); + + maybe_enter_debugger (e); + + throw e; +} + +OCTAVE_NORETURN +static void +error_1 (octave::execution_exception& e, std::ostream& os, + const char *name, const char *id, const char *fmt, + va_list args, bool with_cfn = false) +{ + octave::error_system& es = octave::__get_error_system__ ("error_1"); + + es.error_1 (e, os, name, id, fmt, args, with_cfn); +} + +OCTAVE_NORETURN +static void +error_1 (std::ostream& os, const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn = false) +{ + octave::error_system& es = octave::__get_error_system__ ("error_1"); + + es.error_1 (os, name, id, fmt, args, with_cfn); +} + +static int +check_state (const std::string& state) +{ + // -1: not found + // 0: found, "off" + // 1: found, "on" + // 2: found, "error" + + if (state == "off") + return 0; + else if (state == "on") + return 1; + else if (state == "error") + return 2; + else + return -1; +} + +static void +vwarning (const char *id, const char *fmt, va_list args) +{ + octave::error_system& es = octave::__get_error_system__ ("warning"); + + es.vwarning (id, fmt, args); +} + +static std::list +make_stack_frame_list (const octave_map& stack) +{ + std::list frames; + + Cell name = stack.contents ("name"); + Cell line = stack.contents ("line"); + Cell column; + bool have_column = false; + if (stack.contains ("column")) + { + have_column = true; + column = stack.contents ("column"); + } + + octave_idx_type nel = name.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + error_stack_frame frame; + + frame.name = name(i).string_value (); + frame.line = line(i).int_value (); + frame.column = (have_column ? column(i).int_value () : -1); + + frames.push_back (frame); + } + + return frames; +} + +static void +defun_usage_message (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + error_1 (octave_stdout, nullptr, "", fmt, args); + va_end (args); +} + +typedef void (*error_fun)(const char *, const char *, ...); + +static std::string +handle_message (error_fun f, const char *id, const char *msg, + const octave_value_list& args, bool have_fmt) +{ + std::string retval; + + std::string tmpstr; + + if (args.length () > 0) + { + octave_value arg; + + if (have_fmt) + { + octave_value_list tmp = Fsprintf (args, 1); + arg = tmp(0); + } + else + arg = args(0); + + if (arg.is_defined ()) + { + if (arg.isempty ()) + return retval; + else if (arg.is_string ()) + { + tmpstr = arg.string_value (); // 2-stage assignment required + msg = tmpstr.c_str (); // in order to generate pointer + // to valid memory. + } + } + } + + // Ugh. + + size_t len = strlen (msg); + + if (len > 0) + { + if (msg[len - 1] == '\n') + { + if (len > 1) + { + std::string tmp_msg (msg, len - 1); + f (id, "%s\n", tmp_msg.c_str ()); + retval = tmp_msg; + } + } + else + { + f (id, "%s", msg); + retval = msg; + } + } + + return retval; +} + +// Determine whether the first argument to error or warning function +// should be handled as the message identifier or as the format string. + +static bool +maybe_extract_message_id (const std::string& caller, + const octave_value_list& args, + octave_value_list& nargs, + std::string& id) +{ + nargs = args; + id = ""; + + int nargin = args.length (); + + bool have_fmt = nargin > 1; + + if (nargin > 0) + { + std::string arg1 = args(0).string_value (); + + // For compatibility with Matlab, an identifier must contain ':', + // but not at the beginning or the end, and it must not contain '%' + // (even if it is not a valid conversion operator) or whitespace. + + if (arg1.find_first_of ("% \f\n\r\t\v") == std::string::npos + && arg1.find (':') != std::string::npos + && arg1[0] != ':' + && arg1.back () != ':') + { + if (nargin > 1) + { + id = arg1; + + nargs.resize (nargin-1); + + for (int i = 1; i < nargin; i++) + nargs(i-1) = args(i); + } + else + nargs(0) = "call to " + caller + + " with message identifier '" + arg1 + + "' requires message"; + } + } + + return have_fmt; +} + +namespace octave +{ + static octave_scalar_map + init_warning_options (const std::string& state) + { + octave_scalar_map initw; + + initw.setfield ("identifier", "all"); + initw.setfield ("state", state); + + return initw; + } + + static octave_map + init_error_stack (interpreter& interp) + { + tree_evaluator& tw = interp.get_evaluator (); + + return tw.empty_backtrace (); + } + + error_system::error_system (interpreter& interp) + : m_interpreter (interp), + m_debug_on_error (false), + m_debug_on_caught (false), + m_debug_on_warning (false), + m_buffer_error_messages (0), + m_in_try_catch (0), + m_discard_error_messages (false), + m_discard_warning_messages (false), + m_beep_on_error (false), + m_backtrace_on_warning (true), + m_verbose_warning (false), + m_quiet_warning (false), + m_warning_options (init_warning_options ("on")), + m_last_error_message (), + m_last_warning_message (), + m_last_warning_id (), + m_last_error_id (), + m_last_error_stack (init_error_stack (interp)) + { + initialize_default_warning_state (); + } + + octave_value + error_system::debug_on_error (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_debug_on_error, args, nargout, + "debug_on_error"); + } + + octave_value + error_system::debug_on_caught (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_debug_on_caught, args, nargout, + "debug_on_caught"); + } + + octave_value + error_system::debug_on_warning (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_debug_on_warning, args, nargout, + "debug_on_warning"); + } + + octave_value + error_system::buffer_error_messages (const octave_value_list& args, + int nargout) + { + return set_internal_variable (m_buffer_error_messages, args, nargout, + "buffer_error_messages"); + } + + octave_value + error_system::in_try_catch (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_in_try_catch, args, nargout, + "in_try_catch"); + } + + octave_value + error_system::discard_error_messages (const octave_value_list& args, + int nargout) + { + return set_internal_variable (m_discard_error_messages, args, nargout, + "discard_error_messages"); + } + + octave_value + error_system::discard_warning_messages (const octave_value_list& args, + int nargout) + { + return set_internal_variable (m_discard_warning_messages, args, nargout, + "discard_warning_messages"); + } + + octave_value + error_system::beep_on_error (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_beep_on_error, args, nargout, + "beep_on_error"); + } + + octave_value + error_system::backtrace_on_warning (const octave_value_list& args, + int nargout) + { + return set_internal_variable (m_backtrace_on_warning, args, nargout, + "backtrace_on_warning"); + } + + octave_value + error_system::verbose_warning (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_verbose_warning, args, nargout, + "verbose_warning"); + } + + octave_value + error_system::quiet_warning (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_quiet_warning, args, nargout, + "quiet_warning"); + } + + octave_value + error_system::last_error_message (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_last_error_message, args, nargout, + "last_error_message"); + } + + octave_value + error_system::last_warning_message (const octave_value_list& args, + int nargout) + { + return set_internal_variable (m_last_warning_message, args, nargout, + "last_warning_message"); + } + + octave_value + error_system::last_warning_id (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_last_warning_id, args, nargout, + "last_warning_id"); + } + + octave_value + error_system::last_error_id (const octave_value_list& args, int nargout) + { + return set_internal_variable (m_last_error_id, args, nargout, + "last_error_id"); + } + + // For given warning ID, return 0 if warnings are disabled, 1 if + // enabled, and 2 if the given ID should be an error instead of a + // warning. + + int error_system::warning_enabled (const std::string& id) + { + int retval = 0; + + int all_state = -1; + int id_state = -1; + + octave_map opts = warning_options (); + + octave_idx_type nel = opts.numel (); + + if (nel > 0) + { + Cell identifier = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + bool all_found = false; + bool id_found = false; + + for (octave_idx_type i = 0; i < nel; i++) + { + octave_value ov = identifier(i); + std::string ovs = ov.string_value (); + + if (! all_found && ovs == "all") + { + all_state = check_state (state(i).string_value ()); + + if (all_state >= 0) + all_found = true; + } + + if (! id_found && ovs == id) + { + id_state = check_state (state(i).string_value ()); + + if (id_state >= 0) + id_found = true; + } + + if (all_found && id_found) + break; + } + } + + // If "all" is not present, assume warnings are enabled. + if (all_state == -1) + all_state = 1; + + if (all_state == 0) + { + if (id_state >= 0) + retval = id_state; + } + else if (all_state == 1) + { + if (id_state == 0 || id_state == 2) + retval = id_state; + else + retval = all_state; + } + else if (all_state == 2) + { + if (id_state == 0) + retval= id_state; + else + retval = all_state; + } + + return retval; + } + + void error_system::verror (bool save_last_error, std::ostream& os, + const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn) + { + if (discard_error_messages () && ! debug_on_caught ()) + return; + + if (! buffer_error_messages () || debug_on_caught ()) + flush_stdout (); + + // FIXME: we really want to capture the message before it has all the + // formatting goop attached to it. We probably also want just the + // message, not the traceback information. + + std::ostringstream output_buf; + + vformat (output_buf, fmt, args); + + std::string base_msg = output_buf.str (); + + bool to_beep_or_not_to_beep_p = beep_on_error (); + + std::string msg_string; + + if (to_beep_or_not_to_beep_p) + msg_string = "\a"; + + if (name) + { + if (in_try_catch () && ! strcmp (name, "error")) + msg_string += "caught error: "; + else + msg_string += std::string (name) + ": "; + } + + tree_evaluator& tw = m_interpreter.get_evaluator (); + + // If with_fcn is specified, we'll attempt to prefix the message with the name + // of the current executing function. But we'll do so only if: + // 1. the name is not empty (anonymous function) + // 2. it is not already there (including the following colon) + if (with_cfn) + { + std::string cfn = tw.current_function_name (); + + if (! cfn.empty ()) + { + cfn += ':'; + if (cfn.length () > base_msg.length () + || base_msg.compare (0, cfn.length (), cfn) != 0) + msg_string += cfn + ' '; + } + } + + msg_string += base_msg + '\n'; + + if (save_last_error) + { + // This is the first error in a possible series. + + last_error_id (id); + last_error_message (base_msg); + last_error_stack (tw.in_user_code () + ? tw.backtrace () : tw.empty_backtrace ()); + } + + if (! buffer_error_messages () || debug_on_caught ()) + { + octave_diary << msg_string; + os << msg_string; + } + } + + void error_system::maybe_enter_debugger (execution_exception& e, + bool show_stack_trace) + { + tree_evaluator& tw = m_interpreter.get_evaluator (); + bp_table& bptab = tw.get_bp_table (); + + if ((application::interactive () + || application::forced_interactive ()) + && ((debug_on_error () + && bptab.debug_on_err (last_error_id ())) + || (debug_on_caught () + && bptab.debug_on_caught (last_error_id ()))) + && tw.in_user_code ()) + { + unwind_protect frame; + + frame.protect_var (m_debug_on_error); + m_debug_on_error = false; + + if (show_stack_trace) + { + std::string stack_trace = e.info (); + + if (! stack_trace.empty ()) + { + std::cerr << stack_trace; + + e.set_stack_trace (); + } + } + + tw.enter_debugger (); + } + } + + void error_system::vwarning (const char *name, const char *id, + const char *fmt, va_list args) + { + if (discard_warning_messages ()) + return; + + flush_stdout (); + + std::ostringstream output_buf; + + vformat (output_buf, fmt, args); + + // FIXME: we really want to capture the message before it has all the + // formatting goop attached to it. We probably also want just the + // message, not the traceback information. + + std::string base_msg = output_buf.str (); + std::string msg_string; + + if (name) + msg_string = std::string (name) + ": "; + + msg_string += base_msg + '\n'; + + last_warning_id (id); + last_warning_message (base_msg); + + if (! quiet_warning ()) + { + octave_diary << msg_string; + + std::cerr << msg_string; + } + } + + void error_system::error_1 (execution_exception& e, std::ostream& os, + const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn) + { + bool show_stack_trace = false; + + if (fmt) + { + if (*fmt) + { + size_t len = strlen (fmt); + + if (len > 0) + { + if (fmt[len - 1] == '\n') + { + if (len > 1) + { + std::string tmp_fmt (fmt, len - 1); + verror (true, os, name, id, tmp_fmt.c_str (), + args, with_cfn); + } + + // If format ends with newline, suppress stack trace. + e.set_stack_trace (); + } + else + { + verror (true, os, name, id, fmt, args, with_cfn); + + tree_evaluator& tw = m_interpreter.get_evaluator (); + + if (tw.in_user_code () && ! discard_error_messages ()) + show_stack_trace = true; + } + } + } + } + else + panic ("error_1: invalid format"); + + maybe_enter_debugger (e, show_stack_trace); + + throw e; + } + + void error_system::error_1 (std::ostream& os, const char *name, + const char *id, const char *fmt, + va_list args, bool with_cfn = false) + { + execution_exception e = make_execution_exception ("error"); + + error_1 (e, os, name, id, fmt, args, with_cfn); + } + + void error_system::vwarning (const char *id, const char *fmt, va_list args) + { + int warn_opt = warning_enabled (id); + + if (warn_opt == 2) + { + // Handle this warning as an error. + + error_1 (std::cerr, "error", id, fmt, args); + } + else if (warn_opt == 1) + { + bool fmt_suppresses_backtrace = false; + size_t fmt_len = (fmt ? strlen (fmt) : 0); + fmt_suppresses_backtrace = (fmt_len > 0 && fmt[fmt_len-1] == '\n'); + + if (fmt_suppresses_backtrace && fmt_len > 1) + { + // Strip newline before issuing warning + std::string tmp_fmt (fmt, fmt_len - 1); + vwarning ("warning", id, tmp_fmt.c_str (), args); + } + else + vwarning ("warning", id, fmt, args); + + tree_evaluator& tw = m_interpreter.get_evaluator (); + + bool in_user_code = tw.in_user_code (); + + if (! fmt_suppresses_backtrace && in_user_code + && backtrace_on_warning () + && ! discard_warning_messages ()) + pr_where (std::cerr, "warning"); + + bp_table& bptab = tw.get_bp_table (); + + if ((application::interactive () + || application::forced_interactive ()) + && debug_on_warning () && in_user_code && bptab.debug_on_warn (id)) + { + unwind_protect frame; + + frame.protect_var (m_debug_on_warning); + m_debug_on_warning = false; + + tw.enter_debugger (); + } + } + } + + void error_system::rethrow_error (const char *id, const char *fmt, ...) + { + va_list args; + va_start (args, fmt); + verror (false, std::cerr, nullptr, id, fmt, args); + va_end (args); + } + + void error_system::rethrow_error (const std::string& id, + const std::string& msg, + const octave_map& stack) + { + execution_exception e = make_execution_exception ("error"); + + if (! stack.isempty () + && ! (stack.contains ("file") && stack.contains ("name") + && stack.contains ("line"))) + error ("rethrow: STACK struct must contain the fields 'file', 'name', and 'line'"); + + last_error_id (id); + last_error_message (msg); + last_error_stack (stack); + + size_t len = msg.length (); + + std::string tmp_msg (msg); + if (len > 1 && tmp_msg[len-1] == '\n') + { + tmp_msg.erase (len - 1); + + rethrow_error (id.c_str (), "%s\n", tmp_msg.c_str ()); + } + else + rethrow_error (id.c_str (), "%s", tmp_msg.c_str ()); + + if (! stack.isempty ()) + { + std::ostringstream buf; + + pr_where (buf, "error", make_stack_frame_list (stack)); + + e.set_stack_trace (buf.str ()); + } + + throw e; + } + + void error_system::vpanic (const char *fmt, va_list args) + { + buffer_error_messages (0); + discard_error_messages (false); + + verror (false, std::cerr, "panic", "", fmt, args); + + abort (); + } + + void error_system::panic (const char *fmt, ...) + { + va_list args; + va_start (args, fmt); + vpanic (fmt, args); + va_end (args); + } + + octave_scalar_map error_system::warning_query (const std::string& id_arg) + { + octave_scalar_map retval; + + std::string id = id_arg; + + if (id == "last") + id = last_warning_id (); + + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + assert (nel != 0); + + bool found = false; + + std::string val; + + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == id) + { + val = state(i).string_value (); + found = true; + break; + } + } + + if (! found) + { + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == "all") + { + val = state(i).string_value (); + found = true; + break; + } + } + } + + // The warning state "all" is always supposed to remain in the list, + // so we should always find a state, either explicitly or by using the + // state for "all". + + assert (found); + + retval.assign ("identifier", id); + retval.assign ("state", val); + + return retval; + } + + std::string error_system::default_warning_state (void) + { + std::string retval = "on"; + + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == "all") + { + retval = state(i).string_value (); + break; + } + } + + return retval; + } + + void error_system::display_warning_options (std::ostream& os) + { + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + std::string all_state = default_warning_state (); + + if (all_state == "on") + os << "By default, warnings are enabled."; + else if (all_state == "off") + os << "By default, warnings are disabled."; + else if (all_state == "error") + os << "By default, warnings are treated as errors."; + else + panic_impossible (); + + if (nel > 1) + os << "\n\n"; + + // The state for all is always supposed to be first in the list. + + for (octave_idx_type i = 1; i < nel; i++) + { + std::string tid = ident(i).string_value (); + std::string tst = state(i).string_value (); + + os << std::setw (7) << tst << " " << tid << "\n"; + } + + os << std::endl; + } + + void error_system::set_warning_option (const std::string& state, + const std::string& ident) + { + std::string all_state = default_warning_state (); + + if (state != "on" && state != "off" && state != "error") + error ("invalid warning state: %s", state.c_str ()); + + octave_map opts = warning_options (); + + Cell tid = opts.contents ("identifier"); + Cell tst = opts.contents ("state"); + + octave_idx_type nel = tid.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + if (tid(i).string_value () == ident) + { + // We found it in the current list of options. If the state + // for "all" is same as arg1, we can simply remove the item + // from the list. + + if (state == all_state && ident != "all") + { + for (i = i + 1; i < nel; i++) + { + tid(i-1) = tid(i); + tst(i-1) = tst(i); + } + + tid.resize (dim_vector (1, nel-1)); + tst.resize (dim_vector (1, nel-1)); + } + else + tst(i) = state; + + opts.clear (); + + opts.assign ("identifier", tid); + opts.assign ("state", tst); + + warning_options (opts); + + return; + } + } + + // The option wasn't already in the list. Append it. + + tid.resize (dim_vector (1, nel+1)); + tst.resize (dim_vector (1, nel+1)); + + tid(nel) = ident; + tst(nel) = state; + + opts.clear (); + + opts.assign ("identifier", tid); + opts.assign ("state", tst); + + warning_options (opts); + } + + void error_system::disable_warning (const std::string& id) + { + set_warning_option ("off", id); + } + + void error_system::initialize_default_warning_state (void) + { + warning_options (octave::init_warning_options ("on")); + + // Most people will want to have the following disabled. + + disable_warning ("Octave:array-as-logical"); + disable_warning ("Octave:array-to-scalar"); + disable_warning ("Octave:array-to-vector"); + disable_warning ("Octave:imag-to-real"); + disable_warning ("Octave:language-extension"); + disable_warning ("Octave:missing-semicolon"); + disable_warning ("Octave:neg-dim-as-zero"); + disable_warning ("Octave:resize-on-range-error"); + disable_warning ("Octave:separator-insert"); + disable_warning ("Octave:single-quote-string"); + disable_warning ("Octave:str-to-num"); + disable_warning ("Octave:mixed-string-concat"); + disable_warning ("Octave:variable-switch-label"); + } + + void error_system::interpreter_try (octave::unwind_protect& frame, + try_option opt) + { + switch (opt) + { + case buffer: + frame.protect_var (m_buffer_error_messages); + m_buffer_error_messages++; + break; + + case discard: + frame.protect_var (m_discard_error_messages); + m_discard_error_messages = true; + break; + } + + frame.protect_var (m_debug_on_error); + m_debug_on_error = false; + + frame.protect_var (m_debug_on_warning); + m_debug_on_warning = false; + + // Leave debug_on_caught as it was, so errors in try/catch are still + // caught. + } +} + octave::execution_exception make_execution_exception (const char *who) { @@ -351,85 +1167,6 @@ return retval; } -static void -maybe_enter_debugger (octave::execution_exception& e, - bool show_stack_trace = false) -{ - octave::call_stack& cs = octave::__get_call_stack__ ("maybe_enter_debugger"); - octave::bp_table& bptab = octave::__get_bp_table__ ("maybe_enter_debugger"); - - if ((octave::application::interactive () - || octave::application::forced_interactive ()) - && ((Vdebug_on_error && bptab.debug_on_err (last_error_id ())) - || (Vdebug_on_caught && bptab.debug_on_caught (last_error_id ()))) - && cs.caller_user_code ()) - { - octave::unwind_protect frame; - frame.protect_var (Vdebug_on_error); - Vdebug_on_error = false; - - octave::tree_evaluator& tw - = octave::__get_evaluator__ ("maybe_enter_debugger"); - - tw.debug_mode (true); - tw.current_frame (cs.current_frame ()); - - if (show_stack_trace) - { - std::string stack_trace = e.info (); - - if (! stack_trace.empty ()) - { - std::cerr << stack_trace; - - e.set_stack_trace (); - } - } - - octave::input_system& input_sys - = octave::__get_input_system__ ("maybe_enter_debugger"); - - input_sys.keyboard (); - } -} - -// Warning messages are never buffered. - -static void -vwarning (const char *name, const char *id, const char *fmt, va_list args) -{ - if (discard_warning_messages) - return; - - octave::flush_stdout (); - - std::ostringstream output_buf; - - octave::vformat (output_buf, fmt, args); - - // FIXME: we really want to capture the message before it has all the - // formatting goop attached to it. We probably also want just the - // message, not the traceback information. - - std::string base_msg = output_buf.str (); - std::string msg_string; - - if (name) - msg_string = std::string (name) + ": "; - - msg_string += base_msg + '\n'; - - Vlast_warning_id = id; - Vlast_warning_message = base_msg; - - if (! Vquiet_warning) - { - octave_diary << msg_string; - - std::cerr << msg_string; - } -} - void vmessage (const char *name, const char *fmt, va_list args) { @@ -461,31 +1198,12 @@ va_end (args); } -OCTAVE_NORETURN static -void -usage_1 (octave::execution_exception& e, const char *id, - const char *fmt, va_list args) -{ - verror (true, std::cerr, "usage", id, fmt, args); - - maybe_enter_debugger (e); - - throw e; -} - -OCTAVE_NORETURN static -void -usage_1 (const char *id, const char *fmt, va_list args) -{ - octave::execution_exception e = make_execution_exception ("usage"); - - usage_1 (e, id, fmt, args); -} - void vusage_with_id (const char *id, const char *fmt, va_list args) { - usage_1 (id, fmt, args); + octave::execution_exception e = make_execution_exception ("usage"); + + vusage (e, id, fmt, args); } void @@ -497,67 +1215,6 @@ va_end (args); } -OCTAVE_NORETURN static -void -error_1 (octave::execution_exception& e, std::ostream& os, - const char *name, const char *id, const char *fmt, - va_list args, bool with_cfn = false) -{ - bool show_stack_trace = false; - - if (fmt) - { - if (*fmt) - { - size_t len = strlen (fmt); - - if (len > 0) - { - if (fmt[len - 1] == '\n') - { - if (len > 1) - { - std::string tmp_fmt (fmt, len - 1); - verror (true, os, name, id, tmp_fmt.c_str (), - args, with_cfn); - } - - // If format ends with newline, suppress stack trace. - e.set_stack_trace (); - } - else - { - verror (true, os, name, id, fmt, args, with_cfn); - - octave::call_stack& cs - = octave::__get_call_stack__ ("error_1"); - - bool in_user_code = cs.caller_user_code () != nullptr; - - if (in_user_code && ! discard_error_messages) - show_stack_trace = true; - } - } - } - } - else - panic ("error_1: invalid format"); - - maybe_enter_debugger (e, show_stack_trace); - - throw e; -} - -OCTAVE_NORETURN static -void -error_1 (std::ostream& os, const char *name, const char *id, - const char *fmt, va_list args, bool with_cfn = false) -{ - octave::execution_exception e = make_execution_exception ("error"); - - error_1 (e, os, name, id, fmt, args, with_cfn); -} - void verror (const char *fmt, va_list args) { @@ -633,163 +1290,17 @@ va_end (args); } -static int -check_state (const std::string& state) -{ - // -1: not found - // 0: found, "off" - // 1: found, "on" - // 2: found, "error" - - if (state == "off") - return 0; - else if (state == "on") - return 1; - else if (state == "error") - return 2; - else - return -1; -} - -// For given warning ID, return 0 if warnings are disabled, 1 if -// enabled, and 2 if the given ID should be an error instead of a -// warning. - -int -warning_enabled (const std::string& id) +int warning_enabled (const std::string& id) { - int retval = 0; - - int all_state = -1; - int id_state = -1; - - octave_idx_type nel = warning_options.numel (); - - if (nel > 0) - { - Cell identifier = warning_options.contents ("identifier"); - Cell state = warning_options.contents ("state"); - - bool all_found = false; - bool id_found = false; - - for (octave_idx_type i = 0; i < nel; i++) - { - octave_value ov = identifier(i); - std::string ovs = ov.string_value (); - - if (! all_found && ovs == "all") - { - all_state = check_state (state(i).string_value ()); - - if (all_state >= 0) - all_found = true; - } - - if (! id_found && ovs == id) - { - id_state = check_state (state(i).string_value ()); - - if (id_state >= 0) - id_found = true; - } - - if (all_found && id_found) - break; - } - } - - // If "all" is not present, assume warnings are enabled. - if (all_state == -1) - all_state = 1; - - if (all_state == 0) - { - if (id_state >= 0) - retval = id_state; - } - else if (all_state == 1) - { - if (id_state == 0 || id_state == 2) - retval = id_state; - else - retval = all_state; - } - else if (all_state == 2) - { - if (id_state == 0) - retval= id_state; - else - retval = all_state; - } - - return retval; -} - -static void -warning_1 (const char *id, const char *fmt, va_list args) -{ - int warn_opt = warning_enabled (id); - - if (warn_opt == 2) - { - // Handle this warning as an error. - - error_1 (std::cerr, "error", id, fmt, args); - } - else if (warn_opt == 1) - { - bool fmt_suppresses_backtrace = false; - size_t fmt_len = (fmt ? strlen (fmt) : 0); - fmt_suppresses_backtrace = (fmt_len > 0 && fmt[fmt_len-1] == '\n'); - - if (fmt_suppresses_backtrace && fmt_len > 1) - { - // Strip newline before issuing warning - std::string tmp_fmt (fmt, fmt_len - 1); - vwarning ("warning", id, tmp_fmt.c_str (), args); - } - else - vwarning ("warning", id, fmt, args); - - octave::call_stack& cs = octave::__get_call_stack__ ("warning_1"); - - bool in_user_code = cs.caller_user_code () != nullptr; - - if (! fmt_suppresses_backtrace && in_user_code - && Vbacktrace_on_warning - && ! discard_warning_messages) - pr_where (std::cerr, "warning"); - - octave::bp_table& bptab - = octave::__get_bp_table__ ("warning_1"); - - if ((octave::application::interactive () - || octave::application::forced_interactive ()) - && Vdebug_on_warning && in_user_code && bptab.debug_on_warn (id)) - { - octave::unwind_protect frame; - frame.protect_var (Vdebug_on_warning); - Vdebug_on_warning = false; - - octave::tree_evaluator& tw - = octave::__get_evaluator__ ("warning_1"); - - tw.debug_mode (true); - tw.current_frame (cs.current_frame ()); - - octave::input_system& input_sys - = octave::__get_input_system__ ("warning_1"); - - input_sys.keyboard (); - } - } + octave::error_system& es = octave::__get_error_system__ ("warning_enabled"); + + return es.warning_enabled (id); } void vwarning (const char *fmt, va_list args) { - warning_1 ("", fmt, args); + vwarning ("", fmt, args); } void @@ -804,7 +1315,7 @@ void vwarning_with_id (const char *id, const char *fmt, va_list args) { - warning_1 (id, fmt, args); + vwarning (id, fmt, args); } void @@ -812,7 +1323,7 @@ { va_list args; va_start (args, fmt); - vwarning_with_id (id, fmt, args); + vwarning (id, fmt, args); va_end (args); } @@ -855,177 +1366,33 @@ va_end (args); } -static std::list -make_stack_frame_list (const octave_map& stack) +OCTAVE_NORETURN +void +vpanic (const char *fmt, va_list args) { - std::list frames; - - Cell name = stack.contents ("name"); - Cell line = stack.contents ("line"); - Cell column; - bool have_column = false; - if (stack.contains ("column")) - { - have_column = true; - column = stack.contents ("column"); - } - - octave_idx_type nel = name.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - error_stack_frame frame; - - frame.name = name(i).string_value (); - frame.line = line(i).int_value (); - frame.column = (have_column ? column(i).int_value () : -1); - - frames.push_back (frame); - } - - return frames; -} - -static void -rethrow_error_1 (const char *id, const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - verror (false, std::cerr, nullptr, id, fmt, args); - va_end (args); + octave::error_system& es = octave::__get_error_system__ ("vpanic"); + + es.vpanic (fmt, args); } -OCTAVE_NORETURN static -void -rethrow_error (const std::string& id, const std::string& msg, - const octave_map& stack) -{ - octave::execution_exception e = make_execution_exception ("error"); - - if (! stack.isempty () - && ! (stack.contains ("file") && stack.contains ("name") - && stack.contains ("line"))) - error ("rethrow: STACK struct must contain the fields 'file', 'name', and 'line'"); - - Vlast_error_id = id; - Vlast_error_message = msg; - Vlast_error_stack = stack; - - size_t len = msg.length (); - - std::string tmp_msg (msg); - if (len > 1 && tmp_msg[len-1] == '\n') - { - tmp_msg.erase (len - 1); - - rethrow_error_1 (id.c_str (), "%s\n", tmp_msg.c_str ()); - } - else - rethrow_error_1 (id.c_str (), "%s", tmp_msg.c_str ()); - - if (! stack.isempty ()) - { - std::ostringstream buf; - - pr_where (buf, "error", make_stack_frame_list (stack)); - - e.set_stack_trace (buf.str ()); - } - - throw e; -} - +OCTAVE_NORETURN void panic (const char *fmt, ...) { va_list args; va_start (args, fmt); - buffer_error_messages = 0; - discard_error_messages = false; - verror (false, std::cerr, "panic", "", fmt, args); - va_end (args); - abort (); -} - -static void -defun_usage_message_1 (const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - error_1 (octave_stdout, nullptr, "", fmt, args); + vpanic (fmt, args); va_end (args); } void defun_usage_message (const std::string& msg) { - defun_usage_message_1 ("%s", msg.c_str ()); + defun_usage_message ("%s", msg.c_str ()); } -typedef void (*error_fun)(const char *, const char *, ...); - -extern octave_value_list Fsprintf (const octave_value_list&, int); - -static std::string -handle_message (error_fun f, const char *id, const char *msg, - const octave_value_list& args, bool have_fmt) -{ - std::string retval; - - std::string tmpstr; - - if (args.length () > 0) - { - octave_value arg; - - if (have_fmt) - { - octave_value_list tmp = Fsprintf (args, 1); - arg = tmp(0); - } - else - arg = args(0); - - if (arg.is_defined ()) - { - if (arg.isempty ()) - return retval; - else if (arg.is_string ()) - { - tmpstr = arg.string_value (); // 2-stage assignment required - msg = tmpstr.c_str (); // in order to generate pointer - // to valid memory. - } - } - } - - // Ugh. - - size_t len = strlen (msg); - - if (len > 0) - { - if (msg[len - 1] == '\n') - { - if (len > 1) - { - std::string tmp_msg (msg, len - 1); - f (id, "%s\n", tmp_msg.c_str ()); - retval = tmp_msg; - } - } - else - { - f (id, "%s", msg); - retval = msg; - } - } - - return retval; -} - -DEFUN (rethrow, args, , - doc: /* -*- texinfo -*- +DEFMETHOD (rethrow, interp, args, , + doc: /* -*- texinfo -*- @deftypefn {} {} rethrow (@var{err}) Reissue a previous error as defined by @var{err}. @@ -1047,64 +1414,18 @@ std::string msg = err.contents ("message").string_value (); std::string id = err.contents ("identifier").string_value (); - octave_map err_stack = initialize_last_error_stack (); + octave_map err_stack = octave::init_error_stack (interp); if (err.contains ("stack")) err_stack = err.contents ("stack").xmap_value ("ERR.STACK must be a struct"); - rethrow_error (id, msg, err_stack); + octave::error_system& es = interp.get_error_system (); + + es.rethrow_error (id, msg, err_stack); return ovl (); } -// Determine whether the first argument to error or warning function -// should be handled as the message identifier or as the format string. - -static bool -maybe_extract_message_id (const std::string& caller, - const octave_value_list& args, - octave_value_list& nargs, - std::string& id) -{ - nargs = args; - id = ""; - - int nargin = args.length (); - - bool have_fmt = nargin > 1; - - if (nargin > 0) - { - std::string arg1 = args(0).string_value (); - - // For compatibility with Matlab, an identifier must contain ':', - // but not at the beginning or the end, and it must not contain '%' - // (even if it is not a valid conversion operator) or whitespace. - - if (arg1.find_first_of ("% \f\n\r\t\v") == std::string::npos - && arg1.find (':') != std::string::npos - && arg1[0] != ':' - && arg1.back () != ':') - { - if (nargin > 1) - { - id = arg1; - - nargs.resize (nargin-1); - - for (int i = 1; i < nargin; i++) - nargs(i-1) = args(i); - } - else - nargs(0) = "call to " + caller - + " with message identifier '" + arg1 - + "' requires message"; - } - } - - return have_fmt; -} - DEFUN (error, args, , doc: /* -*- texinfo -*- @deftypefn {} {} error (@var{template}, @dots{}) @@ -1248,177 +1569,6 @@ return retval; } -static octave_scalar_map -warning_query (const std::string& id_arg) -{ - octave_scalar_map retval; - - std::string id = id_arg; - - if (id == "last") - id = Vlast_warning_id; - - Cell ident = warning_options.contents ("identifier"); - Cell state = warning_options.contents ("state"); - - octave_idx_type nel = ident.numel (); - - assert (nel != 0); - - bool found = false; - - std::string val; - - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == id) - { - val = state(i).string_value (); - found = true; - break; - } - } - - if (! found) - { - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == "all") - { - val = state(i).string_value (); - found = true; - break; - } - } - } - - // The warning state "all" is always supposed to remain in the list, - // so we should always find a state, either explicitly or by using the - // state for "all". - - assert (found); - - retval.assign ("identifier", id); - retval.assign ("state", val); - - return retval; -} - -static std::string -default_warning_state (void) -{ - std::string retval = "on"; - - Cell ident = warning_options.contents ("identifier"); - Cell state = warning_options.contents ("state"); - - octave_idx_type nel = ident.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == "all") - { - retval = state(i).string_value (); - break; - } - } - - return retval; -} - -static void -display_warning_options (std::ostream& os) -{ - Cell ident = warning_options.contents ("identifier"); - Cell state = warning_options.contents ("state"); - - octave_idx_type nel = ident.numel (); - - std::string all_state = default_warning_state (); - - if (all_state == "on") - os << "By default, warnings are enabled."; - else if (all_state == "off") - os << "By default, warnings are disabled."; - else if (all_state == "error") - os << "By default, warnings are treated as errors."; - else - panic_impossible (); - - if (nel > 1) - os << "\n\n"; - - // The state for all is always supposed to be first in the list. - - for (octave_idx_type i = 1; i < nel; i++) - { - std::string tid = ident(i).string_value (); - std::string tst = state(i).string_value (); - - os << std::setw (7) << tst << " " << tid << "\n"; - } - - os << std::endl; -} - -static void -set_warning_option (const std::string& state, const std::string& ident) -{ - std::string all_state = default_warning_state (); - - if (state != "on" && state != "off" && state != "error") - error ("invalid warning state: %s", state.c_str ()); - - Cell tid = warning_options.contents ("identifier"); - Cell tst = warning_options.contents ("state"); - - octave_idx_type nel = tid.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - if (tid(i).string_value () == ident) - { - // We found it in the current list of options. If the state - // for "all" is same as arg1, we can simply remove the item - // from the list. - - if (state == all_state && ident != "all") - { - for (i = i + 1; i < nel; i++) - { - tid(i-1) = tid(i); - tst(i-1) = tst(i); - } - - tid.resize (dim_vector (1, nel-1)); - tst.resize (dim_vector (1, nel-1)); - } - else - tst(i) = state; - - warning_options.clear (); - - warning_options.assign ("identifier", tid); - warning_options.assign ("state", tst); - - return; - } - } - - // The option wasn't already in the list. Append it. - - tid.resize (dim_vector (1, nel+1)); - tst.resize (dim_vector (1, nel+1)); - - tid(nel) = ident; - tst(nel) = state; - - warning_options.clear (); - - warning_options.assign ("identifier", tid); - warning_options.assign ("state", tst); -} - DEFMETHOD (warning, interp, args, nargout, doc: /* -*- texinfo -*- @deftypefn {} {} warning (@var{template}, @dots{}) @@ -1546,6 +1696,8 @@ int nargin = args.length (); bool done = false; + octave::error_system& es = interp.get_error_system (); + if (nargin > 0 && args.all_strings_p ()) { string_vector argv = args.make_argv ("warning"); @@ -1561,26 +1713,22 @@ // Prepare output structure octave_map old_warning_options; if (arg2 == "all") - old_warning_options = warning_options; + old_warning_options = es.warning_options (); else - old_warning_options = octave_map (warning_query (arg2)); - - octave::symbol_table& symtab = interp.get_symbol_table (); - - if (nargin == 3 && argv[3] == "local" - && ! symtab.at_top_level ()) + old_warning_options = octave_map (es.warning_query (arg2)); + + if (nargin == 3 && argv[3] == "local" && ! interp.at_top_level ()) { - octave::symbol_scope scope - = symtab.require_current_scope ("warning"); - - octave_scalar_map val = warning_query (arg2); + octave_scalar_map val = es.warning_query (arg2); octave_value curr_state = val.contents ("state"); // FIXME: this might be better with a dictionary object. + octave::tree_evaluator& tw = interp.get_evaluator (); + octave_value curr_warning_states - = scope.varval (".saved_warning_states."); + = tw.get_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES); octave_map m; @@ -1628,7 +1776,7 @@ m.contents ("identifier") = ids; m.contents ("state") = states; - scope.force_assign (".saved_warning_states.", m); + tw.set_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES, m); // Now ignore the "local" argument and continue to // handle the current setting. @@ -1665,7 +1813,7 @@ tmp.assign ("identifier", id); tmp.assign ("state", st); - warning_options = tmp; + es.warning_options (tmp); done = true; } @@ -1673,7 +1821,7 @@ { if (arg1 != "error") { - Vbacktrace_on_warning = (arg1 == "on"); + es.backtrace_on_warning (arg1 == "on"); done = true; } } @@ -1681,7 +1829,7 @@ { if (arg1 != "error") { - Vdebug_on_warning = (arg1 == "on"); + es.debug_on_warning (arg1 == "on"); done = true; } } @@ -1689,7 +1837,7 @@ { if (arg1 != "error") { - Vverbose_warning = (arg1 == "on"); + es.verbose_warning (arg1 == "on"); done = true; } } @@ -1697,16 +1845,16 @@ { if (arg1 != "error") { - Vquiet_warning = (arg1 == "on"); + es.quiet_warning (arg1 == "on"); done = true; } } else { if (arg2 == "last") - arg2 = Vlast_warning_id; - - set_warning_option (arg1, arg2); + arg2 = es.last_warning_id (); + + es.set_warning_option (arg1, arg2); done = true; } @@ -1717,25 +1865,25 @@ else if (arg1 == "query") { if (arg2 == "all") - retval = warning_options; + retval = es.warning_options (); else if (arg2 == "backtrace" || arg2 == "debug" || arg2 == "verbose" || arg2 == "quiet") { octave_scalar_map tmp; tmp.assign ("identifier", arg2); if (arg2 == "backtrace") - tmp.assign ("state", Vbacktrace_on_warning ? "on" : "off"); + tmp.assign ("state", es.backtrace_on_warning () ? "on" : "off"); else if (arg2 == "debug") - tmp.assign ("state", Vdebug_on_warning ? "on" : "off"); + tmp.assign ("state", es.debug_on_warning () ? "on" : "off"); else if (arg2 == "verbose") - tmp.assign ("state", Vverbose_warning ? "on" : "off"); + tmp.assign ("state", es.verbose_warning () ? "on" : "off"); else - tmp.assign ("state", Vquiet_warning ? "on" : "off"); + tmp.assign ("state", es.quiet_warning () ? "on" : "off"); retval = tmp; } else - retval = warning_query (arg2); + retval = es.warning_query (arg2); done = true; } @@ -1743,9 +1891,9 @@ else if (nargin == 0) { if (nargout > 0) - retval = warning_options; + retval = es.warning_options (); else - display_warning_options (octave_stdout); + es.display_warning_options (octave_stdout); done = true; } @@ -1776,7 +1924,7 @@ for (octave_idx_type i = 0; i < nel; i++) { std::string tid = ident(i).string_value (); - oldstate(i) = warning_query (tid).getfield ("state"); + oldstate(i) = es.warning_query (tid).getfield ("state"); } old_warning_options.setfield ("state", oldstate); @@ -1786,7 +1934,7 @@ std::string tst = state(i).string_value (); std::string tid = ident(i).string_value (); - set_warning_option (tst, tid); + es.set_warning_option (tst, tid); } done = true; @@ -1804,7 +1952,7 @@ bool have_fmt = maybe_extract_message_id ("warning", args, nargs, id); - std::string prev_msg = Vlast_warning_message; + std::string prev_msg = es.last_warning_message (); std::string curr_msg = handle_message (warning_with_id, id.c_str (), "unspecified warning", nargs, @@ -1825,7 +1973,7 @@ /* %!test <*51997> -%! id = "Octave:divide-by-zero"; +%! id = "Octave:logical-conversion"; %! current = warning ("query", id); %! current_all = warning (); %! previous = warning (current_all); @@ -1862,29 +2010,9 @@ void disable_warning (const std::string& id) { - set_warning_option ("off", id); -} - -void -initialize_default_warning_state (void) -{ - initialize_warning_options ("on"); - - // Most people will want to have the following disabled. - - disable_warning ("Octave:array-as-logical"); - disable_warning ("Octave:array-to-scalar"); - disable_warning ("Octave:array-to-vector"); - disable_warning ("Octave:imag-to-real"); - disable_warning ("Octave:language-extension"); - disable_warning ("Octave:missing-semicolon"); - disable_warning ("Octave:neg-dim-as-zero"); - disable_warning ("Octave:resize-on-range-error"); - disable_warning ("Octave:separator-insert"); - disable_warning ("Octave:single-quote-string"); - disable_warning ("Octave:str-to-num"); - disable_warning ("Octave:mixed-string-concat"); - disable_warning ("Octave:variable-switch-label"); + octave::error_system& es = octave::__get_error_system__ ("disable_warning"); + + es.disable_warning (id); } DEFMETHOD (lasterror, interp, args, , @@ -1939,24 +2067,28 @@ if (nargin > 1) print_usage (); + octave::error_system& es = interp.get_error_system (); + octave_scalar_map err; - err.assign ("message", Vlast_error_message); - err.assign ("identifier", Vlast_error_id); - - err.assign ("stack", octave_value (Vlast_error_stack)); + err.assign ("message", es.last_error_message ()); + err.assign ("identifier", es.last_error_id ()); + + err.assign ("stack", octave_value (es.last_error_stack ())); if (nargin == 1) { + octave::tree_evaluator& tw = interp.get_evaluator (); + if (args(0).is_string ()) { if (args(0).string_value () != "reset") error ("lasterror: unrecognized string argument"); - Vlast_error_message = ""; - Vlast_error_id = ""; - - Vlast_error_stack = initialize_last_error_stack (); + es.last_error_message (""); + es.last_error_id (""); + + es.last_error_stack (tw.empty_backtrace ()); } else if (args(0).isstruct ()) { @@ -2023,28 +2155,22 @@ } } - Vlast_error_message = new_error_message; - Vlast_error_id = new_error_id; + es.last_error_message (new_error_message); + es.last_error_id (new_error_id); if (initialize_stack) - Vlast_error_stack = initialize_last_error_stack (); + es.last_error_stack (tw.empty_backtrace ()); else if (new_err.contains ("stack")) { new_err_stack.setfield ("file", new_error_file); new_err_stack.setfield ("name", new_error_name); new_err_stack.setfield ("line", new_error_line); new_err_stack.setfield ("column", new_error_column); - Vlast_error_stack = new_err_stack; + + es.last_error_stack (new_err_stack); } else - { - // No stack field. Fill it in with backtrace info. - octave_idx_type curr_frame = -1; - - octave::call_stack& cs = interp.get_call_stack (); - - Vlast_error_stack = cs.backtrace (0, curr_frame); - } + es.last_error_stack (tw.backtrace ()); } else error ("lasterror: argument must be a structure or a string"); @@ -2066,8 +2192,8 @@ %! assert (y, x); */ -DEFUN (lasterr, args, nargout, - doc: /* -*- texinfo -*- +DEFMETHOD (lasterr, interp, args, nargout, + doc: /* -*- texinfo -*- @deftypefn {} {[@var{msg}, @var{msgid}] =} lasterr () @deftypefnx {} {} lasterr (@var{msg}) @deftypefnx {} {} lasterr (@var{msg}, @var{msgid}) @@ -2087,20 +2213,22 @@ if (nargin > 2) print_usage (); + octave::error_system& es = interp.get_error_system (); + string_vector argv = args.make_argv ("lasterr"); - std::string prev_error_id = Vlast_error_id; - std::string prev_error_message = Vlast_error_message; + std::string prev_error_id = es.last_error_id (); + std::string prev_error_message = es.last_error_message (); if (nargin == 2) { - Vlast_error_id = argv[2]; - Vlast_error_message = argv[1]; + es.last_error_id (argv[2]); + es.last_error_message (argv[1]); } else if (nargin == 1) { - Vlast_error_id = ""; - Vlast_error_message = argv[1]; + es.last_error_id (""); + es.last_error_message (argv[1]); } if (nargin == 0 || nargout > 0) @@ -2109,8 +2237,8 @@ return ovl (); } -DEFUN (lastwarn, args, nargout, - doc: /* -*- texinfo -*- +DEFMETHOD (lastwarn, interp, args, nargout, + doc: /* -*- texinfo -*- @deftypefn {} {[@var{msg}, @var{msgid}] =} lastwarn () @deftypefnx {} {} lastwarn (@var{msg}) @deftypefnx {} {} lastwarn (@var{msg}, @var{msgid}) @@ -2130,20 +2258,22 @@ if (nargin > 2) print_usage (); + octave::error_system& es = interp.get_error_system (); + string_vector argv = args.make_argv ("lastwarn"); - std::string prev_warning_id = Vlast_warning_id; - std::string prev_warning_message = Vlast_warning_message; + std::string prev_warning_id = es.last_warning_id (); + std::string prev_warning_message = es.last_warning_message (); if (nargin == 2) { - Vlast_warning_id = argv[2]; - Vlast_warning_message = argv[1]; + es.last_warning_id (argv[2]); + es.last_warning_message (argv[1]); } else if (nargin == 1) { - Vlast_warning_id = ""; - Vlast_warning_message = argv[1]; + es.last_warning_id (""); + es.last_warning_message (argv[1]); } if (nargin == 0 || nargout > 0) @@ -2152,8 +2282,8 @@ return ovl (); } -DEFUN (beep_on_error, args, nargout, - doc: /* -*- texinfo -*- +DEFMETHOD (beep_on_error, interp, args, nargout, + doc: /* -*- texinfo -*- @deftypefn {} {@var{val} =} beep_on_error () @deftypefnx {} {@var{old_val} =} beep_on_error (@var{new_val}) @deftypefnx {} {} beep_on_error (@var{new_val}, "local") @@ -2165,11 +2295,13 @@ The original variable value is restored when exiting the function. @end deftypefn */) { - return SET_INTERNAL_VARIABLE (beep_on_error); + octave::error_system& es = interp.get_error_system (); + + return es.beep_on_error (args, nargout); } -DEFUN (debug_on_error, args, nargout, - doc: /* -*- texinfo -*- +DEFMETHOD (debug_on_error, interp, args, nargout, + doc: /* -*- texinfo -*- @deftypefn {} {@var{val} =} debug_on_error () @deftypefnx {} {@var{old_val} =} debug_on_error (@var{new_val}) @deftypefnx {} {} debug_on_error (@var{new_val}, "local") @@ -2185,11 +2317,13 @@ @seealso{debug_on_warning, debug_on_interrupt} @end deftypefn */) { - return SET_INTERNAL_VARIABLE (debug_on_error); + octave::error_system& es = interp.get_error_system (); + + return es.debug_on_error (args, nargout); } -DEFUN (debug_on_warning, args, nargout, - doc: /* -*- texinfo -*- +DEFMETHOD (debug_on_warning, interp, args, nargout, + doc: /* -*- texinfo -*- @deftypefn {} {@var{val} =} debug_on_warning () @deftypefnx {} {@var{old_val} =} debug_on_warning (@var{new_val}) @deftypefnx {} {} debug_on_warning (@var{new_val}, "local") @@ -2202,48 +2336,76 @@ @seealso{debug_on_error, debug_on_interrupt} @end deftypefn */) { - return SET_INTERNAL_VARIABLE (debug_on_warning); + octave::error_system& es = interp.get_error_system (); + + return es.debug_on_warning (args, nargout); } std::string last_error_message (void) { - return Vlast_error_message; + octave::error_system& es + = octave::__get_error_system__ ("last_error_message"); + + return es.last_error_message (); } std::string last_error_id (void) { - return Vlast_error_id; + octave::error_system& es + = octave::__get_error_system__ ("last_error_id"); + + return es.last_error_id (); } octave_map last_error_stack (void) { - return Vlast_error_stack; + octave::error_system& es + = octave::__get_error_system__ ("last_error_stack"); + + return es.last_error_stack (); } std::string last_warning_message (void) { - return Vlast_warning_message; + octave::error_system& es + = octave::__get_error_system__ ("last_warning_message"); + + return es.last_warning_message (); } std::string last_warning_id (void) { - return Vlast_warning_id; + octave::error_system& es + = octave::__get_error_system__ ("last_warning_id"); + + return es.last_warning_id (); } void -interpreter_try (octave::unwind_protect& frame) +interpreter_try (octave::unwind_protect& frame, + octave::error_system::try_option opt) { - frame.protect_var (buffer_error_messages); - frame.protect_var (Vdebug_on_error); - frame.protect_var (Vdebug_on_warning); - - buffer_error_messages++; - Vdebug_on_error = false; - Vdebug_on_warning = false; - // leave Vdebug_on_caught as it was, so errors in try/catch are still caught + octave::error_system& es + = octave::__get_error_system__ ("interpreter_try"); + + es.interpreter_try (frame, opt); } + +// Deprecated variables and functions. + +// This variable is obsolete and always has the value 0. +int error_state = 0; + +void +reset_error_handler (void) +{ + octave::error_system& es + = octave::__get_error_system__ ("reset_error_handler"); + + es.reset (); +} diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/error.h --- a/libinterp/corefcn/error.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/error.h Fri Jul 12 12:14:43 2019 -0400 @@ -31,18 +31,398 @@ #include "unwind-prot.h" -class octave_map; +#include "oct-map.h" + class octave_value_list; namespace octave { class execution_exception; } +namespace octave +{ + class error_system + { + public: + + enum try_option + { + buffer = 1, + discard = 2, + }; + + error_system (interpreter& interp); + + error_system (const error_system&) = delete; + + error_system& operator = (const error_system&) = delete; + + ~error_system (void) = default; + + void reset (void) + { + m_buffer_error_messages = 0; + m_in_try_catch = 0; + m_discard_error_messages = false; + } + + octave_value debug_on_error (const octave_value_list& args, int nargout); + + void set_debug_on_error (bool flag) { m_debug_on_error = flag; } + + bool debug_on_error (void) const { return m_debug_on_error; } + + bool debug_on_error (bool flag) + { + bool val = m_debug_on_error; + m_debug_on_error = flag; + return val; + } + + octave_value debug_on_caught (const octave_value_list& args, int nargout); + + void set_debug_on_caught (bool flag) { m_debug_on_caught = flag; } + + bool debug_on_caught (void) const { return m_debug_on_caught; } + + bool debug_on_caught (bool flag) + { + bool val = m_debug_on_caught; + m_debug_on_caught = flag; + return val; + } + + octave_value debug_on_warning (const octave_value_list& args, int nargout); + + void set_debug_on_warning (bool flag) { m_debug_on_warning = flag; } + + bool debug_on_warning (void) const { return m_debug_on_warning; } + + bool debug_on_warning (bool flag) + { + bool val = m_debug_on_warning; + m_debug_on_warning = flag; + return val; + } + + octave_value buffer_error_messages (const octave_value_list& args, int nargout); + + void set_buffer_error_messages (int val) { m_buffer_error_messages = val; } + + int buffer_error_messages (void) const { return m_buffer_error_messages; } + + int buffer_error_messages (int new_val) + { + int val = m_buffer_error_messages; + m_buffer_error_messages = new_val; + return val; + } + + octave_value in_try_catch (const octave_value_list& args, int nargout); + + void set_in_try_catch (int val) { m_in_try_catch = val; } + + int in_try_catch (void) const { return m_in_try_catch; } + + int in_try_catch (int new_val) + { + int val = m_in_try_catch; + m_in_try_catch = new_val; + return val; + } + + octave_value discard_error_messages (const octave_value_list& args, int nargout); + + void set_discard_error_messages (bool flag) { m_discard_error_messages = flag; } + + bool discard_error_messages (void) const { return m_discard_error_messages; } + + bool discard_error_messages (bool flag) + { + bool val = m_discard_error_messages; + m_discard_error_messages = flag; + return val; + } + + octave_value discard_warning_messages (const octave_value_list& args, int nargout); + + void set_discard_warning_messages (bool flag) { m_discard_warning_messages = flag; } + + bool discard_warning_messages (void) const { return m_discard_warning_messages; } + + bool discard_warning_messages (bool flag) + { + bool val = m_discard_warning_messages; + m_discard_warning_messages = flag; + return val; + } + + octave_value beep_on_error (const octave_value_list& args, int nargout); + + void set_beep_on_error (bool flag) { m_beep_on_error = flag; } + + bool beep_on_error (void) const { return m_beep_on_error; } + + bool beep_on_error (bool flag) + { + bool val = m_beep_on_error; + m_beep_on_error = flag; + return val; + } + + octave_value backtrace_on_warning (const octave_value_list& args, int nargout); + + void set_backtrace_on_warning (bool flag) { m_backtrace_on_warning = flag; } + + bool backtrace_on_warning (void) const { return m_backtrace_on_warning; } + + bool backtrace_on_warning (bool flag) + { + bool val = m_backtrace_on_warning; + m_backtrace_on_warning = flag; + return val; + } + + octave_value verbose_warning (const octave_value_list& args, int nargout); + + void set_verbose_warning (bool flag) { m_verbose_warning = flag; } + + bool verbose_warning (void) const { return m_verbose_warning; } + + bool verbose_warning (bool flag) + { + bool val = m_verbose_warning; + m_verbose_warning = flag; + return val; + } + + octave_value quiet_warning (const octave_value_list& args, int nargout); + + void set_quiet_warning (bool flag) { m_quiet_warning = flag; } + + bool quiet_warning (void) const { return m_quiet_warning; } + + bool quiet_warning (bool flag) + { + bool val = m_quiet_warning; + m_quiet_warning = flag; + return val; + } + + octave_map warning_options (void) const { return m_warning_options; } + + void set_warning_options (const octave_map& val) { m_warning_options = val; } + + octave_map warning_options (const octave_map& new_val) + { + octave_map val = m_warning_options; + m_warning_options = new_val; + return val; + } + + octave_value last_error_message (const octave_value_list& args, int nargout); + + void set_last_error_message (const std::string& val) { m_last_error_message = val; } + + std::string last_error_message (void) const { return m_last_error_message; } + + std::string last_error_message (const std::string& s) + { + std::string val = m_last_error_message; + m_last_error_message = s; + return val; + } + + octave_value last_warning_message (const octave_value_list& args, int nargout); + + void set_last_warning_message (const std::string& val) { m_last_warning_message = val; } + + std::string last_warning_message (void) const { return m_last_warning_message; } + + std::string last_warning_message (const std::string& s) + { + std::string val = m_last_warning_message; + m_last_warning_message = s; + return val; + } + + octave_value last_warning_id (const octave_value_list& args, int nargout); + + void set_last_warning_id (const std::string& val) { m_last_warning_id = val; } + + std::string last_warning_id (void) const { return m_last_warning_id; } + + std::string last_warning_id (const std::string& s) + { + std::string val = m_last_warning_id; + m_last_warning_id = s; + return val; + } + + octave_value last_error_id (const octave_value_list& args, int nargout); + + void set_last_error_id (const std::string& val) { m_last_error_id = val; } + + std::string last_error_id (void) const { return m_last_error_id; } + + std::string last_error_id (const std::string& s) + { + std::string val = m_last_error_id; + m_last_error_id = s; + return val; + } + + void set_last_error_stack (const octave_map& val) + { + m_last_error_stack = val; + } + + octave_map last_error_stack (void) const { return m_last_error_stack; } + + octave_map last_error_stack (const octave_map& new_val) + { + octave_map val = m_last_error_stack; + m_last_error_stack = new_val; + return val; + } + + //! For given warning ID, return 0 if warnings are disabled, 1 if + //! enabled, and 2 if the given ID should be an error instead of a + //! warning. + + int warning_enabled (const std::string& id); + + void verror (bool save_last_error, std::ostream& os, const char *name, + const char *id, const char *fmt, va_list args, + bool with_cfn = false); + + void maybe_enter_debugger (execution_exception& e, + bool show_stack_trace = false); + + void vwarning (const char *name, const char *id, const char *fmt, + va_list args); + + OCTAVE_NORETURN + void error_1 (execution_exception& e, std::ostream& os, + const char *name, const char *id, const char *fmt, + va_list args, bool with_cfn = false); + + OCTAVE_NORETURN + void error_1 (std::ostream& os, const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn); + + void vwarning (const char *id, const char *fmt, va_list args); + + void rethrow_error (const char *id, const char *fmt, ...); + + void rethrow_error (const std::string& id, const std::string& msg, + const octave_map& stack); + + OCTAVE_NORETURN + void vpanic (const char *fmt, va_list args); + + OCTAVE_NORETURN + void panic (const char *fmt, ...); + + octave_scalar_map warning_query (const std::string& id_arg); + + std::string default_warning_state (void); + + void display_warning_options (std::ostream& os); + + void set_warning_option (const std::string& state, const std::string& id); + + void disable_warning (const std::string& id); + + void initialize_default_warning_state (void); + + void interpreter_try (octave::unwind_protect& frame, + try_option = buffer); + + private: + + interpreter& m_interpreter; + + //! TRUE means that Octave will try to enter the debugger when an error + //! is encountered. This will also inhibit printing of the normal + //! traceback message (you will only see the top-level error message). + + bool m_debug_on_error; + + //! TRUE means that Octave will try to enter the debugger when an error + //! is encountered within the 'try' section of a 'try' / 'catch' block. + + bool m_debug_on_caught; + + //! TRUE means that Octave will try to enter the debugger when a warning + //! is encountered. + + bool m_debug_on_warning; + + //! Tell the error handler whether to print messages, or just store + //! them for later. Used for handling errors in eval() and + //! the 'unwind_protect' statement. + + int m_buffer_error_messages; + + //! The number of layers of try / catch blocks we're in. Used to print + //! "caught error" instead of "error" when "dbstop if caught error" is on. + + int m_in_try_catch; + + //! TRUE means error messages are turned off. + + bool m_discard_error_messages; + + //! TRUE means warning messages are turned off. + + bool m_discard_warning_messages; + + //! TRUE means that Octave will try to beep obnoxiously before + //! printing error messages. + bool m_beep_on_error; + + //! TRUE means that Octave will try to display a stack trace when a + //! warning is encountered. + bool m_backtrace_on_warning; + + //! TRUE means that Octave will print a verbose warning. Currently + //! unused. + bool m_verbose_warning; + + //! TRUE means that Octave will print no warnings, but lastwarn will + //! be updated + bool m_quiet_warning; + + //! A structure containing (most of) the current state of warnings. + octave_map m_warning_options; + + //! The text of the last error message. + std::string m_last_error_message; + + //! The text of the last warning message. + std::string m_last_warning_message; + + //! The last warning message id. + std::string m_last_warning_id; + + //! The last error message id. + std::string m_last_error_id; + + //! The last file in which an error occurred. + octave_map m_last_error_stack; + }; +} + +// FIXME: should we move the following functions inside the octave +// namespace? If so, should the functions outside of the namespace be +// deprecated? Doing that might cause a lot of trouble... If they are +// not deprecated and eventually removed, does it make sense to also +// define them inside the octave namespace? + #define panic_impossible() \ panic ("impossible state reached in file '%s' at line %d", __FILE__, __LINE__) -extern OCTINTERP_API void reset_error_handler (void); - extern OCTINTERP_API int warning_enabled (const std::string& id); extern OCTINTERP_API octave::execution_exception @@ -55,51 +435,60 @@ extern OCTINTERP_API void message (const char *name, const char *fmt, ...); extern OCTINTERP_API void vwarning (const char *fmt, va_list args); + OCTAVE_FORMAT_PRINTF (1, 2) extern OCTINTERP_API void warning (const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void verror (const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void verror (const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void error (const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void error (const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void verror (octave::execution_exception&, const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +verror (octave::execution_exception&, const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error (octave::execution_exception&, const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +error (octave::execution_exception&, const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_cfn (const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_cfn (const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_cfn (const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_cfn (const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void vparse_error (const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +vparse_error (const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void parse_error (const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +parse_error (const char *fmt, ...); extern OCTINTERP_API void -vmessage_with_id (const char *id, const char *name, - const char *fmt, va_list args); +vmessage_with_id (const char *id, const char *name, const char *fmt, + va_list args); OCTAVE_FORMAT_PRINTF (3, 4) extern OCTINTERP_API void message_with_id (const char *id, const char *name, const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void vusage_with_id (const char *id, const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +vusage_with_id (const char *id, const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void usage_with_id (const char *id, const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +usage_with_id (const char *id, const char *fmt, ...); extern OCTINTERP_API void vwarning_with_id (const char *id, const char *fmt, va_list args); @@ -108,30 +497,39 @@ extern OCTINTERP_API void warning_with_id (const char *id, const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_id (const char *id, const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_id (const char *id, const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_id (const char *id, const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_id (const char *id, const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_id_cfn (const char *id, const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_id_cfn (const char *id, const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_id_cfn (const char *id, const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_id_cfn (const char *id, const char *fmt, ...); -OCTAVE_NORETURN OCTINTERP_API extern -void vparse_error_with_id (const char *id, const char *fmt, va_list args); +OCTAVE_NORETURN +extern OCTINTERP_API void +vparse_error_with_id (const char *id, const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void parse_error_with_id (const char *id, const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void +parse_error_with_id (const char *id, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void vpanic (const char *fmt, va_list args); OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void panic (const char *fmt, ...); +OCTAVE_NORETURN +extern OCTINTERP_API void panic (const char *fmt, ...); //! Helper function for print_usage defined in defun.cc. @@ -144,62 +542,15 @@ set_warning_state (const octave_value_list& args); extern OCTINTERP_API void disable_warning (const std::string& id); -extern OCTINTERP_API void initialize_default_warning_state (void); - -//! TRUE means that Octave will try to enter the debugger when an error -//! is encountered. This will also inhibit printing of the normal -//! traceback message (you will only see the top-level error message). - -extern OCTINTERP_API bool Vdebug_on_error; -//! TRUE means that Octave will try to enter the debugger when an error -//! is encountered within the 'try' section of a 'try' / 'catch' block. - -extern OCTINTERP_API bool Vdebug_on_caught; +extern OCTINTERP_API void +interpreter_try (octave::unwind_protect&, + octave::error_system::try_option = octave::error_system::buffer); -//! TRUE means that Octave will try to enter the debugger when a warning -//! is encountered. - -extern OCTINTERP_API bool Vdebug_on_warning; - -//! Current error state. - +OCTAVE_DEPRECATED (6, "this variable is obsolete and always has the value 0") extern OCTINTERP_API int error_state; -//! Current warning state. - -extern OCTINTERP_API int warning_state; - -//! Tell the error handler whether to print messages, or just store -//! them for later. Used for handling errors in eval() and -//! the 'unwind_protect' statement. - -extern OCTINTERP_API int buffer_error_messages; - -//! The number of layers of try / catch blocks we're in. Used to print -//! "caught error" instead of "error" when "dbstop if caught error" is on. - -extern OCTINTERP_API int in_try_catch; - -//! TRUE means error messages are turned off. - -extern OCTINTERP_API bool discard_error_messages; - -//! TRUE means warning messages are turned off. - -extern OCTINTERP_API bool discard_warning_messages; - -//! Helper functions to pass last error and warning messages and ids. -//! @{ - -extern OCTINTERP_API std::string last_error_message (void); -extern OCTINTERP_API std::string last_error_id (void); -extern OCTINTERP_API octave_map last_error_stack (void); -extern OCTINTERP_API std::string last_warning_message (void); -extern OCTINTERP_API std::string last_warning_id (void); - -//! @} - -extern OCTINTERP_API void interpreter_try (octave::unwind_protect&); +OCTAVE_DEPRECATED (6, "use 'error_system::reset' instead") +extern OCTINTERP_API void reset_error_handler (void); #endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/errwarn.cc --- a/libinterp/corefcn/errwarn.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/errwarn.cc Fri Jul 12 12:14:43 2019 -0400 @@ -324,15 +324,17 @@ } void -warn_divide_by_zero (void) +warn_empty_arg (const char *name) { - warning_with_id ("Octave:divide-by-zero", "division by zero"); + warning ("%s: argument is empty matrix", name); } void -warn_empty_arg (const char *name) +warn_empty_index (const std::string& type_name) { - warning ("%s: argument is empty matrix", name); + warning_with_id ("Octave:empty-index", + "'%s' object indexed with empty index list", + type_name.c_str ()); } void diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/errwarn.h --- a/libinterp/corefcn/errwarn.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/errwarn.h Fri Jul 12 12:14:43 2019 -0400 @@ -164,10 +164,10 @@ const std::string& pkg = "Octave"); OCTINTERP_API extern void -warn_divide_by_zero (void); +warn_empty_arg (const char *name); OCTINTERP_API extern void -warn_empty_arg (const char *name); +warn_empty_index (const std::string& type_name); OCTINTERP_API extern void warn_implicit_conversion (const char *id, const char *from, const char *to); @@ -185,4 +185,13 @@ OCTINTERP_API extern void warn_wrong_type_arg (const char *name, const octave_value& tc); +#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) + +OCTAVE_DEPRECATED (6, "this function will be removed in a future version of Octave") +inline void +warn_divide_by_zero (void) +{ } + #endif + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/event-queue.h --- a/libinterp/corefcn/event-queue.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/event-queue.h Fri Jul 12 12:14:43 2019 -0400 @@ -122,14 +122,4 @@ }; } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::event_queue' instead") -typedef octave::event_queue event_queue; - -OCTAVE_DEPRECATED (4.4, "use 'octave::event_queue_safe' instead") -typedef octave::event_queue_safe event_queue_safe; - #endif - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/fcn-info.cc --- a/libinterp/corefcn/fcn-info.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/fcn-info.cc Fri Jul 12 12:14:43 2019 -0400 @@ -26,7 +26,11 @@ #endif #include "file-ops.h" +#include "file-stat.h" +#include "oct-env.h" +#include "defun.h" +#include "dirfns.h" #include "fcn-info.h" #include "interpreter-private.h" #include "interpreter.h" @@ -34,9 +38,13 @@ #include "ov-fcn.h" #include "ov-usr-fcn.h" #include "parse.h" -#include "symrec.h" #include "symscope.h" #include "symtab.h" +#include "utils.h" + +// Should Octave always check to see if function files have changed +// since they were last compiled? +static int Vignore_function_time_stamp = 1; namespace octave { @@ -333,21 +341,26 @@ // Find function definition according to the following precedence list: // + // nested functions (and subfunctions) + // local functions in the current file // private function // class method // class constructor // command-line function // autoload function - // function on the path + // functions on the load_path (current directory is always first) + // package (FIXME: does this belong here?) // built-in function - // - // Matlab documentation states that constructors have higher precedence - // than methods, but that does not seem to be the case. octave_value - fcn_info::fcn_info_rep::find (const octave_value_list& args) + fcn_info::fcn_info_rep::find (const symbol_scope& scope, + const octave_value_list& args) { - octave_value retval = xfind (args); + symbol_scope search_scope + = (scope + ? scope : __get_current_scope__("fcn_info::fcn_info_rep::find")); + + octave_value retval = xfind (search_scope, args); if (retval.is_undefined ()) { @@ -359,20 +372,278 @@ lp.update (); - retval = xfind (args); + retval = xfind (search_scope, args); + } + + return retval; + } + + + static void + split_name_with_package (const std::string& name, std::string& fname, + std::string& pname) + { + size_t pos = name.rfind ('.'); + + fname.clear (); + pname.clear (); + + if (pos != std::string::npos) + { + fname = name.substr (pos + 1); + pname = name.substr (0, pos); + } + else + fname = name; + } + + // Check the load path to see if file that defined this is still + // visible. If the file is no longer visible, then erase the + // definition and move on. If the file is visible, then we also + // need to check to see whether the file has changed since the + // function was loaded/parsed. However, this check should only + // happen once per prompt (for files found from relative path + // elements, we also check if the working directory has changed + // since the last time the function was loaded/parsed). + // + // FIXME: perhaps this should be done for all loaded functions when + // the prompt is printed or the directory has changed, and then we + // would not check for it when finding symbol definitions. + + static inline bool + load_out_of_date_fcn (const std::string& ff, const std::string& dir_name, + octave_value& function, + const std::string& dispatch_type = "", + const std::string& package_name = "") + { + bool retval = false; + + octave_value ov_fcn + = load_fcn_from_file (ff, dir_name, dispatch_type, + package_name); + + if (ov_fcn.is_defined ()) + { + retval = true; + + function = ov_fcn; + } + else + function = octave_value (); + + return retval; + } + + static bool + out_of_date_check (octave_value& function, + const std::string& dispatch_type = "", + bool check_relative = true) + { + bool retval = false; + + octave_function *fcn = function.function_value (true); + + if (fcn) + { + // FIXME: we need to handle subfunctions properly here. + + if (! (fcn->is_subfunction () || fcn->is_anonymous_function ())) + { + std::string ff = fcn->fcn_file_name (); + + if (! ff.empty ()) + { + sys::time tc = fcn->time_checked (); + + bool relative = check_relative && fcn->is_relative (); + + if (tc <= Vlast_prompt_time + || (relative && tc < Vlast_chdir_time)) + { + bool clear_breakpoints = false; + std::string nm = fcn->name (); + std::string pack = fcn->package_name (); + std::string canonical_nm = fcn->canonical_name (); + + bool is_same_file = false; + + std::string file; + std::string dir_name; + + if (check_relative) + { + int nm_len = nm.length (); + + if (sys::env::absolute_pathname (nm) + && ((nm_len > 4 + && (nm.substr (nm_len-4) == ".oct" + || nm.substr (nm_len-4) == ".mex")) + || (nm_len > 2 + && nm.substr (nm_len-2) == ".m"))) + file = nm; + else + { + // We don't want to make this an absolute name, + // because load_fcn_file looks at the name to + // decide whether it came from a relative lookup. + + if (! dispatch_type.empty ()) + { + load_path& lp + = __get_load_path__ ("out_of_date_check"); + + file = lp.find_method (dispatch_type, nm, + dir_name, pack); + + if (file.empty ()) + { + std::string s_name; + std::string s_pack; + + symbol_table& symtab + = __get_symbol_table__ ("out_of_date_check"); + + const std::list& plist + = symtab.parent_classes (dispatch_type); + + std::list::const_iterator it + = plist.begin (); + + while (it != plist.end ()) + { + split_name_with_package (*it, s_name, + s_pack); + + file = lp.find_method (*it, nm, dir_name, + s_pack); + if (! file.empty ()) + { + pack = s_pack; + break; + } + + it++; + } + } + } + + // Maybe it's an autoload? + if (file.empty ()) + { + tree_evaluator& tw + = __get_evaluator__ ("out_of_data_check"); + + file = tw.lookup_autoload (nm); + } + + if (file.empty ()) + { + load_path& lp + = __get_load_path__ ("out_of_date_check"); + file = lp.find_fcn (nm, dir_name, pack); + } + } + + if (! file.empty ()) + is_same_file = same_file (file, ff); + } + else + { + is_same_file = true; + file = ff; + } + + if (file.empty ()) + { + // Can't see this function from current + // directory, so we should clear it. + + function = octave_value (); + + clear_breakpoints = true; + } + else if (is_same_file) + { + // Same file. If it is out of date, then reload it. + + sys::time ottp = fcn->time_parsed (); + time_t tp = ottp.unix_time (); + + fcn->mark_fcn_file_up_to_date (sys::time ()); + + if (! (Vignore_function_time_stamp == 2 + || (Vignore_function_time_stamp + && fcn->is_system_fcn_file ()))) + { + sys::file_stat fs (ff); + + if (fs) + { + if (fs.is_newer (tp)) + { + retval = load_out_of_date_fcn (ff, dir_name, + function, + dispatch_type, + pack); + + clear_breakpoints = true; + } + } + else + { + function = octave_value (); + + clear_breakpoints = true; + } + } + } + else + { + // Not the same file, so load the new file in + // place of the old. + + retval = load_out_of_date_fcn (file, dir_name, function, + dispatch_type, pack); + + clear_breakpoints = true; + } + + // If the function has been replaced then clear any + // breakpoints associated with it + if (clear_breakpoints) + { + bp_table& bptab + = __get_bp_table__ ("out_of_date_check"); + + bptab.remove_all_breakpoints_in_file (canonical_nm, + true); + } + } + } + } } return retval; } octave_value - fcn_info::fcn_info_rep::xfind (const octave_value_list& args) + fcn_info::fcn_info_rep::xfind (const symbol_scope& search_scope, + const octave_value_list& args) { - symbol_scope curr_scope - = __get_current_scope__ ("fcn_info::fcn_info_rep::xfind"); + octave_user_function *current_fcn + = search_scope ? search_scope.function () : nullptr; + + // Subfunction. I think it only makes sense to check for + // subfunctions if we are currently executing a function defined + // from a .m file. - octave_user_function *current_fcn - = curr_scope ? curr_scope.function () : nullptr; + if (search_scope) + { + octave_value fcn = search_scope.find_subfunction (name); + + if (fcn.is_defined ()) + return fcn; + } // Local function. @@ -384,15 +655,6 @@ // they were defined within class methods and use local functions // (helper functions) we can still use those anonymous functions - if (current_fcn->is_anonymous_function ()) - { - if (fcn_file.empty () - && curr_scope.parent_scope () - && curr_scope.parent_scope ()->function () != nullptr) - fcn_file - = curr_scope.parent_scope ()->function ()->fcn_file_name(); - } - if (! fcn_file.empty ()) { auto r = local_functions.find (fcn_file); @@ -537,9 +799,13 @@ // so class methods and constructors are skipped. octave_value - fcn_info::fcn_info_rep::builtin_find (void) + fcn_info::fcn_info_rep::builtin_find (const symbol_scope& scope) { - octave_value retval = x_builtin_find (); + symbol_scope search_scope + = (scope + ? scope : __get_current_scope__("fcn_info::fcn_info_rep::find")); + + octave_value retval = x_builtin_find (search_scope); if (! retval.is_defined ()) { @@ -551,14 +817,14 @@ lp.update (); - retval = x_builtin_find (); + retval = x_builtin_find (search_scope); } return retval; } octave_value - fcn_info::fcn_info_rep::x_builtin_find (void) + fcn_info::fcn_info_rep::x_builtin_find (const symbol_scope& search_scope) { // Built-in function. if (built_in_function.is_defined ()) @@ -585,11 +851,8 @@ // Private function. - symbol_scope curr_scope - = __get_current_scope__ ("fcn_info::fcn_info_rep::x_builtin_find"); - - octave_user_function *current_fcn = curr_scope ? curr_scope.function () - : nullptr; + octave_user_function *current_fcn + = search_scope ? search_scope.function () : nullptr; if (current_fcn) { @@ -651,9 +914,9 @@ // subfunctions if we are currently executing a function defined // from a .m file. - if (curr_scope) + if (search_scope) { - octave_value val = curr_scope.find_subfunction (name); + octave_value val = search_scope.find_subfunction (name); if (val.is_defined ()) return val; @@ -707,7 +970,10 @@ if (! autoload_function.is_defined ()) { - std::string file_name = lookup_autoload (name); + tree_evaluator& tw + = __get_evaluator__ ("fcn_info::fcn_info_rep::x_builtin_find"); + + std::string file_name = tw.lookup_autoload (name); if (! file_name.empty ()) { @@ -835,3 +1101,82 @@ return octave_value (info_map); } } + +DEFUN (ignore_function_time_stamp, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{val} =} ignore_function_time_stamp () +@deftypefnx {} {@var{old_val} =} ignore_function_time_stamp (@var{new_val}) +Query or set the internal variable that controls whether Octave checks +the time stamp on files each time it looks up functions defined in +function files. + +If the internal variable is set to @qcode{"system"}, Octave will not +automatically recompile function files in subdirectories of +@file{@var{octave-home}/lib/@var{version}} if they have changed since they were last compiled, but will recompile other function files in the search path if they change. + +If set to @qcode{"all"}, Octave will not recompile any function files +unless their definitions are removed with @code{clear}. + +If set to @qcode{"none"}, Octave will always check time stamps on files to +determine whether functions defined in function files need to recompiled. +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin > 1) + print_usage (); + + octave_value retval; + + if (nargout > 0 || nargin == 0) + { + switch (Vignore_function_time_stamp) + { + case 1: + retval = "system"; + break; + + case 2: + retval = "all"; + break; + + default: + retval = "none"; + break; + } + } + + if (nargin == 1) + { + std::string sval = args(0).xstring_value ("ignore_function_time_stamp: first argument must be a string"); + + if (sval == "all") + Vignore_function_time_stamp = 2; + else if (sval == "system") + Vignore_function_time_stamp = 1; + else if (sval == "none") + Vignore_function_time_stamp = 0; + else + error (R"(ignore_function_time_stamp: argument must be one of "all", "system", or "none")"); + } + + return retval; +} + +/* +%!shared old_state +%! old_state = ignore_function_time_stamp (); +%!test +%! state = ignore_function_time_stamp ("all"); +%! assert (state, old_state); +%! assert (ignore_function_time_stamp (), "all"); +%! state = ignore_function_time_stamp ("system"); +%! assert (state, "all"); +%! assert (ignore_function_time_stamp (), "system"); +%! ignore_function_time_stamp (old_state); + +## Test input validation +%!error (ignore_function_time_stamp ("all", "all")) +%!error (ignore_function_time_stamp ("UNKNOWN_VALUE")) +%!error (ignore_function_time_stamp (42)) +*/ diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/fcn-info.h --- a/libinterp/corefcn/fcn-info.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/fcn-info.h Fri Jul 12 12:14:43 2019 -0400 @@ -33,6 +33,7 @@ #include "ov.h" #include "ovl.h" +#include "symscope.h" namespace octave { @@ -81,9 +82,10 @@ octave_value load_class_method (const std::string& dispatch_type); - octave_value find (const octave_value_list& args); + octave_value find (const symbol_scope& search_scope, + const octave_value_list& args); - octave_value builtin_find (void); + octave_value builtin_find (const symbol_scope& search_scope); octave_value find_method (const std::string& dispatch_type); @@ -98,9 +100,10 @@ return function_on_path.is_defined (); } - octave_value find_function (const octave_value_list& args) + octave_value find_function (const symbol_scope& search_scope, + const octave_value_list& args) { - return find (args); + return find (search_scope, args); } void install_cmdline_function (const octave_value& f) @@ -221,9 +224,10 @@ private: - octave_value xfind (const octave_value_list& args); + octave_value xfind (const symbol_scope& search_scope, + const octave_value_list& args); - octave_value x_builtin_find (void); + octave_value x_builtin_find (const symbol_scope& search_scope); }; public: @@ -237,14 +241,16 @@ ~fcn_info (void) = default; - octave_value find (const octave_value_list& args = octave_value_list ()) + octave_value find (const symbol_scope& search_scope, + const octave_value_list& args = octave_value_list ()) { - return m_rep->find (args); + return m_rep->find (search_scope, args); } - octave_value builtin_find (void) + octave_value + builtin_find (const symbol_scope& search_scope) { - return m_rep->builtin_find (); + return m_rep->builtin_find (search_scope); } octave_value find_method (const std::string& dispatch_type) const @@ -267,6 +273,7 @@ return m_rep->find_autoload (); } + // FIXME: find_function_on_path might be a better name? octave_value find_user_function (void) { return m_rep->find_user_function (); @@ -277,10 +284,11 @@ return m_rep->is_user_function_defined (); } - octave_value find_function (const octave_value_list& args - = octave_value_list ()) + octave_value + find_function (const symbol_scope& search_scope, + const octave_value_list& args = octave_value_list ()) { - return m_rep->find_function (args); + return m_rep->find_function (search_scope, args); } void install_cmdline_function (const octave_value& f) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/file-io.cc --- a/libinterp/corefcn/file-io.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/file-io.cc Fri Jul 12 12:14:43 2019 -0400 @@ -52,6 +52,7 @@ #include "file-ops.h" #include "file-stat.h" +#include "iconv-wrappers.h" #include "lo-ieee.h" #include "lo-sysdep.h" #include "mkostemp-wrapper.h" @@ -296,7 +297,7 @@ @seealso{fgets, fscanf, fread, fopen} @end deftypefn */) { - static std::string who = "fgetl"; + static const std::string who = "fgetl"; int nargin = args.length (); @@ -338,7 +339,7 @@ @seealso{fputs, fgetl, fscanf, fread, fopen} @end deftypefn */) { - static std::string who = "fgets"; + static const std::string who = "fgets"; int nargin = args.length (); @@ -380,7 +381,7 @@ @seealso{fgetl, fgets, fscanf, fopen} @end deftypefn */) { - static std::string who = "fskipl"; + static const std::string who = "fskipl"; int nargin = args.length (); @@ -405,12 +406,29 @@ static octave::stream do_stream_open (const std::string& name, const std::string& mode_arg, - const std::string& arch, int& fid) + const std::string& arch, std::string encoding, int& fid) { octave::stream retval; fid = -1; + // Valid names for encodings consist of ASCII characters only. + std::transform (encoding.begin (), encoding.end (), encoding.begin (), + ::tolower); + if (encoding.compare ("utf-8")) + { + // check if encoding is valid + void *codec = octave_iconv_open_wrapper (encoding.c_str (), "utf-8"); + if (codec == reinterpret_cast (-1)) + { + if (errno == EINVAL) + error ("fopen: conversion from codepage '%s' not supported", + encoding.c_str ()); + } + else + octave_iconv_close_wrapper (codec); + } + std::string mode = mode_arg; bool use_zlib = false; normalize_fopen_mode (mode, use_zlib); @@ -440,8 +458,8 @@ gzFile gzf = ::gzdopen (fd, mode.c_str ()); - retval = octave_zstdiostream::create (fname, gzf, fd, - md, flt_fmt); + retval = octave_zstdiostream::create (fname, gzf, fd, md, + flt_fmt, encoding); } else retval.error (std::strerror (errno)); @@ -451,8 +469,8 @@ { FILE *fptr = octave::sys::fopen (fname.c_str (), mode.c_str ()); - retval = octave_stdiostream::create (fname, fptr, md, - flt_fmt); + retval = octave_stdiostream::create (fname, fptr, md, flt_fmt, + encoding); if (! fptr) retval.error (std::strerror (errno)); @@ -465,7 +483,8 @@ static octave::stream do_stream_open (const octave_value& tc_name, const octave_value& tc_mode, - const octave_value& tc_arch, const char *fcn, int& fid) + const octave_value& tc_arch, const octave_value& tc_encoding, + const char *fcn, int& fid) { octave::stream retval; @@ -474,8 +493,9 @@ std::string name = tc_name.xstring_value ("%s: filename must be a string", fcn); std::string mode = tc_mode.xstring_value ("%s: file mode must be a string", fcn); std::string arch = tc_arch.xstring_value ("%s: architecture type must be a string", fcn); - - retval = do_stream_open (name, mode, arch, fid); + std::string encoding = tc_encoding.xstring_value ("%s: ENCODING must be a string", fcn); + + retval = do_stream_open (name, mode, arch, encoding, fid); return retval; } @@ -485,19 +505,22 @@ @deftypefn {} {@var{fid} =} fopen (@var{name}) @deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode}) @deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode}, @var{arch}) +@deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode}, @var{arch}, @var{encoding}) @deftypefnx {} {[@var{fid}, @var{msg}] =} fopen (@dots{}) @deftypefnx {} {@var{fid_list} =} fopen ("all") -@deftypefnx {} {[@var{file}, @var{mode}, @var{arch}] =} fopen (@var{fid}) +@deftypefnx {} {[@var{file}, @var{mode}, @var{arch}, @var{encoding}] =} fopen (@var{fid}) Open a file for low-level I/O or query open files and file descriptors. The first form of the @code{fopen} function opens the named file with -the specified mode (read-write, read-only, etc.@:) and architecture -interpretation (IEEE big endian, IEEE little endian, etc.), and returns -an integer value that may be used to refer to the file later. If an -error occurs, @var{fid} is set to @minus{}1 and @var{msg} contains the +the specified mode (read-write, read-only, etc.@:), architecture +interpretation (IEEE big endian, IEEE little endian, etc.) and file encoding, +and returns an integer value that may be used to refer to the file later. If +an error occurs, @var{fid} is set to @minus{}1 and @var{msg} contains the corresponding system error message. The @var{mode} is a one or two character string that specifies whether the file is to be opened for -reading, writing, or both. +reading, writing, or both. The @var{encoding} is a character string with a +valid code page identifier. This code page is used when strings are read from +or written to the file. The second form of the @code{fopen} function returns a vector of file ids corresponding to all the currently open files, excluding the @@ -571,10 +594,6 @@ IEEE little endian format. @end table -@noindent -However, conversions are currently only supported for @samp{native}, -@samp{ieee-be}, and @samp{ieee-le} formats. - When opening a new file that does not yet exist, permissions will be set to @code{0666 - @var{umask}}. @@ -590,7 +609,7 @@ { int nargin = args.length (); - if (nargin < 1 || nargin > 3) + if (nargin < 1 || nargin > 4) print_usage (); octave_value_list retval = ovl (-1.0); @@ -612,21 +631,22 @@ { string_vector tmp = streams.get_info (args(0)); - retval = ovl (tmp(0), tmp(1), tmp(2)); + retval = ovl (tmp(0), tmp(1), tmp(2), tmp(3)); return retval; } } - octave_value mode = (nargin == 2 || nargin == 3) - ? args(1) : octave_value ("r"); - - octave_value arch = (nargin == 3) - ? args(2) : octave_value ("native"); + octave_value mode = (nargin > 1) ? args(1) : octave_value ("r"); + + octave_value arch = (nargin > 2) ? args(2) : octave_value ("native"); + + octave_value encoding = (nargin > 3) ? args(3) : octave_value ("utf-8"); int fid = -1; - octave::stream os = do_stream_open (args(0), mode, arch, "fopen", fid); + octave::stream os = do_stream_open (args(0), mode, arch, encoding, "fopen", + fid); if (os) retval = ovl (streams.insert (os), ""); @@ -641,11 +661,12 @@ } /* -## FIXME: Only have tests for query mode. Need others for regular fopen call. +## Further tests are in io.tst %!test # Uses hardcoded value of 1 for stdout -%! [name, mode, arch] = fopen (1); +%! [name, mode, arch, encoding] = fopen (1); %! assert (name, "stdout"); %! assert (mode, "w"); +%! assert (encoding, "utf-8"); %!test # Query of non-existent stream returns all "" %! [name, mode, arch] = fopen (-1); @@ -831,7 +852,7 @@ @seealso{fputs, fdisp, fwrite, fscanf, printf, sprintf, fopen} @end deftypefn */) { - static std::string who = "fprintf"; + static const std::string who = "fprintf"; return printf_internal (interp, who, args, nargout); } @@ -854,7 +875,7 @@ @seealso{fprintf, sprintf, scanf} @end deftypefn */) { - static std::string who = "printf"; + static const std::string who = "printf"; octave_value_list tmp_args = args; @@ -890,7 +911,7 @@ @seealso{fdisp, fprintf, fwrite, fopen} @end deftypefn */) { - static std::string who = "fputs"; + static const std::string who = "fputs"; return puts_internal (interp, who, args); } @@ -908,7 +929,7 @@ @seealso{fputs, disp} @end deftypefn */) { - static std::string who = "puts"; + static const std::string who = "puts"; octave_value_list tmp_args = args; @@ -931,7 +952,7 @@ @seealso{printf, fprintf, sscanf} @end deftypefn */) { - static std::string who = "sprintf"; + static const std::string who = "sprintf"; int nargin = args.length (); @@ -1067,7 +1088,7 @@ @seealso{fgets, fgetl, fread, scanf, sscanf, fopen} @end deftypefn */) { - static std::string who = "fscanf"; + static const std::string who = "fscanf"; return scanf_internal (interp, who, args); } @@ -1100,7 +1121,7 @@ @seealso{fscanf, scanf, sprintf} @end deftypefn */) { - static std::string who = "sscanf"; + static const std::string who = "sscanf"; int nargin = args.length (); @@ -1145,6 +1166,15 @@ return retval; } +/* +%!test <*56396> +%! [val, count, errmsg, nextpos] = sscanf ('1234a6', '%2d', 3); +%! assert (val, [12; 34]); +%! assert (count, 2); +%! assert (errmsg, "sscanf: format failed to match"); +%! assert (nextpos, 5); +*/ + DEFMETHOD (scanf, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {[@var{val}, @var{count}, @var{errmsg}] =} scanf (@var{template}, @var{size}) @@ -1155,7 +1185,7 @@ @seealso{fscanf, sscanf, printf} @end deftypefn */) { - static std::string who = "scanf"; + static const std::string who = "scanf"; octave_value_list tmp_args = args; @@ -1500,7 +1530,7 @@ @seealso{dlmread, fscanf, load, strread, textread} @end deftypefn */) { - static std::string who = "textscan"; + static const std::string who = "textscan"; return textscan_internal (interp, who, args); } @@ -2214,43 +2244,56 @@ %! ret = textscan (str, "%s", "delimiter", "\t"); %! assert (ret, { {''; ''; 'a'; 'b'; 'c'} }); -%!test <52479> +%!test <*52479> %! str = "\t\ta\tb\tc\n"; %! ret = textscan (str, "%s", "delimiter", {"\t"}); %! assert (ret, { {''; ''; 'a'; 'b'; 'c'} }); -%!test <52550> +%!test <*52550> %! str = ",,1,2,3\n"; %! obs = textscan (str, "%d", "delimiter", ","); %! assert (obs, { [0; 0; 1; 2; 3] }); %! obs = textscan (str, "%d", "delimiter", {","}); %! assert (obs, { [0; 0; 1; 2; 3] }); -%!test <52550> +%!test <*52550> %! str = " , ,1,2,3\n"; %! obs = textscan (str, "%d", "delimiter", ","); %! assert (obs, { [0; 0; 1; 2; 3] }); %! textscan (str, "%d", "delimiter", {","}); %! assert (obs, { [0; 0; 1; 2; 3] }); -%!test <52550> +%!test <*52550> %! str = " 0 , 5+6j , -INF+INFj ,NaN,3\n"; %! obs = textscan (str, "%f", "delimiter", ","); %! assert (obs, { [0; 5+6i; complex(-Inf,Inf); NaN; 3] }); %! obs = textscan (str, "%f", "delimiter", {","}); %! assert (obs, { [0; 5+6i; complex(-Inf,Inf); NaN; 3] }); -%!test <52550> +%!test <*52550> %! str = " 0;,;,1;,2;,3\n"; %! assert (textscan (str, "%f", "delimiter", {";,"}), { [0; NaN; 1; 2; 3] }); -%!test <52550> +%!test <*52550> %! str = " 0 ;1 , $ 2 ;3\n"; %! obs = textscan (str, "%f", "delimiter", ",;$"); %! assert (obs, { [0; 1; NaN; 2; 3] }); %! obs = textscan (str, "%f", "delimiter", {",",";","$"}); %! assert (obs, { [0; 1; NaN; 2; 3] }); +## file stream with encoding +%!test +%! f = tempname (); +%! fid = fopen (f, "w+", "n", "iso-8859-1"); +%! unwind_protect +%! fprintf (fid, "abc,äöü\n"); +%! fseek (fid, 0, "bof"); +%! obs = textscan (fid, "%s", "delimiter", ","); +%! fclose (fid); +%! assert (obs, { {"abc"; "äöü"} }); +%! unwind_protect_cleanup +%! unlink (f); +%! end_unwind_protect */ // These tests have end-comment sequences, so can't just be in a comment diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/ft-text-renderer.cc --- a/libinterp/corefcn/ft-text-renderer.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/ft-text-renderer.cc Fri Jul 12 12:14:43 2019 -0400 @@ -149,14 +149,9 @@ if (! instance) { instance = new ft_manager (); - - if (instance) - singleton_cleanup_list::add (cleanup_instance); + singleton_cleanup_list::add (cleanup_instance); } - if (! instance) - error ("unable to create ft_manager!"); - return retval; } @@ -245,10 +240,6 @@ if (weight == "bold") fc_weight = FC_WEIGHT_BOLD; - else if (weight == "light") - fc_weight = FC_WEIGHT_LIGHT; - else if (weight == "demi") - fc_weight = FC_WEIGHT_DEMIBOLD; else fc_weight = FC_WEIGHT_NORMAL; @@ -378,7 +369,7 @@ xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX), color (dim_vector (1, 3), 0), m_do_strlist (false), m_strlist (), line_xoffset (0), m_ymin (0), m_ymax (0), m_deltax (0), - m_max_fontsize (0) + m_max_fontsize (0), m_antialias (true) { } // No copying! @@ -422,6 +413,8 @@ Matrix get_extent (const std::string& txt, double rotation, const caseless_str& interpreter); + void set_anti_aliasing (bool val) { m_antialias = val; }; + void set_font (const std::string& name, const std::string& weight, const std::string& angle, double size); @@ -547,6 +540,9 @@ // Used for computing the distance between lines. double m_max_fontsize; + // Anti-aliasing. + bool m_antialias; + }; void @@ -712,6 +708,16 @@ break; } } + bool is_opaque (const FT_GlyphSlot &glyph, const int x, const int y) + { + // Borrowed from https://stackoverflow.com/questions/14800827/ + // indexing-pixels-in-a-monochrome-freetype-glyph-buffer + int pitch = std::abs (glyph->bitmap.pitch); + unsigned char *row = &glyph->bitmap.buffer[pitch * y]; + char cvalue = row[x >> 3]; + + return ((cvalue & (128 >> (x & 7))) != 0); + } FT_UInt ft_text_renderer::process_character (FT_ULong code, FT_UInt previous) @@ -754,7 +760,9 @@ switch (mode) { case MODE_RENDER: - if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) + if (FT_Render_Glyph (face->glyph, (m_antialias + ? FT_RENDER_MODE_NORMAL + : FT_RENDER_MODE_MONO))) { glyph_index = 0; warn_glyph_render (code); @@ -786,7 +794,11 @@ for (int r = 0; static_cast (r) < bitmap.rows; r++) for (int c = 0; static_cast (c) < bitmap.width; c++) { - unsigned char pix = bitmap.buffer[r*bitmap.width+c]; + unsigned char pix + = (m_antialias + ? bitmap.buffer[r*bitmap.width+c] + : (is_opaque (face->glyph, c, r) ? 255 : 0)); + if (x0+c < 0 || x0+c >= pixels.dim2 () || y0-r < 0 || y0-r >= pixels.dim3 ()) { @@ -866,7 +878,7 @@ m_strlist = std::list (); - octave::unwind_protect frame; + unwind_protect frame; frame.protect_var (m_do_strlist); frame.protect_var (m_strlist); m_do_strlist = true; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/gl-render.cc --- a/libinterp/corefcn/gl-render.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/gl-render.cc Fri Jul 12 12:14:43 2019 -0400 @@ -191,6 +191,25 @@ // Should we check for dimensions larger than intmax? int h, w, tw, th; h = dv(0), w = dv(1); + + // Return early if the image data are larger than the texture + // can hold + int max_size; + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size); + static bool warned = false; + if (h > max_size || w > max_size) + { + if (! warned) + { + warning ("opengl_texture::create: the opengl library in use " + "doesn't support images with either dimension larger " + "than %d. Not rendering.", max_size); + warned = true; + } + + return opengl_texture (glfcns); + } + GLuint id; bool ok = true; @@ -204,7 +223,7 @@ { const NDArray xdata = data.array_value (); - OCTAVE_LOCAL_BUFFER (float, a, (3*tw*th)); + OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); for (int i = 0; i < h; i++) { @@ -216,13 +235,53 @@ } } - glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a); + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, + GL_FLOAT, a); + } + + else if (data.is_single_type ()) + { + const FloatNDArray xdata = data.float_array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + { + a[idx] = xdata(i,j,0); + a[idx+1] = xdata(i,j,1); + a[idx+2] = xdata(i,j,2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, + GL_FLOAT, a); + } + else if (data.is_uint16_type ()) + { + const uint16NDArray xdata = data.uint16_array_value (); + + OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th)); + + for (int i = 0; i < h; i++) + { + for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) + { + a[idx] = xdata(i,j,0); + a[idx+1] = xdata(i,j,1); + a[idx+2] = xdata(i,j,2); + } + } + + glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, + GL_RGB, GL_UNSIGNED_SHORT, a); } else if (data.is_uint8_type ()) { const uint8NDArray xdata = data.uint8_array_value (); - OCTAVE_LOCAL_BUFFER (octave_uint8, a, (3*tw*th)); + OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th)); for (int i = 0; i < h; i++) { @@ -240,7 +299,7 @@ else { ok = false; - warning ("opengl_texture::create: invalid texture data type (double or uint8 required)"); + warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16"); } if (ok) @@ -619,11 +678,11 @@ #endif opengl_renderer::opengl_renderer (opengl_functions& glfcns) - : m_glfcns (glfcns), toolkit (), xform (), xmin (), xmax (), ymin (), - ymax (), zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), + : m_glfcns (glfcns), xmin (), xmax (), ymin (), ymax (), zmin (), zmax (), + m_devpixratio (1.), xform (), toolkit (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (), camera_pos (), camera_dir (), view_vector (), interpreter ("none"), txt_renderer (), m_current_light (0), - m_max_lights (0), selecting (false), m_devpixratio (1.) + m_max_lights (0), selecting (false) { // This constructor will fail if we don't have OpenGL or if the data // types we assumed in our public interface aren't compatible with the @@ -710,7 +769,6 @@ opengl_renderer::draw_figure (const figure::properties& props) { // Initialize OpenGL context - init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); #if defined (HAVE_OPENGL) @@ -1349,59 +1407,71 @@ std::swap (zpTick, zpTickN); // X box - set_color (props.get_xcolor_rgb ()); - - if (! isXOrigin || props.is_box() || ! is2D) - { - m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); - m_glfcns.glVertex3d (xPlane, ypTick, zpTick); - } - - if (props.is_box ()) + Matrix color = props.get_xcolor_rgb (); + + if (! color.isempty ()) { - m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); - m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); - if (! is2D) + set_color (color); + + if (! isXOrigin || props.is_box() || ! is2D) + { + m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); + m_glfcns.glVertex3d (xPlane, ypTick, zpTick); + } + + if (props.is_box ()) { - m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); - m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); - if (boxFull) + m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); + m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); + if (! is2D) { - m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); - m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); + m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); + m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); + if (boxFull) + { + m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); + m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); + } } } } // Y box - set_color (props.get_ycolor_rgb ()); - if (! isYOrigin || props.is_box() || ! is2D) - { - m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); - m_glfcns.glVertex3d (xpTick, yPlane, zpTick); - } - - if (props.is_box () && ! plotyy) + color = props.get_ycolor_rgb (); + + if (! color.isempty ()) { - m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); - m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); - - if (! is2D) + set_color (color); + if (! isYOrigin || props.is_box() || ! is2D) + { + m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); + m_glfcns.glVertex3d (xpTick, yPlane, zpTick); + } + + if (props.is_box () && ! plotyy) { - m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); - m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); - if (boxFull) + m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); + m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); + + if (! is2D) { - m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); - m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); + m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); + m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); + if (boxFull) + { + m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); + m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); + } } } } // Z box - if (! is2D) + color = props.get_zcolor_rgb (); + + if (! color.isempty () && ! is2D) { - set_color (props.get_zcolor_rgb ()); + set_color (color); if (xySym) { @@ -1487,8 +1557,14 @@ double zpTick = props.get_zpTick (); double zpTickN = props.get_zpTickN (); - // X grid - + // X ticks and grid properties + Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); + Matrix xmticks = xform.xscale (props.get_xminortickvalues ().matrix_value ()); + bool do_xminortick = props.is_xminortick () && ! xticks.isempty (); + string_vector xticklabels = props.get_xticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || math::isinf (fy); double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); @@ -1498,27 +1574,29 @@ double minorgridalpha = props.get_minorgridalpha (); bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); bool do_xminorgrid = (props.is_xminorgrid () - && (minorgridstyle != "none")); - bool do_xminortick = props.is_xminortick (); + && (minorgridstyle != "none") + && ! xticks.isempty ()); bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D () && ! props.yscale_is ("log"); bool is_origin_low = is_origin && (y_min + y_max) < 0; - Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); - Matrix xmticks = xform.xscale (props.get_xminortickvalues ().matrix_value ()); - string_vector xticklabels = props.get_xticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || math::isinf (fy); bool mirror = props.is_box () && xstate != AXE_ANY_DIR; - if (props.xcolormode_is ("manual")) - { - // use axis color for (minor)gridcolor - if (props.gridcolormode_is ("auto")) - gridcolor = props.get_xcolor_rgb (); - if (props.minorgridcolormode_is ("auto")) - minorgridcolor = props.get_xcolor_rgb (); - } + // X grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) + gridcolor = props.get_xcolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) + minorgridcolor = props.get_xcolor_rgb (); + + if (gridcolor.isempty ()) + do_xgrid = false; + + if (minorgridcolor.isempty ()) + do_xminorgrid = false; // set styles when drawing only minor grid if (do_xminorgrid && ! do_xgrid) @@ -1545,6 +1623,10 @@ yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.xcolor_is ("none")) + return; + set_color (props.get_xcolor_rgb ()); // axis line @@ -1669,8 +1751,14 @@ double zpTick = props.get_zpTick (); double zpTickN = props.get_zpTickN (); - // Y grid - + // Y ticks and grid properties + Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); + Matrix ymticks = xform.yscale (props.get_yminortickvalues ().matrix_value ()); + bool do_yminortick = props.is_yminortick () && ! yticks.isempty (); + string_vector yticklabels = props.get_yticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; + bool tick_along_z = nearhoriz || math::isinf (fx); double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); @@ -1680,28 +1768,30 @@ double minorgridalpha = props.get_minorgridalpha (); bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); bool do_yminorgrid = (props.is_yminorgrid () - && (minorgridstyle != "none")); - bool do_yminortick = props.is_yminortick (); + && (minorgridstyle != "none") + && ! yticks.isempty ()); bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D () && ! props.xscale_is ("log"); bool is_origin_low = is_origin && (x_min + x_max) < 0; - Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); - Matrix ymticks = xform.yscale (props.get_yminortickvalues ().matrix_value ()); - string_vector yticklabels = props.get_yticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; - bool tick_along_z = nearhoriz || math::isinf (fx); bool mirror = props.is_box () && ystate != AXE_ANY_DIR && (! props.has_property ("__plotyy_axes__")); - if (props.ycolormode_is ("manual")) - { - // use axis color for (minor)gridcolor - if (props.gridcolormode_is ("auto")) - gridcolor = props.get_ycolor_rgb (); - if (props.minorgridcolormode_is ("auto")) - minorgridcolor = props.get_ycolor_rgb (); - } + // Y grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) + gridcolor = props.get_ycolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) + minorgridcolor = props.get_ycolor_rgb (); + + if (gridcolor.isempty ()) + do_ygrid = false; + + if (minorgridcolor.isempty ()) + do_yminorgrid = false; // set styles when drawing only minor grid if (do_yminorgrid && ! do_ygrid) @@ -1728,6 +1818,10 @@ xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.ycolor_is ("none")) + return; + set_color (props.get_ycolor_rgb ()); // axis line @@ -1841,8 +1935,13 @@ double z_min = props.get_z_min (); double z_max = props.get_z_max (); - // Z Grid - + // Z ticks and grid properties + Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); + Matrix zmticks = xform.zscale (props.get_zminortickvalues ().matrix_value ()); + bool do_zminortick = props.is_zminortick () && ! zticks.isempty (); + string_vector zticklabels = props.get_zticklabel ().string_vector_value (); + int wmax = 0; + int hmax = 0; double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); @@ -1852,23 +1951,26 @@ double minorgridalpha = props.get_minorgridalpha (); bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); bool do_zminorgrid = (props.is_zminorgrid () - && (minorgridstyle != "none")); - bool do_zminortick = props.is_zminortick (); - Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); - Matrix zmticks = xform.zscale (props.get_zminortickvalues ().matrix_value ()); - string_vector zticklabels = props.get_zticklabel ().string_vector_value (); - int wmax = 0; - int hmax = 0; + && (minorgridstyle != "none") + && ! zticks.isempty ()); bool mirror = props.is_box () && zstate != AXE_ANY_DIR; - if (props.zcolormode_is ("manual")) - { - // use axis color for (minor)gridcolor - if (props.gridcolormode_is ("auto")) - gridcolor = props.get_zcolor_rgb (); - if (props.minorgridcolormode_is ("auto")) - minorgridcolor = props.get_zcolor_rgb (); - } + // Z grid + + // possibly use axis color for gridcolor & minorgridcolor + if (props.gridcolormode_is ("auto")) + if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) + gridcolor = props.get_zcolor_rgb (); + + if (props.minorgridcolormode_is ("auto")) + if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) + minorgridcolor = props.get_zcolor_rgb (); + + if (gridcolor.isempty ()) + do_zgrid = false; + + if (minorgridcolor.isempty ()) + do_zminorgrid = false; // set styles when drawing only minor grid if (do_zminorgrid && ! do_zgrid) @@ -1893,6 +1995,10 @@ zticks, z_min, z_max, xPlane, xPlaneN, yPlane, yPlaneN, 2, true); + // Skip drawing axis, ticks, and ticklabels when color is "none" + if (props.zcolor_is ("none")) + return; + set_color (props.get_zcolor_rgb ()); // minor tick marks @@ -3631,7 +3737,7 @@ { #if defined (HAVE_OPENGL) - if (props.get_string ().isempty ()) + if (props.get_string ().isempty () || props.color_is ("none")) return; Matrix pos = xform.scale (props.get_data_position ()); @@ -3786,256 +3892,55 @@ dim_vector dv (cdata.dims ()); int h = dv(0); int w = dv(1); + double x0, x1, y0, y1; Matrix x = props.get_xdata ().matrix_value (); + double dx = 1.0; + if (w > 1) + dx = (x(1) - x(0)) / (w - 1); + + x0 = x(0)-dx/2; + x1 = x(1)+dx/2; + Matrix y = props.get_ydata ().matrix_value (); - - // Someone wants us to draw an empty image? No way. - if (x.isempty () || y.isempty ()) - return; - - // Sort x/ydata and mark flipped dimensions - bool xflip = false; - if (x(0) > x(1)) - { - std::swap (x(0), x(1)); - xflip = true; - } - else if (w > 1 && x(1) == x(0)) - x(1) = x(1) + (w-1); - - bool yflip = false; - if (y(0) > y(1)) - { - std::swap (y(0), y(1)); - yflip = true; - } - else if (h > 1 && y(1) == y(0)) - y(1) = y(1) + (h-1); - - const ColumnVector p0 = xform.transform (x(0), y(0), 0); - const ColumnVector p1 = xform.transform (x(1), y(1), 0); - - if (math::isnan (p0(0)) || math::isnan (p0(1)) - || math::isnan (p1(0)) || math::isnan (p1(1))) - { - warning ("opengl_renderer: image X,Y data too large to draw"); - return; - } - - // image pixel size in screen pixel units - float pix_dx, pix_dy; - // image pixel size in normalized units - float nor_dx, nor_dy; - - if (w > 1) - { - pix_dx = (p1(0) - p0(0)) / (w-1); - nor_dx = (x(1) - x(0)) / (w-1); - } - else - { - const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0); - pix_dx = p1w(0) - p0(0); - nor_dx = 1; - } - + double dy = 1.0; if (h > 1) - { - pix_dy = (p1(1) - p0(1)) / (h-1); - nor_dy = (y(1) - y(0)) / (h-1); - } - else - { - const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0); - pix_dy = p1h(1) - p0(1); - nor_dy = 1; - } - - // OpenGL won't draw any of the image if its origin is outside the - // viewport/clipping plane so we must do the clipping ourselves. - - int j0, j1, jj, i0, i1, ii; - j0 = 0, j1 = w; - i0 = 0, i1 = h; - - float im_xmin = x(0) - nor_dx/2; - float im_xmax = x(1) + nor_dx/2; - float im_ymin = y(0) - nor_dy/2; - float im_ymax = y(1) + nor_dy/2; - - // Clip to axes or viewport - bool do_clip = props.is_clipping (); - Matrix vp = get_viewport_scaled (); - - ColumnVector vp_lim_min = - xform.untransform (std::numeric_limits ::epsilon (), - std::numeric_limits ::epsilon ()); - ColumnVector vp_lim_max = xform.untransform (vp(2), vp(3)); - - if (vp_lim_min(0) > vp_lim_max(0)) - std::swap (vp_lim_min(0), vp_lim_max(0)); - - if (vp_lim_min(1) > vp_lim_max(1)) - std::swap (vp_lim_min(1), vp_lim_max(1)); - - float clip_xmin = - (do_clip ? (vp_lim_min(0) > xmin ? vp_lim_min(0) : xmin) : vp_lim_min(0)); - float clip_ymin = - (do_clip ? (vp_lim_min(1) > ymin ? vp_lim_min(1) : ymin) : vp_lim_min(1)); - - float clip_xmax = - (do_clip ? (vp_lim_max(0) < xmax ? vp_lim_max(0) : xmax) : vp_lim_max(0)); - float clip_ymax = - (do_clip ? (vp_lim_max(1) < ymax ? vp_lim_max(1) : ymax) : vp_lim_max(1)); - - if (im_xmin < clip_xmin) - j0 += (clip_xmin - im_xmin)/nor_dx + 1; - if (im_xmax > clip_xmax) - j1 -= (im_xmax - clip_xmax)/nor_dx; - - if (im_ymin < clip_ymin) - i0 += (clip_ymin - im_ymin)/nor_dy + 1; - if (im_ymax > clip_ymax) - i1 -= (im_ymax - clip_ymax)/nor_dy; - - if (i0 >= i1 || j0 >= j1) - return; - - m_glfcns.glPixelZoom (m_devpixratio * pix_dx, - - m_devpixratio * pix_dy); - m_glfcns.glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0); - - // by default this is 4 - m_glfcns.glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + dy = (y(1) - y(0)) / (h - 1); + + y0 = y(0)-dy/2; + y1 = y(1)+dy/2; // Expect RGB data if (dv.ndims () == 3 && dv(2) == 3) { - if (cdata.is_double_type ()) + opengl_texture tex = opengl_texture::create (m_glfcns, cdata); + if (tex.is_valid ()) { - const NDArray xcdata = cdata.array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - if (! yflip) - ii = i; - else - ii = h - i - 1; - - if (! xflip) - jj = j; - else - jj = w - j - 1; - - a[idx] = xcdata(ii,jj,0); - a[idx+1] = xcdata(ii,jj,1); - a[idx+2] = xcdata(ii,jj,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else if (cdata.is_single_type ()) - { - const FloatNDArray xcdata = cdata.float_array_value (); - - OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - if (! yflip) - ii = i; - else - ii = h - i - 1; - - if (! xflip) - jj = j; - else - jj = w - j - 1; - - a[idx] = xcdata(ii,jj,0); - a[idx+1] = xcdata(ii,jj,1); - a[idx+2] = xcdata(ii,jj,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - + m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0); + + m_glfcns.glEnable (GL_TEXTURE_2D); + + m_glfcns.glBegin (GL_QUADS); + + tex.tex_coord (0.0, 0.0); + m_glfcns.glVertex3d (x0, y0, 0.0); + + tex.tex_coord (1.0, 0.0); + m_glfcns.glVertex3d (x1, y0, 0.0); + + tex.tex_coord (1.0, 1.0); + m_glfcns.glVertex3d (x1, y1, 0.0); + + tex.tex_coord (0.0, 1.0); + m_glfcns.glVertex3d (x0, y1, 0.0); + + m_glfcns.glEnd (); + m_glfcns.glDisable (GL_TEXTURE_2D); } - else if (cdata.is_uint8_type ()) - { - const uint8NDArray xcdata = cdata.uint8_array_value (); - - OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - if (! yflip) - ii = i; - else - ii = h - i - 1; - - if (! xflip) - jj = j; - else - jj = w - j - 1; - - a[idx] = xcdata(ii,jj,0); - a[idx+1] = xcdata(ii,jj,1); - a[idx+2] = xcdata(ii,jj,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else if (cdata.is_uint16_type ()) - { - const uint16NDArray xcdata = cdata.uint16_array_value (); - - OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0)); - - for (int i = i0; i < i1; i++) - { - for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) - { - if (! yflip) - ii = i; - else - ii = h - i - 1; - - if (! xflip) - jj = j; - else - jj = w - j - 1; - - a[idx] = xcdata(ii,jj,0); - a[idx+1] = xcdata(ii,jj,1); - a[idx+2] = xcdata(ii,jj,2); - } - } - - draw_pixels (j1-j0, i1-i0, a); - - } - else - warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)"); } else warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); - m_glfcns.glPixelZoom (1, 1); - #else octave_unused_parameter (props); @@ -4098,76 +4003,14 @@ } void - opengl_renderer::draw_pixels (int width, int height, const float *data) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glDrawPixels (width, height, GL_RGB, GL_FLOAT, data); - -#else - - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::draw_pixels (int width, int height, const uint8_t *data) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_BYTE, data); - -#else - - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void - opengl_renderer::draw_pixels (int width, int height, const uint16_t *data) - { -#if defined (HAVE_OPENGL) - - m_glfcns.glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_SHORT, data); - -#else - - octave_unused_parameter (width); - octave_unused_parameter (height); - octave_unused_parameter (data); - - // This shouldn't happen because construction of opengl_renderer - // objects is supposed to be impossible if OpenGL is not available. - - panic_impossible (); - -#endif - } - - void opengl_renderer::set_color (const Matrix& c) { #if defined (HAVE_OPENGL) m_glfcns.glColor3dv (c.data ()); - txt_renderer.set_color (c); + if (! c.isempty ()) + txt_renderer.set_color (c); #else @@ -4184,6 +4027,8 @@ void opengl_renderer::set_font (const base_properties& props) { + bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on"; + txt_renderer.set_anti_aliasing (do_anti_alias); txt_renderer.set_font (props.get ("fontname").string_value (), props.get ("fontweight").string_value (), props.get ("fontangle").string_value (), @@ -4225,8 +4070,15 @@ opengl_renderer::set_linewidth (float w) { #if defined (HAVE_OPENGL) - - m_glfcns.glLineWidth (w * m_devpixratio); + // FIXME: See bug #53056 (measure LineWidth in points). + // pts2pix and m_devpixratio should eventually be combined in to a + // a single conversion factor so that only one multiplication per + // function call is required. + const static double pts2pix = + gh_manager::get_object (0).get ("screenpixelsperinch").double_value () + / 72.0; + + m_glfcns.glLineWidth (w * pts2pix * m_devpixratio); #else @@ -4246,7 +4098,11 @@ { #if defined (HAVE_OPENGL) - int factor = math::round (linewidth * m_devpixratio); + // FIXME: See bug #53056 (measure LineWidth in points). + const static double pts2pix = + gh_manager::get_object (0).get ("screenpixelsperinch").double_value () + / 72.0; + int factor = math::round (linewidth * pts2pix * m_devpixratio); if (factor < 1) factor = 1; @@ -4257,11 +4113,25 @@ if (s == "-") solid = true; else if (s == ":") - pattern = 0x5555; + { + if (factor > 1) + pattern = 0x5555; + else + pattern = 0x1111; + } else if (s == "--") - pattern = 0x0F0F; + if (factor > 1) + pattern = 0x0F0F; + else + pattern = 0x01FF; + else if (s == "-.") - pattern = 0x6F6F; + { + if (factor > 1) + pattern = 0x6F6F; + else + pattern = 0x18FF; + } else pattern = 0x0000; @@ -4571,7 +4441,13 @@ return 0; unsigned int ID = m_glfcns.glGenLists (1); - double sz = size * toolkit.get_screen_resolution () / 72.0; + + // FIXME: See bug #53056 (measure LineWidth in points). + const static double pts2pix = + gh_manager::get_object (0).get ("screenpixelsperinch").double_value () + / 72.0; + + double sz = size * pts2pix; // constants for the * marker const double sqrt2d4 = 0.35355339059327; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/gl-render.h --- a/libinterp/corefcn/gl-render.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/gl-render.h Fri Jul 12 12:14:43 2019 -0400 @@ -52,7 +52,10 @@ virtual void draw (const graphics_object& go, bool toplevel = true); - virtual void draw (const Matrix& hlist, bool toplevel = false) + // The following version of the draw method is not declared virtual + // because no derived class overrides it. + + void draw (const Matrix& hlist, bool toplevel = false) { int len = hlist.numel (); @@ -140,10 +143,6 @@ double x, double y, double z, int halign, int valign, double rotation = 0.0); - virtual void draw_pixels (int w, int h, const float *data); - virtual void draw_pixels (int w, int h, const uint8_t *data); - virtual void draw_pixels (int w, int h, const uint16_t *data); - virtual void render_grid (const double linewidth, const std::string& gridstyle, const Matrix& gridcolor, const double gridalpha, @@ -212,19 +211,22 @@ opengl_functions& m_glfcns; + // axis limits in model scaled coordinate + double xmin, xmax; + double ymin, ymax; + double zmin, zmax; + + // Factor used for translating Octave pixels to actual device pixels + double m_devpixratio; + + // axes transformation data + graphics_xform xform; + private: // The graphics toolkit associated with the figure being rendered. graphics_toolkit toolkit; - // axes transformation data - graphics_xform xform; - - // axis limits in model scaled coordinate - double xmin, xmax; - double ymin, ymax; - double zmin, zmax; - // Z projection limits in windows coordinate double xZ1, xZ2; @@ -246,9 +248,6 @@ // Indicate we are drawing for selection purpose bool selecting; - // Factor used for translating Octave pixels to actual device pixels - double m_devpixratio; - private: class patch_tesselator; }; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/gl2ps-print.cc --- a/libinterp/corefcn/gl2ps-print.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/gl2ps-print.cc Fri Jul 12 12:14:43 2019 -0400 @@ -175,6 +175,7 @@ void draw_text (const text::properties& props); + void draw_image (const image::properties& props); void draw_pixels (int w, int h, const float *data); void draw_pixels (int w, int h, const uint8_t *data); void draw_pixels (int w, int h, const uint16_t *data); @@ -1042,6 +1043,266 @@ } void + gl2ps_renderer::draw_image (const image::properties& props) + { + octave_value cdata = props.get_color_data (); + dim_vector dv (cdata.dims ()); + int h = dv(0); + int w = dv(1); + + Matrix x = props.get_xdata ().matrix_value (); + Matrix y = props.get_ydata ().matrix_value (); + + // Someone wants us to draw an empty image? No way. + if (x.isempty () || y.isempty ()) + return; + + // Sort x/ydata and mark flipped dimensions + bool xflip = false; + if (x(0) > x(1)) + { + std::swap (x(0), x(1)); + xflip = true; + } + else if (w > 1 && x(1) == x(0)) + x(1) = x(1) + (w-1); + + bool yflip = false; + if (y(0) > y(1)) + { + std::swap (y(0), y(1)); + yflip = true; + } + else if (h > 1 && y(1) == y(0)) + y(1) = y(1) + (h-1); + + + const ColumnVector p0 = xform.transform (x(0), y(0), 0); + const ColumnVector p1 = xform.transform (x(1), y(1), 0); + + if (math::isnan (p0(0)) || math::isnan (p0(1)) + || math::isnan (p1(0)) || math::isnan (p1(1))) + { + warning ("opengl_renderer: image X,Y data too large to draw"); + return; + } + + // image pixel size in screen pixel units + float pix_dx, pix_dy; + // image pixel size in normalized units + float nor_dx, nor_dy; + + if (w > 1) + { + pix_dx = (p1(0) - p0(0)) / (w-1); + nor_dx = (x(1) - x(0)) / (w-1); + } + else + { + const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0); + pix_dx = p1w(0) - p0(0); + nor_dx = 1; + } + + if (h > 1) + { + pix_dy = (p1(1) - p0(1)) / (h-1); + nor_dy = (y(1) - y(0)) / (h-1); + } + else + { + const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0); + pix_dy = p1h(1) - p0(1); + nor_dy = 1; + } + + // OpenGL won't draw any of the image if its origin is outside the + // viewport/clipping plane so we must do the clipping ourselves. + + int j0, j1, jj, i0, i1, ii; + j0 = 0, j1 = w; + i0 = 0, i1 = h; + + float im_xmin = x(0) - nor_dx/2; + float im_xmax = x(1) + nor_dx/2; + float im_ymin = y(0) - nor_dy/2; + float im_ymax = y(1) + nor_dy/2; + + // Clip to axes or viewport + bool do_clip = props.is_clipping (); + Matrix vp = get_viewport_scaled (); + + ColumnVector vp_lim_min = + xform.untransform (std::numeric_limits ::epsilon (), + std::numeric_limits ::epsilon ()); + ColumnVector vp_lim_max = xform.untransform (vp(2), vp(3)); + + if (vp_lim_min(0) > vp_lim_max(0)) + std::swap (vp_lim_min(0), vp_lim_max(0)); + + if (vp_lim_min(1) > vp_lim_max(1)) + std::swap (vp_lim_min(1), vp_lim_max(1)); + + float clip_xmin = + (do_clip ? (vp_lim_min(0) > xmin ? vp_lim_min(0) : xmin) : vp_lim_min(0)); + float clip_ymin = + (do_clip ? (vp_lim_min(1) > ymin ? vp_lim_min(1) : ymin) : vp_lim_min(1)); + + float clip_xmax = + (do_clip ? (vp_lim_max(0) < xmax ? vp_lim_max(0) : xmax) : vp_lim_max(0)); + float clip_ymax = + (do_clip ? (vp_lim_max(1) < ymax ? vp_lim_max(1) : ymax) : vp_lim_max(1)); + + if (im_xmin < clip_xmin) + j0 += (clip_xmin - im_xmin)/nor_dx + 1; + if (im_xmax > clip_xmax) + j1 -= (im_xmax - clip_xmax)/nor_dx; + + if (im_ymin < clip_ymin) + i0 += (clip_ymin - im_ymin)/nor_dy + 1; + if (im_ymax > clip_ymax) + i1 -= (im_ymax - clip_ymax)/nor_dy; + + if (i0 >= i1 || j0 >= j1) + return; + + float zoom_x; + m_glfcns.glGetFloatv (GL_ZOOM_X, &zoom_x); + float zoom_y; + m_glfcns.glGetFloatv (GL_ZOOM_Y, &zoom_y); + + m_glfcns.glPixelZoom (m_devpixratio * pix_dx, + - m_devpixratio * pix_dy); + m_glfcns.glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0); + + // Expect RGB data + if (dv.ndims () == 3 && dv(2) == 3) + { + if (cdata.is_double_type ()) + { + const NDArray xcdata = cdata.array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_single_type ()) + { + const FloatNDArray xcdata = cdata.float_array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint8_type ()) + { + const uint8NDArray xcdata = cdata.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint16_type ()) + { + const uint16NDArray xcdata = cdata.uint16_array_value (); + + OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else + warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)"); + + m_glfcns.glPixelZoom (zoom_x, zoom_y); + + } + } + + void gl2ps_renderer::draw_pixels (int w, int h, const float *data) { // Clip data between 0 and 1 for float values @@ -1154,7 +1415,7 @@ std::string cmd = stream.substr (1); - fp = octave::popen (cmd.c_str (), "w"); + fp = popen (cmd.c_str (), "w"); if (! fp) error (R"(print: failed to open pipe "%s")", stream.c_str ()); @@ -1165,7 +1426,7 @@ { // Write gl2ps output directly to file. - fp = octave::sys::fopen (stream.c_str (), "w"); + fp = sys::fopen (stream.c_str (), "w"); if (! fp) error (R"(gl2ps_print: failed to create file "%s")", stream.c_str ()); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/graphics.cc --- a/libinterp/corefcn/graphics.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/graphics.cc Fri Jul 12 12:14:43 2019 -0400 @@ -308,6 +308,19 @@ } static Matrix +default_data_lim (void) +{ + Matrix retval (1, 4); + + retval(0) = 0; + retval(1) = 1; + retval(2) = 1; // minimum positive + retval(3) = -octave::numeric_limits::Inf (); // maximum negative + + return retval; +} + +static Matrix default_image_cdata (void) { Matrix m (64, 64); @@ -1013,10 +1026,22 @@ } \ while (0) - if (cdata.is_uint8_type ()) + if (cdata.is_int8_type ()) + CONVERT_CDATA_1 (int8NDArray, int8_, false); + else if (cdata.is_int16_type ()) + CONVERT_CDATA_1 (int16NDArray, int16_, false); + else if (cdata.is_int32_type ()) + CONVERT_CDATA_1 (int32NDArray, int32_, false); + else if (cdata.is_int64_type ()) + CONVERT_CDATA_1 (int64NDArray, int64_, false); + else if (cdata.is_uint8_type ()) CONVERT_CDATA_1 (uint8NDArray, uint8_, false); else if (cdata.is_uint16_type ()) CONVERT_CDATA_1 (uint16NDArray, uint16_, false); + else if (cdata.is_uint32_type ()) + CONVERT_CDATA_1 (uint32NDArray, uint32_, false); + else if (cdata.is_uint64_type ()) + CONVERT_CDATA_1 (uint64NDArray, uint64_, false); else if (cdata.is_double_type ()) CONVERT_CDATA_1 (NDArray, , true); else if (cdata.is_single_type ()) @@ -1027,7 +1052,8 @@ { // Don't throw an error; leads to an incomplete FLTK object (bug #46933). warning ("unsupported type for cdata (= %s). " - "Valid types are uint8, uint16, double, single, and bool.", + "Valid types are int8, int16, int32, int64, uint8, uint16, " + "uint32, uint64, double, single, and bool.", cdata.type_name ().c_str ()); a = NDArray (dv, 0); // return 0 instead } @@ -1772,12 +1798,6 @@ } void -children_property::do_delete_children (bool clear) -{ - do_delete_children (clear, false); -} - -void children_property::do_delete_children (bool clear, bool from_root) { for (graphics_handle hchild : children_list) @@ -2732,12 +2752,6 @@ } void -gh_manager::do_free (const graphics_handle& h) -{ - do_free (h, false); -} - -void gh_manager::do_free (const graphics_handle& h, bool from_root) { if (h.ok ()) @@ -3556,13 +3570,7 @@ octave::unwind_protect frame; - frame.protect_var (discard_error_messages); - frame.protect_var (Vdebug_on_error); - frame.protect_var (Vdebug_on_warning); - - discard_error_messages = true; - Vdebug_on_error = false; - Vdebug_on_warning = false; + interpreter_try (frame, octave::error_system::discard); try { @@ -3908,12 +3916,6 @@ */ void -root_figure::properties::remove_child (const graphics_handle& h) -{ - remove_child (h, false); -} - -void root_figure::properties::remove_child (const graphics_handle& h, bool) { gh_manager::pop_figure (h); @@ -3950,12 +3952,6 @@ } void -figure::properties::remove_child (const graphics_handle& h) -{ - remove_child (h, false); -} - -void figure::properties::remove_child (const graphics_handle& h, bool from_root) { base_properties::remove_child (h, from_root); @@ -5077,7 +5073,7 @@ void axes::properties::sync_positions (void) { - // First part is equivalent to `update_tightinset ()' + // First part is equivalent to 'update_tightinset ()' if (activepositionproperty.is ("position")) update_position (); else @@ -5102,9 +5098,9 @@ } /* -%!testif HAVE_OPENGL, HAVE_FLTK; have_window_system () && any (strcmp ("fltk", available_graphics_toolkits ())) +%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) %! hf = figure ("visible", "off"); -%! graphics_toolkit (hf, "fltk"); +%! graphics_toolkit (hf, "qt"); %! unwind_protect %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1)); %! hax = findall (gcf (), "type", "axes"); @@ -5122,9 +5118,9 @@ %! close (hf); %! end_unwind_protect -%!testif HAVE_OPENGL, HAVE_FLTK; have_window_system () && any (strcmp ("fltk", available_graphics_toolkits ())) +%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) %! hf = figure ("visible", "off"); -%! graphics_toolkit (hf, "fltk"); +%! graphics_toolkit (hf, "qt"); %! fpos = get (hf, "position"); %! unwind_protect %! plot (rand (3)); @@ -5142,9 +5138,9 @@ %! close (hf); %! end_unwind_protect -%!testif HAVE_OPENGL, HAVE_FLTK; have_window_system () && any (strcmp ("fltk", available_graphics_toolkits ())) +%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) %! hf = figure ("visible", "off"); -%! graphics_toolkit (hf, "fltk"); +%! graphics_toolkit (hf, "qt"); %! fpos = get (hf, "position"); %! set (gca, "activepositionproperty", "position"); %! unwind_protect @@ -5303,8 +5299,6 @@ // Note: dataspectratio (not mode) will be set through update_aspectratios dataaspectratiomode = "auto"; - drawmode = "normal"; - fontangle = "normal"; fontname = OCTAVE_DEFAULT_FONTNAME; fontsize = 10; @@ -5479,12 +5473,6 @@ } void -axes::properties::delete_text_child (handle_property& hp) -{ - delete_text_child (hp, false); -} - -void axes::properties::delete_text_child (handle_property& hp, bool from_root) { graphics_handle h = hp.handle_value (); @@ -5514,12 +5502,6 @@ } void -axes::properties::remove_child (const graphics_handle& h) -{ - remove_child (h, false); -} - -void axes::properties::remove_child (const graphics_handle& h, bool from_root) { graphics_object go = gh_manager::get_object (h); @@ -6148,7 +6130,9 @@ Matrix bbox = get_boundingbox (true); Matrix ticklen = get_ticklength ().matrix_value (); ticklen(0) *= std::max (bbox(2), bbox(3)); - ticklen(1) *= std::max (bbox(2), bbox(3)); + // FIXME: This algorithm is not Matlab-compatible. See bug #55483. + // Scale the results of Octave's algorithm for better visuals. + ticklen(1) *= (0.76 * std::max (bbox(2), bbox(3))); xticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1)); yticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1)); @@ -7224,31 +7208,35 @@ double& min_pos, double& max_neg, const octave_value& data) { + Matrix m; + if (data.is_matrix_type ()) - { - Matrix m = data.matrix_value (); - - if (m.numel () == 4) - { - double val; - - val = m(0); - if (octave::math::isfinite (val) && val < min_val) - min_val = val; - - val = m(1); - if (octave::math::isfinite (val) && val > max_val) - max_val = val; - - val = m(2); - if (octave::math::isfinite (val) && val > 0 && val < min_pos) - min_pos = val; - - val = m(3); - if (octave::math::isfinite (val) && val < 0 && val > max_neg) - max_neg = val; - } - } + m = data.matrix_value (); + + if (m.numel () != 4) + { + m = Matrix (1, 4, 0.0); + m(2) = octave::numeric_limits::Inf (); + m(3) = -octave::numeric_limits::Inf (); + } + + double val; + + val = m(0); + if (octave::math::isfinite (val) && val < min_val) + min_val = val; + + val = m(1); + if (octave::math::isfinite (val) && val > max_val) + max_val = val; + + val = m(2); + if (octave::math::isfinite (val) && val > 0 && val < min_pos) + min_pos = val; + + val = m(3); + if (octave::math::isfinite (val) && val < 0 && val > max_neg) + max_neg = val; } // magform(x) Returns (a, b), @@ -7642,18 +7630,17 @@ do_update = true; } // FIXME: maybe this test should also be relative? - else if (std::abs (limits(0) - limits(1)) < sqrt (eps)) - { - if (logscale) - { - limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0)); - limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1)); - } - else - { - limits(0) -= 0.1 * std::abs (limits(0)); - limits(1) += 0.1 * std::abs (limits(1)); - } + else if (! logscale && (std::abs (limits(0) - limits(1)) < sqrt (eps))) + { + limits(0) -= 0.1 * std::abs (limits(0)); + limits(1) += 0.1 * std::abs (limits(1)); + do_update = true; + } + else if (logscale + && (std::abs (std::log10 (limits(0) / limits(1))) < sqrt (eps))) + { + limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0)); + limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1)); do_update = true; } @@ -9184,7 +9171,12 @@ get ("fontangle").string_value (), get ("__fontsize_points__").double_value () * dpr); - txt_renderer.set_color (get_color_rgb ()); + txt_renderer.set_anti_aliasing (is_fontsmoothing ()); + + Matrix c = get_color_rgb (); + if (! c.isempty ()) + txt_renderer.set_color (c); + } void @@ -9430,6 +9422,104 @@ facevertexcdata.set (fvc); } +// core coplanar tester +bool is_coplanar (const Matrix &cov) +{ + // Accuracy note: this test will also accept single precision input (although + // stored in double precision). This is because the error threshold is + // sqrt(tol) = 1.5e-7. + double tol = 100 * std::numeric_limits::epsilon (); + EIG eig (cov, false, false, true); + ColumnVector ev = real (eig.eigenvalues ()); + return ev.min () <= tol * ev.max (); +} + +std::list +coplanar_partition (const Matrix &vert, const Matrix &idx, + octave_idx_type nc, octave_idx_type jj) +{ + std::list coplanar_ends; + + Matrix plane_pivot = Matrix (1, 3, 0.0); + for (octave_idx_type i = 0; i < 3; i++) + plane_pivot(0,i) = vert(idx(0,jj)-1,i); + + Matrix fc = Matrix (0, 3, 0.0); // face corner vertex coordinates + Matrix fa = Matrix (1, 3, 0.0); // for append face corner + Matrix coor_cov = Matrix (3, 3, 0.0); + + if (nc >= 5) + { + // Coplanar test that involves all points. + // For nc == 4, this initial test is not beneficial at all. + // If the probability of coplanar input is more than half, for + // the best average performance, we should use nc >= 5. + // Higher threshold is meaningful only when input is known to be + // non-coplanar and nc is small. + + fc.resize (nc - 1, 3); + for (octave_idx_type j = 1; j < nc; j++) + for (octave_idx_type i = 0; i < 3; i++) + fc(j-1,i) = vert(idx(j,jj)-1,i) - plane_pivot(i); + + coor_cov = fc.transpose () * fc; + if (is_coplanar (coor_cov)) + { + coplanar_ends.push_back (nc - 1); + return coplanar_ends; + } + } + + fc.resize (3, 3); + octave_idx_type i_start = 1; + octave_idx_type i_end = 2; + + // Split the polygon into coplanar segments. + // The first point is common corner of all planes. + while (i_start < nc - 1) + { + i_end = i_start + 2; + if (i_end > nc - 1) + { + coplanar_ends.push_back (nc - 1); + break; + } + + // Algorithm: Start from 3 points, keep adding points until the point set + // is no more in a plane. Record the coplanar point set, then advance + // i_start. + + // Prepare 1+3 points for coplanar test. + // The first point is implicitly included. + for (octave_idx_type j = 0; j < 3; j++) + for (octave_idx_type i = 0; i < 3; i++) + fc(j,i) = vert(idx(j+i_start,jj)-1,i) - plane_pivot(i); + + // covariance matrix between coordinates of vertices + coor_cov = fc.transpose () * fc; + + while (true) + { + // coplanar test + if (! is_coplanar (coor_cov)) + break; + + i_end++; + if (i_end > nc - 1) + break; + + // add a point to plane + for (octave_idx_type i = 0; i < 3; i++) + fa(0,i) = vert(idx(i_end,jj)-1,i) - plane_pivot(i); + coor_cov += fa.transpose () * fa; + } + + i_start = i_end - 1; + coplanar_ends.push_back (i_start); + } + return coplanar_ends; +} + void patch::properties::update_data (void) { @@ -9478,48 +9568,32 @@ { for (octave_idx_type jj = 0; jj < idx.columns (); jj++) { - if (! octave::math::isnan (idx(3,jj))) - { - // find first element that is NaN to get number of corners - octave_idx_type nc = 3; - while (nc < fcmax && ! octave::math::isnan (idx(nc,jj))) - nc++; - - std::list coplanar_ends; - - octave_idx_type i_start = 1; - octave_idx_type i_end = 2; - while (i_end < nc - 1) + if (octave::math::isnan (idx(3,jj))) + continue; + + // find first element that is NaN to get number of corners + octave_idx_type nc = 3; + while (nc < fcmax && ! octave::math::isnan (idx(nc,jj))) + nc++; + + // If any of the corners is NaN or Inf, skip coplanar test. + // FIXME: Add support for non-coplanar faces with unclosed contour. + bool is_unclosed = false; + for (octave_idx_type j = 0; j < nc; j++) + { + const octave_idx_type k = idx(j, jj) - 1; + if (! (octave::math::isfinite (vert(k, 0)) + && octave::math::isfinite (vert(k, 1)) + && octave::math::isfinite (vert(k, 2)))) { - // look for coplanar subsets - for (i_end = nc-1; i_end > i_start+1; i_end--) - { - Matrix fc = Matrix (i_end - i_start + 1, 3, 0.0); - for (octave_idx_type j = 0; j <= i_end-i_start; j++) - for (octave_idx_type i = 0; i < 3; i++) - fc(j,i) = vert(idx(j + i_start,jj)-1,i) - - vert(idx(0,jj)-1,i); - - // FIXME: Using svd's to check for co-planarity is slow - // for faces with many vertices. Is there a better way to - // check this? - - // calculate rank of matrix - octave::math::svd result - (fc, - octave::math::svd::Type::sigma_only, - octave::math::svd::Driver::GESDD); - DiagMatrix sigma = result.singular_values (); - double tol = nc * sigma(0,0) - * std::numeric_limits::epsilon (); - if (sigma(2,2) < tol) - break; - } - coplanar_ends.push_back (i_end); - i_start = i_end; + is_unclosed = true; + break; } - coplanar_last_idx.push_back (coplanar_ends); - } + } + if (is_unclosed) + continue; + + coplanar_last_idx.push_back (coplanar_partition (vert, idx, nc, jj)); } } @@ -10137,12 +10211,6 @@ // --------------------------------------------------------------------- void -hggroup::properties::remove_child (const graphics_handle& h) -{ - remove_child (h, false); -} - -void hggroup::properties::remove_child (const graphics_handle& h, bool from_root) { graphics_object go = gh_manager::get_object (h); @@ -11262,9 +11330,7 @@ gh_manager::create_instance (void) { instance = new gh_manager (); - - if (instance) - singleton_cleanup_list::add (cleanup_instance); + singleton_cleanup_list::add (cleanup_instance); } graphics_handle @@ -11623,6 +11689,7 @@ if (cb_arg.is_defined () && ! cb_arg.isempty ()) { octave_value_list args; + octave_value ov_fcn; octave_function *fcn = nullptr; args(0) = h.as_octave_value (); @@ -11647,8 +11714,15 @@ // Copy CB because "function_value" method is non-const. octave_value cb = cb_arg; - if (cb.is_function () || cb.is_function_handle ()) + octave::interpreter& interp + = octave::__get_interpreter__ ("gh_manager::do_execute_callback"); + + octave::error_system& es = interp.get_error_system (); + + if (cb.is_function ()) fcn = cb.function_value (); + else if (cb.is_function_handle ()) + ov_fcn = cb; else if (cb.is_string ()) { int status; @@ -11656,16 +11730,16 @@ try { - octave::interpreter& interp - = octave::__get_interpreter__ ("gh_manager::do_execute_callback"); - interp.eval_string (s, false, status, 0); } catch (octave::execution_exception&) { std::cerr << "execution error in graphics callback function" << std::endl; - Flasterr (ovl ("execution error in graphics callback function")); + + es.last_error_id (""); + es.last_error_message ("execution error in graphics callback function"); + octave::interpreter::recover_from_exception (); } } @@ -11676,7 +11750,7 @@ { Cell c = cb.cell_value (); - fcn = c(0).function_value (); + ov_fcn = c(0); for (int i = 1; i < c.numel () ; i++) args(1+i) = c(i); @@ -11688,16 +11762,22 @@ nm.c_str ()); } - if (fcn) + if (fcn || ov_fcn.is_defined ()) try { - octave::feval (fcn, args); + if (ov_fcn.is_defined ()) + octave::feval (ov_fcn, args); + else + octave::feval (fcn, args); } catch (octave::execution_exception&) { std::cerr << "execution error in graphics callback function" << std::endl; - Flasterr (ovl ("execution error in graphics callback function")); + + es.last_error_id (""); + es.last_error_message ("execution error in graphics callback function"); + octave::interpreter::recover_from_exception (); } @@ -11877,8 +11957,9 @@ %! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]); %!endfunction %! -%!testif HAVE_OPENGL, HAVE_FLTK; have_window_system () && any (strcmp ("fltk", available_graphics_toolkits ())) +%!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ())) %! hf = figure ("visible", "off", "resizefcn", @cb); +%! graphics_toolkit (hf, "qt"); %! unwind_protect %! ## Default %! hui1 = uicontrol ("parent", hf, "interruptible", "on", "callback", @cb); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/graphics.in.h --- a/libinterp/corefcn/graphics.in.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/graphics.in.h Fri Jul 12 12:14:43 2019 -0400 @@ -1759,12 +1759,7 @@ return octave_value (get_children ()); } - void delete_children (bool clear = false) - { - do_delete_children (clear); - } - - void delete_children (bool clear, bool from_root) + void delete_children (bool clear = false, bool from_root = false) { do_delete_children (clear, from_root); } @@ -1890,8 +1885,6 @@ children_list.push_front (val); } - void do_delete_children (bool clear); - void do_delete_children (bool clear, bool from_root); }; @@ -2241,16 +2234,7 @@ bool is_modified (void) const { return is___modified__ (); } - virtual void remove_child (const graphics_handle& h) - { - if (children.remove_child (h.value ())) - { - children.run_listeners (); - mark_modified (); - } - } - - virtual void remove_child (const graphics_handle& h, bool) + virtual void remove_child (const graphics_handle& h, bool = false) { if (children.remove_child (h.value ())) { @@ -2322,12 +2306,7 @@ virtual void update_uicontextmenu (void) const; - virtual void delete_children (bool clear = false) - { - children.delete_children (clear); - } - - virtual void delete_children (bool clear, bool from_root) + virtual void delete_children (bool clear = false, bool from_root = false) { children.delete_children (clear, from_root); } @@ -2489,7 +2468,10 @@ error ("base_graphics_object::set_defaults: invalid graphics object"); } - virtual octave_value get (bool all = false) const + // The following version of the get method is not declared virtual + // because no derived class overrides it. + + octave_value get (bool all = false) const { if (! valid_object ()) error ("base_graphics_object::get: invalid graphics object"); @@ -2559,15 +2541,7 @@ return get_properties ().get___myhandle__ (); } - virtual void remove_child (const graphics_handle& h) - { - if (! valid_object ()) - error ("base_graphics_object::remove_child: invalid graphics object"); - - get_properties ().remove_child (h); - } - - virtual void remove_child (const graphics_handle& h, bool from_root) + virtual void remove_child (const graphics_handle& h, bool from_root = false) { if (! valid_object ()) error ("base_graphics_object::remove_child: invalid graphics object"); @@ -2921,9 +2895,7 @@ class OCTINTERP_API properties : public base_properties { public: - void remove_child (const graphics_handle& h); - - void remove_child (const graphics_handle& h, bool from_root); + void remove_child (const graphics_handle& h, bool from_root = false); Matrix get_boundingbox (bool internal = false, const Matrix& parent_pix_size = Matrix ()) const; @@ -2958,12 +2930,6 @@ // Hide base properties which don't make sense for root object //radio_property beingdeleted h , "{off}|on" END_PROPERTIES - - private: - - // Even though this data member is now unused, keep it for now to - // ensure backward compatibility. It will be removed in version 6. - std::list cbo_stack; }; private: @@ -3106,9 +3072,7 @@ integerhandle = val; } - void remove_child (const graphics_handle& h); - - void remove_child (const graphics_handle& h, bool from_root); + void remove_child (const graphics_handle& h, bool from_root = false); void set_visible (const octave_value& val); @@ -3178,9 +3142,9 @@ array_property papersize U , default_figure_papersize () radio_property papertype SU , "{usletter}|uslegal|a0|a1|a2|a3|a4|a5|b0|b1|b2|b3|b4|b5|arch-a|arch-b|arch-c|arch-d|arch-e|a|b|c|d|e|tabloid|" radio_property paperunits Su , "{inches}|centimeters|normalized|points" - radio_property pointer , "crosshair|fullcrosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand" - array_property pointershapecdata , Matrix (16, 16, 0) - array_property pointershapehotspot , Matrix (1, 2, 0) + radio_property pointer , "crosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand" + array_property pointershapecdata , Matrix (16, 16, 1) + array_property pointershapehotspot , Matrix (1, 2, 1) array_property position s , default_figure_position () radio_property renderer m , "{opengl}|painters" radio_property renderermode , "{auto}|manual" @@ -3222,16 +3186,6 @@ any_property __zoom_mode__ h , Matrix () double_property __device_pixel_ratio__ hU , 1.0 - // Obsolete properties: doublebuffer, mincolormap, wvisual, wvisualmode, - // xdisplay, xvisual, xvisualmode - // FIXME: DEPRECATED: Remove in version 6. - bool_property doublebuffer hd , "on" - double_property mincolormap hd , 64 - string_property wvisual hmd , "" - radio_property wvisualmode hd , "{auto}|manual" - string_property xdisplay hd , "" - string_property xvisual hmd , "" - radio_property xvisualmode hd , "{auto}|manual" END_PROPERTIES protected: @@ -3247,6 +3201,7 @@ papersize.add_constraint (dim_vector (1, 2)); papersize.add_constraint (FINITE); pointershapecdata.add_constraint (dim_vector (16, 16)); + pointershapecdata.add_constraint (dim_vector (32, 32)); pointershapehotspot.add_constraint (dim_vector (1, 2)); position.add_constraint (dim_vector (1, 4)); position.add_constraint (FINITE); @@ -3443,9 +3398,7 @@ public: void set_defaults (base_graphics_object& obj, const std::string& mode); - void remove_child (const graphics_handle& h); - - void remove_child (const graphics_handle& h, bool from_root); + void remove_child (const graphics_handle& h, bool from_root = false); void adopt (const graphics_handle& h); @@ -3643,9 +3596,7 @@ void set_text_child (handle_property& h, const std::string& who, const octave_value& v); - void delete_text_child (handle_property& h); - - void delete_text_child (handle_property& h, bool from_root); + void delete_text_child (handle_property& h, bool from_root = false); // See the genprops.awk script for an explanation of the // properties declarations. @@ -3676,16 +3627,15 @@ array_property currentpoint , Matrix (2, 3, 0.0) row_vector_property dataaspectratio mu , Matrix (1, 3, 1.0) radio_property dataaspectratiomode u , "{auto}|manual" - radio_property drawmode hd , "{normal}|fast" radio_property fontangle u , "{normal}|italic" string_property fontname u , OCTAVE_DEFAULT_FONTNAME double_property fontsize u , 10 + bool_property fontsmoothing u , "on" radio_property fontunits SU , "{points}|inches|centimeters|normalized|pixels" - bool_property fontsmoothing , "on" radio_property fontweight u , "{normal}|bold" double_property gridalpha m , 0.15 radio_property gridalphamode , "{auto}|manual" - color_property gridcolor , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none")) + color_property gridcolor m , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none")) radio_property gridcolormode , "{auto}|manual" radio_property gridlinestyle , "{-}|--|:|-.|none" double_property labelfontsizemultiplier u , 1.1 @@ -3719,7 +3669,7 @@ radio_property units SU , "{normalized}|inches|centimeters|points|pixels|characters" array_property view u , default_axes_view () radio_property xaxislocation u , "{bottom}|top|origin" - color_property xcolor mu , color_values (0.15, 0.15, 0.15) + color_property xcolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none")) radio_property xcolormode , "{auto}|manual" radio_property xdir u , "{normal}|reverse" bool_property xgrid , "off" @@ -3736,7 +3686,7 @@ double_property xticklabelrotation , 0.0 radio_property xtickmode u , "{auto}|manual" radio_property yaxislocation u , "{left}|right|origin" - color_property ycolor mu , color_values (0.15, 0.15, 0.15) + color_property ycolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none")) radio_property ycolormode , "{auto}|manual" radio_property ydir u , "{normal}|reverse" bool_property ygrid , "off" @@ -3751,7 +3701,7 @@ radio_property yticklabelmode u , "{auto}|manual" double_property yticklabelrotation , 0.0 radio_property ytickmode u , "{auto}|manual" - color_property zcolor mu , color_values (0.15, 0.15, 0.15) + color_property zcolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none")) radio_property zcolormode , "{auto}|manual" radio_property zdir u , "{normal}|reverse" bool_property zgrid , "off" @@ -4018,6 +3968,10 @@ update_font ("fontsize"); sync_positions (); } + void update_fontsmoothing (void) + { + update_font ("fontsmoothing"); + } void update_fontangle (void) { update_font ("fontangle"); @@ -4296,12 +4250,12 @@ string_property zdatasource , "" // hidden properties for limit computation - row_vector_property xlim hlr , Matrix () - row_vector_property ylim hlr , Matrix () + row_vector_property xlim hlr , default_data_lim () + row_vector_property ylim hlr , default_data_lim () row_vector_property zlim hlr , Matrix () bool_property xliminclude hl , "on" bool_property yliminclude hl , "on" - bool_property zliminclude hl , "off" + bool_property zliminclude hl , "on" END_PROPERTIES protected: @@ -4319,11 +4273,7 @@ void update_ydata (void) { set_ylim (compute_ylim ()); } - void update_zdata (void) - { - set_zlim (zdata.get_limits ()); - set_zliminclude (get_zdata ().numel () > 0); - } + void update_zdata (void) { set_zlim (zdata.get_limits ()); } }; private: @@ -4393,7 +4343,7 @@ BEGIN_PROPERTIES (text) color_property backgroundcolor , color_property (radio_values ("{none}"), color_values (1, 1, 1)) - color_property color u , color_values (0, 0, 0) + color_property color u , color_property (color_values (0, 0, 0), radio_values ("none")) color_property edgecolor , color_property (radio_values ("{none}"), color_values (0, 0, 0)) bool_property editing , "off" array_property extent rG , Matrix (1, 4, 0.0) @@ -4401,8 +4351,9 @@ radio_property fontangle u , "{normal}|italic|oblique" string_property fontname u , OCTAVE_DEFAULT_FONTNAME double_property fontsize u , 10 + bool_property fontsmoothing u , "on" radio_property fontunits SU , "inches|centimeters|normalized|{points}|pixels" - radio_property fontweight u , "light|{normal}|demi|bold" + radio_property fontweight u , "{normal}|bold" radio_property horizontalalignment mu , "{left}|center|right" radio_property interpreter u , "{tex}|none|latex" radio_property linestyle , "{-}|--|:|-.|none" @@ -4455,19 +4406,23 @@ Matrix pos = get_data_position (); Matrix lim; - lim = Matrix (1, 3, pos(0)); + lim = Matrix (1, 4, pos(0)); lim(2) = (lim(2) <= 0 ? octave::numeric_limits::Inf () : lim(2)); + lim(3) = (lim(3) >= 0 ? -octave::numeric_limits::Inf () : lim(3)); set_xlim (lim); - lim = Matrix (1, 3, pos(1)); + lim = Matrix (1, 4, pos(1)); lim(2) = (lim(2) <= 0 ? octave::numeric_limits::Inf () : lim(2)); + lim(3) = (lim(3) >= 0 ? -octave::numeric_limits::Inf () : lim(3)); set_ylim (lim); if (pos.numel () == 3) { - lim = Matrix (1, 3, pos(2)); + lim = Matrix (1, 4, pos(2)); lim(2) = (lim(2) <= 0 ? octave::numeric_limits::Inf () : lim(2)); + lim(3) = (lim(3) >= 0 ? -octave::numeric_limits::Inf () + : lim(3)); set_zliminclude ("on"); set_zlim (lim); } @@ -4483,21 +4438,30 @@ void update_string (void) { request_autopos (); update_text_extent (); } void update_rotation (void) { update_text_extent (); } - void update_color (void) { update_font (); update_text_extent (); } void update_fontname (void) { update_font (); update_text_extent (); } void update_fontsize (void) { update_font (); update_text_extent (); } - void update_fontangle (void) { update_font (); update_text_extent (); } - - void update_fontweight (void) + void update_fontsmoothing (void) { update_font (); update_text_extent (); } + + void update_color (void) + { + if (! color.is ("none")) + { + update_font (); + update_text_extent (); + } + } + + void update_fontangle (void) { update_font (); update_text_extent (); - // FIXME: DEPRECATED: Remove warning with demi and light in version 6. - if (fontweight.is ("demi") || fontweight.is ("light")) + // FIXME: DEPRECATED: Remove warning for "oblique" in version 7. + if (fontangle.is ("oblique")) warning_with_id ("Octave:deprecated-property", - "Setting 'fontweight' to '%s' is deprecated, \ -use 'normal' or 'bold'.", fontweight.current_value ().c_str ()); + "Setting 'fontangle' to '%s' is deprecated, \ +use 'italic' or 'normal'.", fontangle.current_value ().c_str ()); } + void update_fontweight (void) { update_font (); update_text_extent (); } void update_interpreter (void) { update_text_extent (); } void update_horizontalalignment (void) { update_text_extent (); } @@ -4594,9 +4558,14 @@ cdata.add_constraint ("double"); cdata.add_constraint ("single"); cdata.add_constraint ("logical"); + cdata.add_constraint ("int8"); + cdata.add_constraint ("int16"); + cdata.add_constraint ("int32"); + cdata.add_constraint ("int64"); cdata.add_constraint ("uint8"); cdata.add_constraint ("uint16"); - cdata.add_constraint ("int16"); + cdata.add_constraint ("uint32"); + cdata.add_constraint ("uint64"); cdata.add_constraint ("real"); cdata.add_constraint (dim_vector (-1, -1)); cdata.add_constraint (dim_vector (-1, -1, 3)); @@ -4896,6 +4865,18 @@ faces.add_constraint (dim_vector (-1, -1)); vertices.add_constraint (dim_vector (-1, 2)); vertices.add_constraint (dim_vector (-1, 3)); + cdata.add_constraint ("double"); + cdata.add_constraint ("single"); + cdata.add_constraint ("logical"); + cdata.add_constraint ("int8"); + cdata.add_constraint ("int16"); + cdata.add_constraint ("int32"); + cdata.add_constraint ("int64"); + cdata.add_constraint ("uint8"); + cdata.add_constraint ("uint16"); + cdata.add_constraint ("uint32"); + cdata.add_constraint ("uint64"); + cdata.add_constraint ("real"); cdata.add_constraint (dim_vector (-1, -1)); cdata.add_constraint (dim_vector (-1, -1, 3)); facevertexcdata.add_constraint (dim_vector (-1, 1)); @@ -5148,6 +5129,16 @@ zdata.add_constraint (dim_vector (-1, -1)); cdata.add_constraint ("double"); cdata.add_constraint ("single"); + cdata.add_constraint ("logical"); + cdata.add_constraint ("int8"); + cdata.add_constraint ("int16"); + cdata.add_constraint ("int32"); + cdata.add_constraint ("int64"); + cdata.add_constraint ("uint8"); + cdata.add_constraint ("uint16"); + cdata.add_constraint ("uint32"); + cdata.add_constraint ("uint64"); + cdata.add_constraint ("real"); cdata.add_constraint (dim_vector (-1, -1)); cdata.add_constraint (dim_vector (-1, -1, 3)); alphadata.add_constraint ("double"); @@ -5270,9 +5261,7 @@ class OCTINTERP_API properties : public base_properties { public: - void remove_child (const graphics_handle& h); - - void remove_child (const graphics_handle& h, bool from_root); + void remove_child (const graphics_handle& h, bool from_root = false); void adopt (const graphics_handle& h); @@ -5346,12 +5335,7 @@ class OCTINTERP_API properties : public base_properties { public: - void remove_child (const graphics_handle& h) - { - base_properties::remove_child (h); - } - - void remove_child (const graphics_handle& h, bool from_root) + void remove_child (const graphics_handle& h, bool from_root = false) { base_properties::remove_child (h, from_root); } @@ -5510,7 +5494,7 @@ string_property fontname u , OCTAVE_DEFAULT_FONTNAME double_property fontsize u , 10 radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels" - radio_property fontweight u , "light|{normal}|demi|bold" + radio_property fontweight u , "{normal}|bold" color_property foregroundcolor , color_values (0, 0, 0) radio_property horizontalalignment , "left|{center}|right" callback_property keypressfcn , Matrix () @@ -5553,17 +5537,16 @@ void update_string (void) { update_text_extent (); } void update_fontname (void) { update_text_extent (); } void update_fontsize (void) { update_text_extent (); } - void update_fontangle (void) { update_text_extent (); } - - void update_fontweight (void) + void update_fontangle (void) { update_text_extent (); - // FIXME: DEPRECATED: Remove warning with demi and light in version 6. - if (fontweight.is ("demi") || fontweight.is ("light")) + // FIXME: DEPRECATED: Remove warning for "oblique" in version 7. + if (fontangle.is ("oblique")) warning_with_id ("Octave:deprecated-property", - "Setting 'fontweight' to '%s' is deprecated, \ -use 'normal' or 'bold'.", fontweight.current_value ().c_str ()); + "Setting 'fontangle' to '%s' is deprecated, \ +use 'italic' or 'normal'.", fontangle.current_value ().c_str ()); } + void update_fontweight (void) { update_text_extent (); } void update_fontunits (const caseless_str& old_units); @@ -5623,7 +5606,7 @@ string_property fontname , OCTAVE_DEFAULT_FONTNAME double_property fontsize , 10 radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels" - radio_property fontweight u , "light|{normal}|demi|bold" + radio_property fontweight , "{normal}|bold" color_property foregroundcolor , color_values (0, 0, 0) color_property highlightcolor , color_values (1, 1, 1) array_property position S , default_panel_position () @@ -5656,15 +5639,7 @@ // void update_fontname (void) { update_text_extent (); } // void update_fontsize (void) { update_text_extent (); } // void update_fontangle (void) { update_text_extent (); } - - void update_fontweight (void) - { - // FIXME: DEPRECATED: Remove warning with demi and light in version 6. - if (fontweight.is ("demi") || fontweight.is ("light")) - warning_with_id ("Octave:deprecated-property", - "Setting 'fontweight' to '%s' is deprecated, \ -use 'normal' or 'bold'.", fontweight.current_value ().c_str ()); - } + // void update_fontweight (void) { update_fontweight (); } void update_units (const caseless_str& old_units); void update_fontunits (const caseless_str& old_units); @@ -5723,7 +5698,7 @@ string_property fontname , OCTAVE_DEFAULT_FONTNAME double_property fontsize , 10 radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels" - radio_property fontweight u , "light|{normal}|demi|bold" + radio_property fontweight , "{normal}|bold" color_property foregroundcolor , color_values (0, 0, 0) color_property highlightcolor , color_values (1, 1, 1) array_property position S , default_panel_position () @@ -5751,14 +5726,6 @@ void update_units (const caseless_str& old_units); void update_fontunits (const caseless_str& old_units); - void update_fontweight (void) - { - // FIXME: DEPRECATED: Remove warning with demi and light in version 6. - if (fontweight.is ("demi") || fontweight.is ("light")) - warning_with_id ("Octave:deprecated-property", - "Setting 'fontweight' to '%s' is deprecated, \ -use 'normal' or 'bold'.", fontweight.current_value ().c_str ()); - } }; private: @@ -5825,7 +5792,7 @@ string_property fontname u , OCTAVE_DEFAULT_FONTNAME double_property fontsize u , 10 radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels" - radio_property fontweight u , "light|{normal}|demi|bold" + radio_property fontweight u , "{normal}|bold" color_property foregroundcolor , color_values (0, 0, 0) callback_property keypressfcn , Matrix () callback_property keyreleasefcn , Matrix () @@ -5859,18 +5826,16 @@ void update_data (void) { update_table_extent (); } void update_fontname (void) { update_table_extent (); } void update_fontsize (void) { update_table_extent (); } - void update_fontangle (void) { update_table_extent (); } - - void update_fontweight (void) + void update_fontangle (void) { - // FIXME: DEPRECATED: Remove warning with demi and light in version 6. - if (fontweight.is ("demi") || fontweight.is ("light")) + update_table_extent (); + // FIXME: DEPRECATED: Remove warning for "oblique" in version 7. + if (fontangle.is ("oblique")) warning_with_id ("Octave:deprecated-property", - "Setting 'fontweight' to '%s' is deprecated, \ -use 'normal' or 'bold'.", fontweight.current_value ().c_str ()); - - update_table_extent (); + "Setting 'fontangle' to '%s' is deprecated, \ +use 'italic' or 'normal'.", fontangle.current_value ().c_str ()); } + void update_fontweight (void) { update_table_extent (); } }; private: @@ -6019,6 +5984,7 @@ string_property tooltipstring , "" // Octave-specific properties + string_property __named_icon__ , "" any_property __object__ h , Matrix () END_PROPERTIES @@ -6082,6 +6048,7 @@ string_property tooltipstring , "" // Octave-specific properties + string_property __named_icon__ , "" any_property __object__ h , Matrix () END_PROPERTIES @@ -6254,13 +6221,7 @@ : graphics_handle (); } - static void free (const graphics_handle& h) - { - if (instance_ok ()) - instance->do_free (h); - } - - static void free (const graphics_handle& h, bool from_root) + static void free (const graphics_handle& h, bool from_root = false) { if (instance_ok ()) instance->do_free (h, from_root); @@ -6515,8 +6476,6 @@ graphics_handle do_get_handle (bool integer_figure_handle); - void do_free (const graphics_handle& h); - void do_free (const graphics_handle& h, bool from_root); void do_renumber_figure (const graphics_handle& old_gh, diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/help.cc --- a/libinterp/corefcn/help.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/help.cc Fri Jul 12 12:14:43 2019 -0400 @@ -44,7 +44,6 @@ #include "Cell.h" #include "builtin-defun-decls.h" -#include "call-stack.h" #include "defaults.h" #include "defun.h" #include "dirfns.h" @@ -72,86 +71,86 @@ #include "default-defs.h" -const static char * const operators[] = -{ - "!", - "~", - "!=", - "~=", - R"(")", - "#", - "%", - "#{", - "%{", - "#}", - "%}", - "...", - "&", - "&&", - "'", - "(", - ")", - "*", - "**", - "^", - "+", - "++", - ",", - "-", - "--", - ".'", - ".*", - ".**", - ".^", - "./", - "/", - R"(.\)", - R"(\)", - ":", - ";", - "<", - "<=", - "=", - "==", - ">", - ">=", - "[", - "]", - "|", - "||", - nullptr -}; - -const static string_vector operator_names (operators); - -static bool -looks_like_html (const std::string& msg) -{ - const size_t p1 = msg.find ('\n'); - std::string t = msg.substr (0, p1); - // FIXME: this comparison should be case-insensitive - const size_t p2 = t.find ("", + ">=", + "[", + "]", + "|", + "||", + nullptr + }; + + const static string_vector operator_names (operators); + + static bool + looks_like_html (const std::string& msg) + { + const size_t p1 = msg.find ('\n'); + std::string t = msg.substr (0, p1); + // FIXME: this comparison should be case-insensitive + const size_t p2 = t.find ("= 0 && (line[pos] == ')' || line[pos] == '}')) - { - if (line[pos] == ')') - paren_count++; - else - curly_count++; - - while (curly_count + paren_count > 0 && --pos >= 0) - { - if (line[pos] == ')') - paren_count++; - else if (line[pos] == '(') - paren_count--; - else if (line[pos] == '}') - curly_count++; - else if (line[pos] == '{') - curly_count--; - } - - while (--pos >= 0 && line[pos] == ' ') - ; - } - - while (pos >= 0 && (isalnum (line[pos]) || line[pos] == '_')) - pos--; - - if (++pos >= 0) - return (line.substr (pos, last + 1 - pos)); - else - return std::string (); -} - -static inline bool -is_variable (octave::symbol_table& symtab, const std::string& name) -{ - bool retval = false; - - if (! name.empty ()) - { - octave::symbol_scope scope = symtab.current_scope (); - - octave_value val = scope ? scope.varval (name) : octave_value (); - - retval = val.is_defined (); - } - - return retval; -} - -static string_vector -generate_struct_completions (const std::string& text, - std::string& prefix, std::string& hint) -{ - string_vector names; - - size_t pos = text.rfind ('.'); - bool array = false; - - if (pos != std::string::npos) - { - if (pos == text.length ()) - hint = ""; - else - hint = text.substr (pos+1); - - prefix = text.substr (0, pos); - - if (prefix == "") - { - array = true; - prefix = find_indexed_expression (text); - } - - std::string base_name = prefix; - - pos = base_name.find_first_of ("{(. "); - - if (pos != std::string::npos) - base_name = base_name.substr (0, pos); - - octave::interpreter& interp - = octave::__get_interpreter__ ("generate_struct_completions"); - - octave::symbol_table& symtab = interp.get_symbol_table (); - - if (is_variable (symtab, base_name)) - { - int parse_status; - - octave::unwind_protect frame; - - frame.protect_var (discard_error_messages); - frame.protect_var (discard_warning_messages); - - discard_error_messages = true; - discard_warning_messages = true; - - try - { - octave_value tmp - = interp.eval_string (prefix, true, parse_status); - - frame.run (); - - if (tmp.is_defined () - && (tmp.isstruct () || tmp.isjava () || tmp.is_classdef_object ())) - names = tmp.map_keys (); - } - catch (const octave::execution_exception&) - { - octave::interpreter::recover_from_exception (); - } - } - } - - // Undo look-back that found the array expression, - // but insert an extra "." to distinguish from the non-struct case. - if (array) - prefix = "."; - - return names; -} - -// FIXME: this will have to be much smarter to work "correctly". -static bool -looks_like_struct (const std::string& text, char prev_char) -{ - bool retval = (! text.empty () - && (text != "." || prev_char == ')' || prev_char == '}') - && text.find_first_of (octave::sys::file_ops::dir_sep_chars ()) == std::string::npos - && text.find ("..") == std::string::npos - && text.rfind ('.') != std::string::npos); - - return retval; -} - -// FIXME: make this generate filenames when appropriate. - -static string_vector -generate_possible_completions (const std::string& text, std::string& prefix, - std::string& hint, bool& deemed_struct) -{ - string_vector names; - - prefix = ""; - - char prev_char = octave::command_editor::get_prev_char (text.length ()); - deemed_struct = looks_like_struct (text, prev_char); - - if (deemed_struct) - names = generate_struct_completions (text, prefix, hint); - else - names = octave::make_name_list (); - - // Sort and remove duplicates. - - names.sort (true); - - return names; -} - -static bool -is_completing_dirfns (void) -{ - static std::string dirfns_commands[] = {"cd", "isfile", "isfolder", "ls"}; - static const size_t dirfns_commands_length = 4; - - bool retval = false; - - std::string line = octave::command_editor::get_line_buffer (); - - for (size_t i = 0; i < dirfns_commands_length; i++) - { - int index = line.find (dirfns_commands[i] + ' '); - - if (index == 0) - { - retval = true; - break; - } - } - - return retval; -} - -static std::string -generate_completion (const std::string& text, int state) -{ - std::string retval; - - static std::string prefix; - static std::string hint; - - static size_t hint_len = 0; - - static int list_index = 0; - static int name_list_len = 0; - static int name_list_total_len = 0; - static string_vector name_list; - static string_vector file_name_list; - - static int matches = 0; - - if (state == 0) - { - list_index = 0; - - prefix = ""; - - hint = text; - - // No reason to display symbols while completing a - // file/directory operation. - - bool deemed_struct = false; - - if (is_completing_dirfns ()) - name_list = string_vector (); - else - name_list = generate_possible_completions (text, prefix, hint, - deemed_struct); - - name_list_len = name_list.numel (); - - // If the line was something like "a{1}." then text = "." but - // we don't want to expand all the . files. - if (! deemed_struct) - { - - file_name_list = octave::command_editor::generate_filename_completions (text); - - name_list.append (file_name_list); - - } - - name_list_total_len = name_list.numel (); - - hint_len = hint.length (); - - matches = 0; - - for (int i = 0; i < name_list_len; i++) - if (hint == name_list[i].substr (0, hint_len)) - matches++; - } - - if (name_list_total_len > 0 && matches > 0) - { - while (list_index < name_list_total_len) - { - std::string name = name_list[list_index]; - - list_index++; - - if (hint == name.substr (0, hint_len)) - { - // Special case: array reference forces prefix="." - // in generate_struct_completions () - if (list_index <= name_list_len && ! prefix.empty ()) - retval = (prefix == "." ? "" : prefix) + '.' + name; - else - retval = name; - - char prev_char = octave::command_editor::get_prev_char - (text.length ()); - if (matches == 1 && looks_like_struct (retval, prev_char)) - { - // Don't append anything, since we don't know - // whether it should be '(' or '.'. - - octave::command_editor::set_completion_append_character ('\0'); - } - else - { - octave::input_system& input_sys - = octave::__get_input_system__ ("generate_completion"); - - octave::command_editor::set_completion_append_character - (input_sys.completion_append_char ()); - } - - break; - } - } - } - - return retval; -} - namespace octave { + static std::string + quoting_filename (const std::string& text, int, char quote) + { + if (quote) + return text; + else + return ("'" + text); + } + + // Try to parse a partial command line in reverse, excluding trailing TEXT. + // If it appears a variable has been indexed by () or {}, + // return that expression, + // to allow autocomplete of field names of arrays of structures. + static std::string + find_indexed_expression (const std::string& text) + { + std::string line = command_editor::get_line_buffer (); + + int pos = line.length () - text.length (); + int curly_count = 0; + int paren_count = 0; + + int last = --pos; + + while (pos >= 0 && (line[pos] == ')' || line[pos] == '}')) + { + if (line[pos] == ')') + paren_count++; + else + curly_count++; + + while (curly_count + paren_count > 0 && --pos >= 0) + { + if (line[pos] == ')') + paren_count++; + else if (line[pos] == '(') + paren_count--; + else if (line[pos] == '}') + curly_count++; + else if (line[pos] == '{') + curly_count--; + } + + while (--pos >= 0 && line[pos] == ' ') + ; + } + + while (pos >= 0 && (isalnum (line[pos]) || line[pos] == '_')) + pos--; + + if (++pos >= 0) + return (line.substr (pos, last + 1 - pos)); + else + return std::string (); + } + + static string_vector + generate_struct_completions (const std::string& text, + std::string& prefix, std::string& hint) + { + string_vector names; + + size_t pos = text.rfind ('.'); + bool array = false; + + if (pos != std::string::npos) + { + if (pos == text.length ()) + hint = ""; + else + hint = text.substr (pos+1); + + prefix = text.substr (0, pos); + + if (prefix == "") + { + array = true; + prefix = find_indexed_expression (text); + } + + std::string base_name = prefix; + + pos = base_name.find_first_of ("{(. "); + + if (pos != std::string::npos) + base_name = base_name.substr (0, pos); + + interpreter& interp + = __get_interpreter__ ("generate_struct_completions"); + + if (interp.is_variable (base_name)) + { + int parse_status; + + error_system& es = interp.get_error_system (); + + unwind_protect frame; + + frame.add_method (es, &error_system::set_discard_error_messages, + es.discard_error_messages ()); + frame.add_method (es, &error_system::set_discard_warning_messages, + es.discard_warning_messages ()); + + es.discard_error_messages (true); + es.discard_warning_messages (true); + + try + { + octave_value tmp + = interp.eval_string (prefix, true, parse_status); + + frame.run (); + + if (tmp.is_defined () + && (tmp.isstruct () || tmp.isjava () || tmp.is_classdef_object ())) + names = tmp.map_keys (); + } + catch (const execution_exception&) + { + interpreter::recover_from_exception (); + } + } + } + + // Undo look-back that found the array expression, + // but insert an extra "." to distinguish from the non-struct case. + if (array) + prefix = "."; + + return names; + } + + // FIXME: this will have to be much smarter to work "correctly". + static bool + looks_like_struct (const std::string& text, char prev_char) + { + bool retval = (! text.empty () + && (text != "." || prev_char == ')' || prev_char == '}') + && text.find_first_of (sys::file_ops::dir_sep_chars ()) == std::string::npos + && text.find ("..") == std::string::npos + && text.rfind ('.') != std::string::npos); + + return retval; + } + + // FIXME: make this generate filenames when appropriate. + + static string_vector + generate_possible_completions (const std::string& text, std::string& prefix, + std::string& hint, bool& deemed_struct) + { + string_vector names; + + prefix = ""; + + char prev_char = command_editor::get_prev_char (text.length ()); + deemed_struct = looks_like_struct (text, prev_char); + + if (deemed_struct) + names = generate_struct_completions (text, prefix, hint); + else + names = make_name_list (); + + // Sort and remove duplicates. + + names.sort (true); + + return names; + } + + static bool + is_completing_dirfns (void) + { + static std::string dirfns_commands[] = {"cd", "isfile", "isfolder", "ls"}; + static const size_t dirfns_commands_length = 4; + + bool retval = false; + + std::string line = command_editor::get_line_buffer (); + + for (size_t i = 0; i < dirfns_commands_length; i++) + { + int index = line.find (dirfns_commands[i] + ' '); + + if (index == 0) + { + retval = true; + break; + } + } + + return retval; + } + + static std::string + generate_completion (const std::string& text, int state) + { + std::string retval; + + static std::string prefix; + static std::string hint; + + static size_t hint_len = 0; + + static int list_index = 0; + static int name_list_len = 0; + static int name_list_total_len = 0; + static string_vector name_list; + static string_vector file_name_list; + + static int matches = 0; + + if (state == 0) + { + list_index = 0; + + prefix = ""; + + hint = text; + + // No reason to display symbols while completing a + // file/directory operation. + + bool deemed_struct = false; + + if (is_completing_dirfns ()) + name_list = string_vector (); + else + name_list = generate_possible_completions (text, prefix, hint, + deemed_struct); + + name_list_len = name_list.numel (); + + // If the line was something like "a{1}." then text = "." but + // we don't want to expand all the . files. + if (! deemed_struct) + { + + file_name_list = command_editor::generate_filename_completions (text); + + name_list.append (file_name_list); + + } + + name_list_total_len = name_list.numel (); + + hint_len = hint.length (); + + matches = 0; + + for (int i = 0; i < name_list_len; i++) + if (hint == name_list[i].substr (0, hint_len)) + matches++; + } + + if (name_list_total_len > 0 && matches > 0) + { + while (list_index < name_list_total_len) + { + std::string name = name_list[list_index]; + + list_index++; + + if (hint == name.substr (0, hint_len)) + { + // Special case: array reference forces prefix="." + // in generate_struct_completions () + if (list_index <= name_list_len && ! prefix.empty ()) + retval = (prefix == "." ? "" : prefix) + '.' + name; + else + retval = name; + + char prev_char = command_editor::get_prev_char + (text.length ()); + if (matches == 1 && looks_like_struct (retval, prev_char)) + { + // Don't append anything, since we don't know + // whether it should be '(' or '.'. + + command_editor::set_completion_append_character ('\0'); + } + else + { + input_system& input_sys + = __get_input_system__ ("generate_completion"); + + command_editor::set_completion_append_character + (input_sys.completion_append_char ()); + } + + break; + } + } + } + + return retval; + } + // Use literal "octave" in default setting for PS1 instead of // "\\s" to avoid setting the prompt to "octave.exe" or // "octave-gui", etc. @@ -426,7 +407,7 @@ void input_system::initialize (bool line_editing) { -// Force default line editor if we don't want readline editing. + // Force default line editor if we don't want readline editing. if (! line_editing) { command_editor::force_default_editor (); @@ -436,26 +417,26 @@ // If we are using readline, this allows conditional parsing of the // .inputrc file. - octave::command_editor::set_name ("Octave"); + command_editor::set_name ("Octave"); // FIXME: this needs to include a comma too, but that // causes trouble for the new struct element completion code. static const char *s = "\t\n !\"\'*+-/:;<=>(){}[\\]^`~"; - octave::command_editor::set_basic_word_break_characters (s); + command_editor::set_basic_word_break_characters (s); - octave::command_editor::set_completer_word_break_characters (s); + command_editor::set_completer_word_break_characters (s); - octave::command_editor::set_basic_quote_characters (R"(")"); + command_editor::set_basic_quote_characters (R"(")"); - octave::command_editor::set_filename_quote_characters (" \t\n\\\"'@<>=;|&()#$`?*[!:{"); + command_editor::set_filename_quote_characters (" \t\n\\\"'@<>=;|&()#$`?*[!:{"); - octave::command_editor::set_completer_quote_characters (R"('")"); + command_editor::set_completer_quote_characters (R"('")"); - octave::command_editor::set_completion_function (generate_completion); + command_editor::set_completion_function (generate_completion); - octave::command_editor::set_quoting_function (quoting_filename); + command_editor::set_quoting_function (quoting_filename); } octave_value @@ -541,8 +522,8 @@ } // Synchronize the related gui preference for editor encoding - octave::feval ("__octave_link_gui_preference__", - ovl ("editor/default_encoding", m_mfile_encoding)); + feval ("__octave_link_gui_preference__", + ovl ("editor/default_encoding", m_mfile_encoding)); return retval; } @@ -570,7 +551,7 @@ { Vlast_prompt_time.stamp (); - if (Vdrawnow_requested && octave::application::interactive ()) + if (Vdrawnow_requested && application::interactive ()) { bool eval_error = false; @@ -578,7 +559,7 @@ { Fdrawnow (); } - catch (const octave::execution_exception& e) + catch (const execution_exception& e) { eval_error = true; @@ -587,11 +568,11 @@ if (! stack_trace.empty ()) std::cerr << stack_trace; - if (octave::application::interactive ()) - octave::interpreter::recover_from_exception (); + if (application::interactive ()) + interpreter::recover_from_exception (); } - octave::flush_stdout (); + flush_stdout (); // We set Vdrawnow_requested to false even if there is an error in // drawnow so that the error doesn't reappear at every prompt. @@ -654,53 +635,15 @@ retval = m_interpreter.eval_string (input_buf, true, parse_status, nargout); - if (! Vdebugging && retval.empty ()) + tree_evaluator& tw = m_interpreter.get_evaluator (); + + if (! tw.in_debug_repl () && retval.empty ()) retval(0) = Matrix (); } return retval; } - octave_value input_system::keyboard (const octave_value_list& args) - { - octave_value retval; - - int nargin = args.length (); - - assert (nargin == 0 || nargin == 1); - - octave::unwind_protect frame; - - frame.add_fcn (octave::command_history::ignore_entries, - octave::command_history::ignoring_entries ()); - - octave::command_history::ignore_entries (false); - - frame.protect_var (Vdebugging); - - octave::call_stack& cs = m_interpreter.get_call_stack (); - - frame.add_method (cs, &octave::call_stack::restore_frame, - cs.current_frame ()); - - // FIXME: probably we just want to print one line, not the - // entire statement, which might span many lines... - // - // tree_print_code tpc (octave_stdout); - // stmt.accept (tpc); - - Vdebugging = true; - Vtrack_line_num = false; - - std::string prompt = "debug> "; - if (nargin > 0) - prompt = args(0).string_value (); - - get_debug_input (prompt); - - return retval; - } - bool input_system::have_input_event_hooks (void) const { return ! m_input_event_hook_functions.empty (); @@ -740,7 +683,7 @@ eof = false; - std::string retval = octave::command_editor::readline (s, eof); + std::string retval = command_editor::readline (s, eof); if (! eof && retval.empty ()) retval = "\n"; @@ -748,153 +691,6 @@ return retval; } - static void - execute_in_debugger_handler (const std::pair& arg) - { - octave_link::execute_in_debugger_event (arg.first, arg.second); - } - - void input_system::get_debug_input (const std::string& prompt) - { - octave::unwind_protect frame; - - octave::tree_evaluator& tw = m_interpreter.get_evaluator (); - - bool silent = tw.quiet_breakpoint_flag (false); - - octave::call_stack& cs = m_interpreter.get_call_stack (); - - octave_user_code *caller = cs.caller_user_code (); - std::string nm; - int curr_debug_line; - - if (caller) - { - nm = caller->fcn_file_name (); - - if (nm.empty ()) - nm = caller->name (); - - curr_debug_line = cs.caller_user_code_line (); - } - else - curr_debug_line = cs.current_line (); - - std::ostringstream buf; - - if (! nm.empty ()) - { - if (m_gud_mode) - { - static char ctrl_z = 'Z' & 0x1f; - - buf << ctrl_z << ctrl_z << nm << ':' << curr_debug_line; - } - else - { - // FIXME: we should come up with a clean way to detect - // that we are stopped on the no-op command that marks the - // end of a function or script. - - if (! silent) - { - buf << "stopped in " << nm; - - if (curr_debug_line > 0) - buf << " at line " << curr_debug_line; - } - - octave_link::enter_debugger_event (nm, curr_debug_line); - - octave_link::set_workspace (); - - frame.add_fcn (execute_in_debugger_handler, - std::pair (nm, curr_debug_line)); - - if (! silent) - { - std::string line_buf; - - if (caller) - line_buf = caller->get_code_line (curr_debug_line); - - if (! line_buf.empty ()) - buf << "\n" << curr_debug_line << ": " << line_buf; - } - } - } - - if (silent) - octave::command_editor::erase_empty_line (true); - - std::string msg = buf.str (); - - if (! msg.empty ()) - std::cerr << msg << std::endl; - - frame.add_method (*this, &octave::input_system::set_PS1, m_PS1); - m_PS1 = prompt; - - // FIXME: should debugging be possible in an embedded interpreter? - - octave::application *app = octave::application::app (); - - if (! app->interactive ()) - { - - frame.add_method (app, &octave::application::interactive, - app->interactive ()); - - frame.add_method (app, &octave::application::forced_interactive, - app->forced_interactive ()); - - app->interactive (true); - - app->forced_interactive (true); - } - - octave::parser curr_parser (m_interpreter); - - while (Vdebugging) - { - try - { - Vtrack_line_num = false; - - reset_error_handler (); - - curr_parser.reset (); - - int retval = curr_parser.run (); - - if (octave::command_editor::interrupt (false)) - break; - else - { - if (retval == 0 && curr_parser.m_stmt_list) - { - curr_parser.m_stmt_list->accept (tw); - - if (octave_completion_matches_called) - octave_completion_matches_called = false; - } - - octave_quit (); - } - } - catch (const octave::execution_exception& e) - { - std::string stack_trace = e.info (); - - if (! stack_trace.empty ()) - std::cerr << stack_trace; - - // Ignore errors when in debugging mode; - octave::interpreter::recover_from_exception (); - } - } - } - std::string base_reader::octave_gets (bool& eof) { octave_quit (); @@ -906,9 +702,13 @@ // Process pre input event hook function prior to flushing output and // printing the prompt. + interpreter& interp = __get_interpreter__ ("base_reader::octave_gets"); + + tree_evaluator& tw = interp.get_evaluator (); + if (application::interactive ()) { - if (! Vdebugging) + if (! tw.in_debug_repl ()) octave_link::exit_debugger_event (); octave_link::pre_input_event (); @@ -918,7 +718,7 @@ bool history_skip_auto_repeated_debugging_command = false; - input_system& input_sys = __get_input_system__ ("base_reader::octave_gets"); + input_system& input_sys = interp.get_input_system (); std::string ps = (m_pflag > 0) ? input_sys.PS1 () : input_sys.PS2 (); @@ -926,7 +726,7 @@ pipe_handler_error_count = 0; - output_system& output_sys = __get_output_system__ ("do_sync"); + output_system& output_sys = interp.get_output_system (); output_sys.reset (); @@ -939,16 +739,16 @@ if (retval != "\n" && retval.find_first_not_of (" \t\n\r") != std::string::npos) { - load_path& lp = __get_load_path__ ("base_reader::octave_gets"); + load_path& lp = interp.get_load_path (); lp.update (); - if (Vdebugging) + if (tw.in_debug_repl ()) input_sys.last_debugging_command (retval); else input_sys.last_debugging_command ("\n"); } - else if (Vdebugging) + else if (tw.in_debug_repl ()) { retval = input_sys.last_debugging_command (); history_skip_auto_repeated_debugging_command = true; @@ -981,17 +781,17 @@ bool base_reader::reading_fcn_file (void) const { - return m_lexer ? m_lexer->m_reading_fcn_file : false; + return m_lexer.m_reading_fcn_file; } bool base_reader::reading_classdef_file (void) const { - return m_lexer ? m_lexer->m_reading_classdef_file : false; + return m_lexer.m_reading_classdef_file; } bool base_reader::reading_script_file (void) const { - return m_lexer ? m_lexer->m_reading_script_file : false; + return m_lexer.m_reading_script_file; } class @@ -999,7 +799,7 @@ { public: - terminal_reader (base_lexer *lxr = nullptr) + terminal_reader (base_lexer& lxr) : base_reader (lxr) { } @@ -1019,7 +819,7 @@ { public: - file_reader (FILE *f_arg, base_lexer *lxr = nullptr) + file_reader (FILE *f_arg, base_lexer& lxr) : base_reader (lxr), m_file (f_arg) { } std::string get_input (bool& eof); @@ -1040,7 +840,7 @@ { public: - eval_string_reader (const std::string& str, base_lexer *lxr = nullptr) + eval_string_reader (const std::string& str, base_lexer& lxr) : base_reader (lxr), m_eval_string (str) { } @@ -1057,15 +857,15 @@ static const std::string s_in_src; }; - input_reader::input_reader (base_lexer *lxr) + input_reader::input_reader (base_lexer& lxr) : m_rep (new terminal_reader (lxr)) { } - input_reader::input_reader (FILE *file, base_lexer *lxr) + input_reader::input_reader (FILE *file, base_lexer& lxr) : m_rep (new file_reader (file, lxr)) { } - input_reader::input_reader (const std::string& str, base_lexer *lxr) + input_reader::input_reader (const std::string& str, base_lexer& lxr) : m_rep (new eval_string_reader (str, lxr)) { } @@ -1094,8 +894,8 @@ std::string src_str = octave_fgets (m_file, eof); - octave::input_system& input_sys - = octave::__get_input_system__ ("get_input"); + input_system& input_sys + = __get_input_system__ ("get_input"); std::string mfile_encoding = input_sys.mfile_encoding (); @@ -1125,7 +925,7 @@ error ("file_reader::get_input: converting from codepage '%s' to UTF-8: %s", encoding.c_str (), std::strerror (errno)); - octave::unwind_protect frame; + unwind_protect frame; frame.add_fcn (::free, static_cast (utf8_str)); src_str = std::string (reinterpret_cast (utf8_str), length); @@ -1254,28 +1054,22 @@ @seealso{dbstop, dbcont, dbquit} @end deftypefn */) { - if (args.length () > 1) - print_usage (); - - octave::unwind_protect frame; + int nargin = args.length (); - octave::call_stack& cs = interp.get_call_stack (); - - frame.add_method (cs, &octave::call_stack::restore_frame, - cs.current_frame ()); - - // Skip the frame assigned to the keyboard function. - cs.goto_frame_relative (0); + if (nargin > 1) + print_usage (); octave::tree_evaluator& tw = interp.get_evaluator (); - tw.debug_mode (true); - tw.quiet_breakpoint_flag (false); - tw.current_frame (cs.current_frame ()); + if (nargin == 1) + { + std::string prompt + = args(0).xstring_value ("keyboard: PROMPT must be a string"); - octave::input_system& input_sys = interp.get_input_system (); - - input_sys.keyboard (args); + tw.keyboard (prompt); + } + else + tw.keyboard (); return ovl (); } @@ -1306,7 +1100,7 @@ for (;;) { - std::string cmd = generate_completion (hint, k); + std::string cmd = octave::generate_completion (hint, k); if (! cmd.empty ()) { @@ -1412,18 +1206,20 @@ return ovl (); } -static int -internal_input_event_hook_fcn (void) +namespace octave { - octave::input_system& input_sys - = octave::__get_input_system__ ("internal_input_event_hook_fcn"); + static int internal_input_event_hook_fcn (void) + { + input_system& input_sys + = __get_input_system__ ("internal_input_event_hook_fcn"); - input_sys.run_input_event_hooks (); + input_sys.run_input_event_hooks (); - if (! input_sys.have_input_event_hooks ()) - octave::command_editor::remove_event_hook (internal_input_event_hook_fcn); + if (! input_sys.have_input_event_hooks ()) + command_editor::remove_event_hook (internal_input_event_hook_fcn); - return 0; + return 0; + } } DEFMETHOD (add_input_event_hook, interp, args, , @@ -1461,7 +1257,7 @@ hook_function hook_fcn (args(0), user_data); if (! input_sys.have_input_event_hooks ()) - octave::command_editor::add_event_hook (internal_input_event_hook_fcn); + octave::command_editor::add_event_hook (octave::internal_input_event_hook_fcn); input_sys.add_input_event_hook (hook_fcn); @@ -1494,7 +1290,7 @@ hook_fcn_id.c_str ()); if (! input_sys.have_input_event_hooks ()) - octave::command_editor::remove_event_hook (internal_input_event_hook_fcn); + octave::command_editor::remove_event_hook (octave::internal_input_event_hook_fcn); return ovl (); } @@ -1644,15 +1440,6 @@ return input_sys.yes_or_no (prompt); } -octave_value -do_keyboard (const octave_value_list& args) -{ - octave::input_system& input_sys - = octave::__get_input_system__ ("do_keyboard"); - - return input_sys.keyboard (args); -} - void remove_input_event_hook_functions (void) { diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/input.h --- a/libinterp/corefcn/input.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/input.h Fri Jul 12 12:14:43 2019 -0400 @@ -44,9 +44,6 @@ // the next user prompt. extern OCTINTERP_API bool Vdrawnow_requested; -// TRUE if we are in debugging mode. -extern OCTINTERP_API bool Vdebugging; - // TRUE if we are not executing a command direct from debug> prompt. extern OCTINTERP_API bool Vtrack_line_num; @@ -155,9 +152,6 @@ octave_value_list get_user_input (const octave_value_list& args, int nargout); - octave_value - keyboard (const octave_value_list& args = octave_value_list ()); - bool have_input_event_hooks (void) const; void add_input_event_hook (const hook_function& hook_fcn); @@ -196,8 +190,6 @@ hook_function_list m_input_event_hook_functions; std::string gnu_readline (const std::string& s, bool& eof) const; - - void get_debug_input (const std::string& prompt); }; class base_reader @@ -206,7 +198,7 @@ friend class input_reader; - base_reader (base_lexer *lxr) + base_reader (base_lexer& lxr) : m_count (1), m_pflag (0), m_lexer (lxr) { } @@ -255,7 +247,7 @@ int m_pflag; - base_lexer *m_lexer; + base_lexer& m_lexer; static const std::string s_in_src; }; @@ -264,11 +256,11 @@ { public: - input_reader (base_lexer *lxr = nullptr); + input_reader (base_lexer& lxr); - input_reader (FILE *file, base_lexer *lxr = nullptr); + input_reader (FILE *file, base_lexer& lxr); - input_reader (const std::string& str, base_lexer *lxr = nullptr); + input_reader (const std::string& str, base_lexer& lxr); input_reader (const input_reader& ir) { @@ -339,10 +331,6 @@ OCTAVE_DEPRECATED (5, "use 'octave::input_system::yes_or_no' instead") extern bool octave_yes_or_no (const std::string& prompt); -OCTAVE_DEPRECATED (5, "use 'octave::input_system::keyboard' instead") -extern octave_value do_keyboard (const octave_value_list& args - = octave_value_list ()); - OCTAVE_DEPRECATED (5, "use 'octave::input_system::clear_input_event_hooks' instead") extern void remove_input_event_hook_functions (void); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/interpreter-private.cc --- a/libinterp/corefcn/interpreter-private.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/interpreter-private.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,10 +24,11 @@ # include "config.h" #endif +#include #include #include "bp-table.h" -#include "call-stack.h" +#include "cdef-manager.h" #include "child-list.h" #include "error.h" #include "gtk-manager.h" @@ -38,7 +39,8 @@ #include "load-path.h" #include "load-save.h" #include "oct-hist.h" -#include "ov-classdef.h" +#include "ov.h" +#include "ov-fcn-inline.h" #include "pager.h" #include "symtab.h" @@ -64,6 +66,13 @@ return interp.get_dynamic_loader (); } + error_system& __get_error_system__ (const std::string& who) + { + interpreter& interp = __get_interpreter__ (who); + + return interp.get_error_system (); + } + help_system& __get_help_system__ (const std::string& who) { interpreter& interp = __get_interpreter__ (who); @@ -151,13 +160,6 @@ return tw.get_bp_table (); } - call_stack& __get_call_stack__ (const std::string& who) - { - interpreter& interp = __get_interpreter__ (who); - - return interp.get_call_stack (); - } - child_list& __get_child_list__ (const std::string& who) { interpreter& interp = __get_interpreter__ (who); @@ -178,4 +180,44 @@ return interp.get_gtk_manager (); } + + octave_value + get_function_handle (interpreter& interp, const octave_value& arg, + const std::string& parameter_name) + { + std::list parameter_names; + parameter_names.push_back (parameter_name); + return get_function_handle (interp, arg, parameter_names); + } + + octave_value + get_function_handle (interpreter& interp, const octave_value& arg, + const std::list& parameter_names) + { + if (arg.is_function_handle () || arg.is_inline_function ()) + return arg; + else if (arg.is_string ()) + { + std::string fstr = arg.string_value (); + + if (fstr.empty ()) + return octave_value (); + + symbol_table& symtab = interp.get_symbol_table (); + + octave_value fcn = symtab.find_function (fstr); + + if (fcn.is_defined ()) + return fcn; + + fcn = octave_value (new octave_fcn_inline (fstr, parameter_names)); + + // Possibly warn here that passing the function body in a + // character string is discouraged. + + return fcn; + } + + return octave_value (); + } } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/interpreter-private.h --- a/libinterp/corefcn/interpreter-private.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/interpreter-private.h Fri Jul 12 12:14:43 2019 -0400 @@ -25,18 +25,18 @@ #include "octave-config.h" +#include #include #include "symtab.h" -class cdef_manager; - namespace octave { class bp_table; - class call_stack; + class cdef_manager; class child_list; class dynamic_loader; + class error_system; class gtk_manager; class help_system; class history_system; @@ -52,6 +52,8 @@ extern dynamic_loader& __get_dynamic_loader__ (const std::string& who); + extern error_system& __get_error_system__ (const std::string& who); + extern help_system& __get_help_system__ (const std::string& who); extern history_system& __get_history_system__ (const std::string& who); @@ -76,13 +78,29 @@ extern bp_table& __get_bp_table__ (const std::string& who); - extern call_stack& __get_call_stack__ (const std::string& who); - extern child_list& __get_child_list__ (const std::string& who); extern cdef_manager& __get_cdef_manager__ (const std::string& who); extern gtk_manager& __get_gtk_manager__ (const std::string& who); + + + // Functions that could be methods in the interpreter class but maybe + // shouldn't be exposed as part of the public interface. + + // Convert octave_value object ARG to be a function handle object. It + // may be a function handle, inline function, the name of a function, + // or the text of an inline function that has the given argument names + // PARAMETER_NAMES. Use of the latter form is discouraged. + + octave_value + get_function_handle (interpreter& interp, const octave_value& arg, + const std::string& parameter_name); + + octave_value + get_function_handle (interpreter& interp, const octave_value& arg, + const std::list& parameter_names + = std::list ()); } #endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/interpreter.cc --- a/libinterp/corefcn/interpreter.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/interpreter.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,6 +24,8 @@ # include "config.h" #endif +#include + #include #include @@ -41,7 +43,6 @@ #include "builtin-defun-decls.h" #include "defaults.h" #include "Cell.h" -#include "call-stack.h" #include "defun.h" #include "display.h" #include "error.h" @@ -62,6 +63,7 @@ #include "ov.h" #include "ov-classdef.h" #include "parse.h" +#include "pt-classdef.h" #include "pt-eval.h" #include "pt-jump.h" #include "pt-stmt.h" @@ -359,6 +361,7 @@ : m_app_context (app_context), m_environment (), m_settings (), + m_error_system (*this), m_help_system (*this), m_input_system (*this), m_output_system (*this), @@ -367,7 +370,7 @@ m_load_path (), m_load_save_system (*this), m_type_info (), - m_symbol_table (), + m_symbol_table (*this), m_evaluator (*this), m_stream_list (*this), m_child_list (), @@ -405,11 +408,6 @@ thread::init (); - // Initialize default warning state before --traditional option - // that may reset them. - - initialize_default_warning_state (); - octave_ieee_init (); initialize_xerbla_error_handler (); @@ -417,7 +415,10 @@ initialize_error_handlers (); if (m_app_context) - install_signal_handlers (); + { + install_signal_handlers (); + octave_unblock_signal_by_name ("SIGTSTP"); + } else quit_allowed = false; @@ -536,14 +537,7 @@ void interpreter::intern_nargin (octave_idx_type nargs) { - // FIXME: should this explicitly be top_scope? - symbol_scope scope = m_symbol_table.current_scope (); - - if (scope) - { - scope.assign (".nargin.", nargs); - scope.mark_hidden (".nargin."); - } + m_evaluator.set_auto_fcn_var (stack_frame::NARGIN, nargs); } // Read the history file unless a command-line option inhibits that. @@ -718,7 +712,7 @@ // occurs when reading any of them, but don't exit early because of an // exception. - int interpreter::execute_startup_files (void) const + int interpreter::execute_startup_files (void) { bool read_site_files = m_read_site_files; bool read_init_files = m_read_init_files; @@ -765,6 +759,37 @@ if (read_init_files) { + // Try to execute commands from the Matlab compatible startup.m file + // if it exists anywhere in the load path when starting Octave. + std::string ff_startup_m = file_in_path ("startup.m", ""); + + if (! ff_startup_m.empty ()) + { + int parse_status = 0; + + try + { + eval_string (std::string ("startup"), false, parse_status, 0); + } + catch (const interrupt_exception&) + { + recover_from_exception (); + } + catch (const execution_exception& e) + { + std::string stack_trace = e.info (); + + if (! stack_trace.empty ()) + std::cerr << stack_trace; + + recover_from_exception (); + } + } + + // Schedule the Matlab compatible finish.m file to run if it exists + // anywhere in the load path when exiting Octave. + add_atexit_function ("__finish__"); + // Try to execute commands from $HOME/$OCTAVE_INITFILE and // $OCTAVE_INITFILE. If $OCTAVE_INITFILE is not set, // .octaverc is assumed. @@ -966,19 +991,23 @@ { \ try \ { \ - unwind_protect frame; \ + unwind_protect frame; \ \ - frame.protect_var (Vdebug_on_error); \ - frame.protect_var (Vdebug_on_warning); \ + frame.add_method (m_error_system, \ + &error_system::set_debug_on_error, \ + m_error_system.debug_on_error ()); \ + frame.add_method (m_error_system, \ + &error_system::set_debug_on_warning, \ + m_error_system.debug_on_warning ()); \ \ - Vdebug_on_error = false; \ - Vdebug_on_warning = false; \ + m_error_system.debug_on_error (false); \ + m_error_system.debug_on_warning (false); \ \ F ARGS; \ } \ - OCTAVE_IGNORE_EXCEPTION (const exit_exception&) \ - OCTAVE_IGNORE_EXCEPTION (const interrupt_exception&) \ - OCTAVE_IGNORE_EXCEPTION (const execution_exception&) \ + OCTAVE_IGNORE_EXCEPTION (const exit_exception&) \ + OCTAVE_IGNORE_EXCEPTION (const interrupt_exception&) \ + OCTAVE_IGNORE_EXCEPTION (const execution_exception&) \ OCTAVE_IGNORE_EXCEPTION (const std::bad_alloc&) \ } \ while (0) @@ -999,7 +1028,7 @@ atexit_functions.pop_front (); - OCTAVE_SAFE_CALL (reset_error_handler, ()); + OCTAVE_SAFE_CALL (m_error_system.reset, ()); OCTAVE_SAFE_CALL (feval, (fcn, octave_value_list (), 0)); @@ -1062,13 +1091,19 @@ } symbol_scope - interpreter::get_current_scope (void) + interpreter::get_top_scope (void) const { - return m_symbol_table.current_scope (); + return m_evaluator.get_top_scope (); } symbol_scope - interpreter::require_current_scope (const std::string& who) + interpreter::get_current_scope (void) const + { + return m_evaluator.get_current_scope (); + } + + symbol_scope + interpreter::require_current_scope (const std::string& who) const { symbol_scope scope = get_current_scope (); @@ -1078,11 +1113,6 @@ return scope; } - call_stack& interpreter::get_call_stack (void) - { - return m_evaluator.get_call_stack (); - } - profiler& interpreter::get_profiler (void) { return m_evaluator.get_profiler (); @@ -1090,14 +1120,7 @@ void interpreter::mlock (void) { - call_stack& cs = get_call_stack (); - - octave_function *fcn = cs.current (); - - if (! fcn) - error ("mlock: invalid use outside a function"); - - fcn->lock (); + m_evaluator.mlock (); } void interpreter::munlock (const std::string& nm) @@ -1130,6 +1153,11 @@ return retval; } + std::string interpreter::mfilename (const std::string& opt) const + { + return m_evaluator.mfilename (opt); + } + octave_value_list interpreter::eval_string (const std::string& eval_str, bool silent, int& parse_status, int nargout) @@ -1150,6 +1178,449 @@ return m_evaluator.eval_string (arg, silent, parse_status, nargout); } + octave_value_list interpreter::eval (const std::string& try_code, + int nargout) + { + return m_evaluator.eval (try_code, nargout); + } + + octave_value_list interpreter::eval (const std::string& try_code, + const std::string& catch_code, + int nargout) + { + return m_evaluator.eval (try_code, catch_code, nargout); + } + + octave_value_list interpreter::evalin (const std::string& context, + const std::string& try_code, + int nargout) + { + return m_evaluator.evalin (context, try_code, nargout); + } + + octave_value_list interpreter::evalin (const std::string& context, + const std::string& try_code, + const std::string& catch_code, + int nargout) + { + return m_evaluator.evalin (context, try_code, catch_code, nargout); + } + + //! Evaluate an Octave function (built-in or interpreted) and return + //! the list of result values. + //! + //! @param name The name of the function to call. + //! @param args The arguments to the function. + //! @param nargout The number of output arguments expected. + //! @return A list of output values. The length of the list is not + //! necessarily the same as @c nargout. + + octave_value_list interpreter::feval (const char *name, + const octave_value_list& args, + int nargout) + { + return feval (std::string (name), args, nargout); + } + + octave_value_list interpreter::feval (const std::string& name, + const octave_value_list& args, + int nargout) + { + octave_value fcn = m_symbol_table.find_function (name, args); + + if (fcn.is_undefined ()) + error ("feval: function '%s' not found", name.c_str ()); + + octave_function *of = fcn.function_value (); + + return of->call (m_evaluator, nargout, args); + } + + octave_value_list interpreter::feval (octave_function *fcn, + const octave_value_list& args, + int nargout) + { + if (fcn) + return fcn->call (m_evaluator, nargout, args); + + return octave_value_list (); + } + + octave_value_list interpreter::feval (const octave_value& val, + const octave_value_list& args, + int nargout) + { + // FIXME: do we really want to silently return an empty ovl if + // the function object is undefined? It's essentially what the + // version above that accepts a pointer to an octave_function + // object does and some code was apparently written to rely on it + // (for example, __ode15__). + + if (val.is_undefined ()) + return ovl (); + + if (val.is_function ()) + { + return feval (val.function_value (), args, nargout); + } + else if (val.is_function_handle ()) + { + // This covers function handles, inline functions, and anonymous + // functions. + + std::list arg_list; + arg_list.push_back (args); + + // FIXME: could we make octave_value::subsref a const method? + // It would be difficult because there are instances of + // incrementing the reference count inside subsref methods, + // which means they can't be const with the current way of + // handling reference counting. + + octave_value xval = val; + return xval.subsref ("(", arg_list, nargout); + } + else if (val.is_string ()) + { + return feval (val.string_value (), args, nargout); + } + else + error ("feval: first argument must be a string, inline function, or a function handle"); + + return ovl (); + } + + //! Evaluate an Octave function (built-in or interpreted) and return + //! the list of result values. + //! + //! @param args The first element of @c args is the function to call. + //! It may be the name of the function as a string, a function + //! handle, or an inline function. The remaining arguments are + //! passed to the function. + //! @param nargout The number of output arguments expected. + //! @return A list of output values. The length of the list is not + //! necessarily the same as @c nargout. + + octave_value_list interpreter::feval (const octave_value_list& args, + int nargout) + { + if (args.length () == 0) + error ("feval: first argument must be a string, inline function, or a function handle"); + + octave_value f_arg = args(0); + + octave_value_list tmp_args = args.slice (1, args.length () - 1, true); + + return feval (f_arg, tmp_args, nargout); + } + + void interpreter::install_variable (const std::string& name, + const octave_value& value, bool global) + { + m_evaluator.install_variable (name, value, global); + } + + octave_value interpreter::global_varval (const std::string& name) const + { + return m_evaluator.global_varval (name); + } + + void interpreter::global_assign (const std::string& name, + const octave_value& val) + { + m_evaluator.global_assign (name, val); + } + + octave_value interpreter::top_level_varval (const std::string& name) const + { + return m_evaluator.top_level_varval (name); + } + + void interpreter::top_level_assign (const std::string& name, + const octave_value& val) + { + m_evaluator.top_level_assign (name, val); + } + + bool interpreter::is_variable (const std::string& name) const + { + return m_evaluator.is_variable (name); + } + + bool interpreter::is_local_variable (const std::string& name) const + { + return m_evaluator.is_local_variable (name); + } + + octave_value interpreter::varval (const std::string& name) const + { + return m_evaluator.varval (name); + } + + void interpreter::assign (const std::string& name, + const octave_value& val) + { + m_evaluator.assign (name, val); + } + + void interpreter::assignin (const std::string& context, + const std::string& name, + const octave_value& val) + { + m_evaluator.assignin (context, name, val); + } + + void interpreter::source_file (const std::string& file_name, + const std::string& context, bool verbose, + bool require_file, + const std::string& warn_for) + { + m_evaluator.source_file (file_name, context, verbose, require_file, + warn_for); + } + + static void + safe_fclose (FILE *f) + { + if (f) + fclose (static_cast (f)); + } + + octave_value + interpreter::parse_fcn_file (const std::string& full_file, + const std::string& file, + const std::string& dir_name, + const std::string& dispatch_type, + const std::string& package_name, + bool require_file, + bool force_script, bool autoload, + bool relative_lookup, + const std::string& warn_for) + { + octave_value retval; + + unwind_protect frame; + + octave_function *fcn_ptr = nullptr; + + // Open function file and parse. + + FILE *in_stream = command_editor::get_input_stream (); + + frame.add_fcn (command_editor::set_input_stream, in_stream); + + frame.add_fcn (command_history::ignore_entries, + command_history::ignoring_entries ()); + + command_history::ignore_entries (); + + FILE *ffile = nullptr; + + if (! full_file.empty ()) + ffile = sys::fopen (full_file, "rb"); + + if (ffile) + { + frame.add_fcn (safe_fclose, ffile); + + parser parser (ffile, *this); + + parser.m_curr_class_name = dispatch_type; + parser.m_curr_package_name = package_name; + parser.m_autoloading = autoload; + parser.m_fcn_file_from_relative_lookup = relative_lookup; + + parser.m_lexer.m_force_script = force_script; + parser.m_lexer.prep_for_file (); + parser.m_lexer.m_parsing_class_method = ! dispatch_type.empty (); + + parser.m_lexer.m_fcn_file_name = file; + parser.m_lexer.m_fcn_file_full_name = full_file; + parser.m_lexer.m_dir_name = dir_name; + parser.m_lexer.m_package_name = package_name; + + int status = parser.run (); + + fcn_ptr = parser.m_primary_fcn_ptr; + + if (status == 0) + { + if (parser.m_lexer.m_reading_classdef_file + && parser.m_classdef_object) + { + // Convert parse tree for classdef object to + // meta.class info (and stash it in the symbol + // table?). Return pointer to constructor? + + if (fcn_ptr) + panic_impossible (); + + bool is_at_folder = ! dispatch_type.empty (); + + try + { + fcn_ptr = parser.m_classdef_object->make_meta_class (*this, is_at_folder); + } + catch (const execution_exception&) + { + delete parser.m_classdef_object; + throw; + } + + if (fcn_ptr) + retval = octave_value (fcn_ptr); + + delete parser.m_classdef_object; + + parser.m_classdef_object = nullptr; + } + else if (fcn_ptr) + { + retval = octave_value (fcn_ptr); + + fcn_ptr->maybe_relocate_end (); + + if (parser.m_parsing_subfunctions) + { + if (! parser.m_endfunction_found) + parser.m_subfunction_names.reverse (); + + fcn_ptr->stash_subfunction_names (parser.m_subfunction_names); + } + } + } + else + error ("parse error while reading file %s", full_file.c_str ()); + } + else if (require_file) + error ("no such file, '%s'", full_file.c_str ()); + else if (! warn_for.empty ()) + error ("%s: unable to open file '%s'", warn_for.c_str (), + full_file.c_str ()); + + return retval; + } + + bool interpreter::at_top_level (void) const + { + return m_evaluator.at_top_level (); + } + + bool interpreter::isglobal (const std::string& name) const + { + return m_evaluator.is_global (name); + } + + octave_value interpreter::find (const std::string& name) + { + return m_evaluator.find (name); + } + + void interpreter::clear_all (bool force) + { + m_evaluator.clear_all (force); + } + + void interpreter::clear_objects (void) + { + m_evaluator.clear_objects (); + } + + void interpreter::clear_variable (const std::string& name) + { + m_evaluator.clear_variable (name); + } + + void interpreter::clear_variable_pattern (const std::string& pattern) + { + m_evaluator.clear_variable_pattern (pattern); + } + + void interpreter::clear_variable_regexp (const std::string& pattern) + { + m_evaluator.clear_variable_regexp (pattern); + } + + void interpreter::clear_variables (void) + { + m_evaluator.clear_variables (); + } + + void interpreter::clear_global_variable (const std::string& name) + { + m_evaluator.clear_global_variable (name); + } + + void interpreter::clear_global_variable_pattern (const std::string& pattern) + { + m_evaluator.clear_global_variable_pattern (pattern); + } + + void interpreter::clear_global_variable_regexp (const std::string& pattern) + { + m_evaluator.clear_global_variable_regexp (pattern); + } + + void interpreter::clear_global_variables (void) + { + m_evaluator.clear_global_variables (); + } + + void interpreter::clear_functions (bool force) + { + m_symbol_table.clear_functions (force); + } + + void interpreter::clear_function (const std::string& name) + { + m_symbol_table.clear_function (name); + } + + void interpreter::clear_symbol (const std::string& name) + { + m_evaluator.clear_symbol (name); + } + + void interpreter::clear_function_pattern (const std::string& pat) + { + m_symbol_table.clear_function_pattern (pat); + } + + void interpreter::clear_function_regexp (const std::string& pat) + { + m_symbol_table.clear_function_regexp (pat); + } + + void interpreter::clear_symbol_pattern (const std::string& pat) + { + return m_evaluator.clear_symbol_pattern (pat); + } + + void interpreter::clear_symbol_regexp (const std::string& pat) + { + return m_evaluator.clear_symbol_regexp (pat); + } + + std::list interpreter::global_variable_names (void) + { + return m_evaluator.global_variable_names (); + } + + std::list interpreter::variable_names (void) + { + return m_evaluator.variable_names (); + } + + std::list interpreter::user_function_names (void) + { + return m_symbol_table.user_function_names (); + } + + std::list interpreter::autoloaded_functions (void) const + { + return m_evaluator.autoloaded_functions (); + } + void interpreter::recover_from_exception (void) { can_interrupt = true; @@ -1201,7 +1672,7 @@ m_history_system.timestamp_format_string ("%%-- %D %I:%M %p --%%"); - Fbeep_on_error (octave_value (true)); + m_error_system.beep_on_error (true); Fconfirm_recursive_rmdir (octave_value (false)); Fdisable_diagonal_matrix (octave_value (true)); @@ -1223,13 +1694,13 @@ { m_load_path.execute_pkg_add (dir); } - catch (const octave::interrupt_exception&) + catch (const interrupt_exception&) { - octave::interpreter::recover_from_exception (); + interpreter::recover_from_exception (); } - catch (const octave::execution_exception&) + catch (const execution_exception&) { - octave::interpreter::recover_from_exception (); + interpreter::recover_from_exception (); } } } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/interpreter.h --- a/libinterp/corefcn/interpreter.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/interpreter.h Fri Jul 12 12:14:43 2019 -0400 @@ -25,14 +25,17 @@ #include "octave-config.h" +#include #include #include "child-list.h" #include "quit.h" #include "str-vec.h" +#include "cdef-manager.h" #include "dynamic-ld.h" #include "environment.h" +#include "error.h" #include "gtk-manager.h" #include "help.h" #include "input.h" @@ -40,7 +43,6 @@ #include "load-save.h" #include "oct-hist.h" #include "oct-stream.h" -#include "ov-classdef.h" #include "ov-typeinfo.h" #include "pager.h" #include "pt-eval.h" @@ -60,7 +62,6 @@ namespace octave { class profiler; - class call_stack; class child_list; // The application object contains a pointer to the current @@ -97,7 +98,7 @@ void initialize_history (bool read_history_file = false); // If creating an embedded interpreter, you may inhibit setting - // the default compiled-in path by calling intialize_load_path + // the default compiled-in path by calling initialize_load_path // with set_initial_path = false prior calling initialize. After // that, you can add directories to the load path to set up a // custom path. @@ -109,7 +110,7 @@ void initialize (void); // Initialize the interpreter (if not already done by an explicit - // call to intialize), execute startup files, --eval option code, + // call to initialize), execute startup files, --eval option code, // script files, and/or interactive commands. int execute (void); @@ -159,6 +160,11 @@ return m_settings; } + error_system& get_error_system (void) + { + return m_error_system; + } + help_system& get_help_system (void) { return m_help_system; @@ -194,25 +200,24 @@ return m_load_save_system; } + type_info& get_type_info (void) + { + return m_type_info; + } + symbol_table& get_symbol_table (void) { return m_symbol_table; } - type_info& get_type_info (void) - { - return m_type_info; - } + tree_evaluator& get_evaluator (void); - symbol_scope get_current_scope (void); - symbol_scope require_current_scope (const std::string& who); - - call_stack& get_call_stack (void); + symbol_scope get_top_scope (void) const; + symbol_scope get_current_scope (void) const; + symbol_scope require_current_scope (const std::string& who) const; profiler& get_profiler (void); - tree_evaluator& get_evaluator (void); - stream_list& get_stream_list (void); child_list& get_child_list (void) @@ -238,6 +243,8 @@ bool mislocked (const std::string& nm); + std::string mfilename (const std::string& opt = "") const; + octave_value_list eval_string (const std::string& eval_str, bool silent, int& parse_status, int nargout); @@ -247,6 +254,129 @@ octave_value_list eval_string (const octave_value& arg, bool silent, int& parse_status, int nargout); + octave_value_list eval (const std::string& try_code, int nargout); + + octave_value_list eval (const std::string& try_code, + const std::string& catch_code, int nargout); + + octave_value_list evalin (const std::string& context, + const std::string& try_code, int nargout); + + octave_value_list evalin (const std::string& context, + const std::string& try_code, + const std::string& catch_code, int nargout); + + octave_value_list + feval (const char *name, + const octave_value_list& args = octave_value_list (), + int nargout = 0); + + octave_value_list + feval (const std::string& name, + const octave_value_list& args = octave_value_list (), + int nargout = 0); + + octave_value_list + feval (octave_function *fcn, + const octave_value_list& args = octave_value_list (), + int nargout = 0); + + octave_value_list + feval (const octave_value& f_arg, + const octave_value_list& args = octave_value_list (), + int nargout = 0); + + octave_value_list feval (const octave_value_list& args, int nargout = 0); + + void install_variable (const std::string& name, const octave_value& value, + bool global); + + void set_global_value (const std::string& name, const octave_value& value); + + octave_value global_varval (const std::string& name) const; + + void global_assign (const std::string& name, + const octave_value& val = octave_value ()); + + octave_value top_level_varval (const std::string& name) const; + + void top_level_assign (const std::string& name, + const octave_value& val = octave_value ()); + + bool is_variable (const std::string& name) const; + + bool is_local_variable (const std::string& name) const; + + octave_value varval (const std::string& name) const; + + void assign (const std::string& name, + const octave_value& val = octave_value ()); + + void assignin (const std::string& context, const std::string& varname, + const octave_value& val = octave_value ()); + + void source_file (const std::string& file_name, + const std::string& context = "", + bool verbose = false, bool require_file = true, + const std::string& warn_for = ""); + + octave_value parse_fcn_file (const std::string& full_file, + const std::string& file, + const std::string& dir_name, + const std::string& dispatch_type, + const std::string& package_name, + bool require_file, bool force_script, + bool autoload, bool relative_lookup, + const std::string& warn_for); + + bool at_top_level (void) const; + + bool isglobal (const std::string& name) const; + + octave_value find (const std::string& name); + + void clear_all (bool force = false); + + void clear_objects (void); + + void clear_variable (const std::string& name); + + void clear_variable_pattern (const std::string& pattern); + + void clear_variable_regexp (const std::string& pattern); + + void clear_variables (void); + + void clear_global_variable (const std::string& name); + + void clear_global_variable_pattern (const std::string& pattern); + + void clear_global_variable_regexp (const std::string& pattern); + + void clear_global_variables (void); + + void clear_functions (bool force = false); + + void clear_function (const std::string& name); + + void clear_symbol (const std::string& name); + + void clear_function_pattern (const std::string& pat); + + void clear_function_regexp (const std::string& pat); + + void clear_symbol_pattern (const std::string& pat); + + void clear_symbol_regexp (const std::string& pat); + + std::list global_variable_names (void); + + std::list variable_names (void); + + std::list user_function_names (void); + + std::list autoloaded_functions (void) const; + static void recover_from_exception (void); static void add_atexit_function (const std::string& fname); @@ -271,7 +401,7 @@ void display_startup_message (void) const; - int execute_startup_files (void) const; + int execute_startup_files (void); int execute_eval_option_code (void); @@ -287,6 +417,8 @@ settings m_settings; + error_system m_error_system; + help_system m_help_system; input_system m_input_system; @@ -340,29 +472,4 @@ }; } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::interpreter::recover_from_exception' instead") -static inline void -recover_from_exception (void) -{ - octave::interpreter::recover_from_exception (); -} - -OCTAVE_DEPRECATED (4.4, "use 'octave::interpreter::add_atexit_function' instead") -static inline void -add_atexit_function (const std::string& fname) -{ - octave::interpreter::add_atexit_function (fname); -} - -OCTAVE_DEPRECATED (4.4, "use 'octave::interpreter::remove_atexit_function' instead") -static inline bool -remove_atexit_function (const std::string& fname) -{ - return octave::interpreter::remove_atexit_function (fname); -} - #endif - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/inv.cc --- a/libinterp/corefcn/inv.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/inv.cc Fri Jul 12 12:14:43 2019 -0400 @@ -83,13 +83,17 @@ if (isfloat) { result = arg.float_complex_diag_matrix_value ().inverse (info); - if (nargout > 1) + if (info == -1) + frcond = 0.0f; + else if (nargout > 1) frcond = arg.float_complex_diag_matrix_value ().rcond (); } else { result = arg.complex_diag_matrix_value ().inverse (info); - if (nargout > 1) + if (info == -1) + rcond = 0.0; + else if (nargout > 1) rcond = arg.complex_diag_matrix_value ().rcond (); } } @@ -98,13 +102,17 @@ if (isfloat) { result = arg.float_diag_matrix_value ().inverse (info); - if (nargout > 1) + if (info == -1) + frcond = 0.0f; + else if (nargout > 1) frcond = arg.float_diag_matrix_value ().rcond (); } else { result = arg.diag_matrix_value ().inverse (info); - if (nargout > 1) + if (info == -1) + rcond = 0.0; + else if (nargout > 1) rcond = arg.diag_matrix_value ().rcond (); } } @@ -189,7 +197,7 @@ if (isfloat) { volatile float xrcond = frcond; - rcond_plus_one_eq_one = xrcond + 1.0F == 1.0F; + rcond_plus_one_eq_one = xrcond + 1.0f == 1.0f; } else { @@ -225,10 +233,29 @@ %! assert (isa (xinv, "double")); %! assert (isa (rcond, "double")); +%!testif HAVE_UMFPACK <*56232> +%! fail ("A = inv (sparse ([1, 2;0 ,0]))", "warning", "matrix singular"); +%! assert (A, sparse ([Inf, Inf; 0, 0])); + +%!testif HAVE_UMFPACK <*56232> +%! fail ("A = inv (sparse ([1i, 2;0 ,0]))", "warning", "matrix singular"); +%! assert (A, sparse ([Inf, Inf; 0, 0])); + +%!test +%! fail ("A = inv (diag ([1, 0, 1]))", "warning", "matrix singular"); +%! assert (A, diag ([Inf, Inf, Inf])); + +%!error inv (diag ([0, 0])) +%!error inv (diag (complex ([0, 0]))) + +%!testif HAVE_UMFPACK <*56232> +%! fail ("A = inv (sparse ([1, 0, 0; 0, 0, 0; 0, 0, 1]))", "warning", "matrix singular"); +%! assert (A, sparse ([Inf, 0, 0; 0, 0, 0; 0, 0, Inf])); + %!error inv () %!error inv ([1, 2; 3, 4], 2) %!error inv ([1, 2; 3, 4; 5, 6]) - +%!error inv (sparse (2, 2, 0)) */ DEFALIAS (inverse, inv); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/load-path.cc --- a/libinterp/corefcn/load-path.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/load-path.cc Fri Jul 12 12:14:43 2019 -0400 @@ -46,146 +46,146 @@ #include "unwind-prot.h" #include "utils.h" -static void -maybe_add_path_elts (std::string& path, const std::string& dir) -{ - std::string tpath = octave::genpath (dir); - - if (! tpath.empty ()) - { - if (path.empty ()) - path = tpath; - else - path += octave::directory_path::path_sep_str () + tpath; - } -} - -static std::list -split_path (const std::string& p) -{ - std::list retval; - - size_t beg = 0; - size_t end = p.find (octave::directory_path::path_sep_char ()); - - size_t len = p.length (); - - while (end != std::string::npos) - { - std::string elt = p.substr (beg, end-beg); - - if (! elt.empty ()) - retval.push_back (elt); - - beg = end + 1; - - if (beg == len) - break; - - end = p.find (octave::directory_path::path_sep_char (), beg); - } - - std::string elt = p.substr (beg); - - if (! elt.empty ()) - retval.push_back (elt); - - return retval; -} - -// Strip trailing directory separators. - -static std::string -strip_trailing_separators (const std::string& dir_arg) -{ - std::string dir = dir_arg; - - size_t k = dir.length (); - - while (k > 1 && octave::sys::file_ops::is_dir_sep (dir[k-1])) - k--; - - if (k < dir.length ()) - dir.resize (k); - - return dir; -} - -// Should we cache all files in private directories, or is it OK to just -// look them up each time as needed? - -static std::string -find_private_file (const std::string& fname) -{ - std::string retval; - - // Look in private directory corresponding to current function (if - // any). - - octave::symbol_scope scope = octave::__get_current_scope__ ("find_private_file"); - - octave_user_function *curr_fcn = scope ? scope.function () : nullptr; - - if (curr_fcn) - { - // Even for private functions, dir_name doesn't contain the - // "private" directory component so we append it here in all - // cases. - - std::string dir_name = curr_fcn->dir_name (); - - if (! dir_name.empty ()) - { - std::string pfname = dir_name + octave::sys::file_ops::dir_sep_str () - + "private" + octave::sys::file_ops::dir_sep_str () + fname; - - octave::sys::file_stat fs (pfname); - - if (fs.exists () && fs.is_reg ()) - retval = pfname; - } - } - - return retval; -} - -// True if a path is contained in a path list separated by path_sep_char - -static bool -in_path_list (const std::string& path_list, const std::string& path) -{ - size_t ps = path.size (); - size_t pls = path_list.size (); - size_t pos = path_list.find (path); - char psc = octave::directory_path::path_sep_char (); - while (pos != std::string::npos) - { - if ((pos == 0 || path_list[pos-1] == psc) - && (pos + ps == pls || path_list[pos + ps] == psc)) - return true; - else - pos = path_list.find (path, pos + 1); - } - - return false; -} - -static void -rehash_internal (void) -{ - octave::load_path& lp = octave::__get_load_path__ ("rehash_internal"); - - lp.update (); - - // FIXME: maybe we should rename this variable since it is being - // used for more than keeping track of the prompt time. - - // This will force updated functions to be found. - Vlast_prompt_time.stamp (); -} - namespace octave { + static void + maybe_add_path_elts (std::string& path, const std::string& dir) + { + std::string tpath = genpath (dir); + + if (! tpath.empty ()) + { + if (path.empty ()) + path = tpath; + else + path += directory_path::path_sep_str () + tpath; + } + } + + static std::list + split_path (const std::string& p) + { + std::list retval; + + size_t beg = 0; + size_t end = p.find (directory_path::path_sep_char ()); + + size_t len = p.length (); + + while (end != std::string::npos) + { + std::string elt = p.substr (beg, end-beg); + + if (! elt.empty ()) + retval.push_back (elt); + + beg = end + 1; + + if (beg == len) + break; + + end = p.find (directory_path::path_sep_char (), beg); + } + + std::string elt = p.substr (beg); + + if (! elt.empty ()) + retval.push_back (elt); + + return retval; + } + + // Strip trailing directory separators. + + static std::string + strip_trailing_separators (const std::string& dir_arg) + { + std::string dir = dir_arg; + + size_t k = dir.length (); + + while (k > 1 && sys::file_ops::is_dir_sep (dir[k-1])) + k--; + + if (k < dir.length ()) + dir.resize (k); + + return dir; + } + + // Should we cache all files in private directories, or is it OK to just + // look them up each time as needed? + + static std::string + find_private_file (const std::string& fname) + { + std::string retval; + + // Look in private directory corresponding to current function (if + // any). + + symbol_scope scope = __get_current_scope__ ("find_private_file"); + + octave_user_function *curr_fcn = scope ? scope.function () : nullptr; + + if (curr_fcn) + { + // Even for private functions, dir_name doesn't contain the + // "private" directory component so we append it here in all + // cases. + + std::string dir_name = curr_fcn->dir_name (); + + if (! dir_name.empty ()) + { + std::string pfname = dir_name + sys::file_ops::dir_sep_str () + + "private" + sys::file_ops::dir_sep_str () + fname; + + sys::file_stat fs (pfname); + + if (fs.exists () && fs.is_reg ()) + retval = pfname; + } + } + + return retval; + } + + // True if a path is contained in a path list separated by path_sep_char + + static bool + in_path_list (const std::string& path_list, const std::string& path) + { + size_t ps = path.size (); + size_t pls = path_list.size (); + size_t pos = path_list.find (path); + char psc = directory_path::path_sep_char (); + while (pos != std::string::npos) + { + if ((pos == 0 || path_list[pos-1] == psc) + && (pos + ps == pls || path_list[pos + ps] == psc)) + return true; + else + pos = path_list.find (path, pos + 1); + } + + return false; + } + + static void + rehash_internal (void) + { + load_path& lp = __get_load_path__ ("rehash_internal"); + + lp.update (); + + // FIXME: maybe we should rename this variable since it is being + // used for more than keeping track of the prompt time. + + // This will force updated functions to be found. + Vlast_prompt_time.stamp (); + } + std::string load_path::sys_path; load_path::abs_dir_cache_type load_path::abs_dir_cache; @@ -846,14 +846,14 @@ if (! octave_interpreter_ready) return; - octave::unwind_protect frame; - - std::string file = octave::sys::file_ops::concat (dir, script_file); - - octave::sys::file_stat fs (file); + unwind_protect frame; + + std::string file = sys::file_ops::concat (dir, script_file); + + sys::file_stat fs (file); if (fs.exists ()) - octave::source_file (file, "base"); + source_file (file, "base"); } // FIXME: maybe we should also maintain a map to speed up this method of access. @@ -1082,7 +1082,7 @@ string_vector flist; std::string msg; - if (! octave::sys::get_dirlist (d, flist, msg)) + if (! sys::get_dirlist (d, flist, msg)) warning ("load_path: %s: %s", d.c_str (), msg.c_str ()); else { @@ -2246,7 +2246,7 @@ Reinitialize Octave's load path directory cache. @end deftypefn */) { - rehash_internal (); + octave::rehash_internal (); return ovl (); } @@ -2337,7 +2337,7 @@ lp.set (path, true); - rehash_internal (); + octave::rehash_internal (); } if (nargout > 0) @@ -2438,7 +2438,7 @@ { std::string arg = arglist(i).xstring_value ("addpath: all arguments must be strings"); - std::list dir_elts = split_path (arg); + std::list dir_elts = octave::split_path (arg); if (! append) std::reverse (dir_elts.begin (), dir_elts.end ()); @@ -2483,7 +2483,7 @@ } if (need_to_update) - rehash_internal (); + octave::rehash_internal (); return retval; } @@ -2527,7 +2527,7 @@ for (int i = 0; i < nargin; i++) { std::string arg = args(i).xstring_value ("rmpath: all arguments must be strings"); - std::list dir_elts = split_path (arg); + std::list dir_elts = octave::split_path (arg); for (const auto& dir : dir_elts) { @@ -2542,7 +2542,7 @@ } if (need_to_update) - rehash_internal (); + octave::rehash_internal (); return retval; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/load-path.h diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/load-save.cc --- a/libinterp/corefcn/load-save.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/load-save.cc Fri Jul 12 12:14:43 2019 -0400 @@ -66,7 +66,6 @@ #include "ov-cell.h" #include "pager.h" #include "syminfo.h" -#include "symtab.h" #include "sysdep.h" #include "unwind-prot.h" #include "utils.h" @@ -685,17 +684,10 @@ std::string struct_name = argv[argv_idx]; - symbol_scope scope = m_interpreter.get_current_scope (); - - octave_value struct_var; + if (! m_interpreter.is_variable (struct_name)) + error ("save: no such variable: '%s'", struct_name.c_str ()); - if (scope) - { - if (! scope.is_variable (struct_name)) - error ("save: no such variable: '%s'", struct_name.c_str ()); - - struct_var = scope.varval (struct_name); - } + octave_value struct_var = m_interpreter.varval (struct_name); if (! struct_var.isstruct () || struct_var.numel () != 1) error ("save: '%s' is not a scalar structure", struct_name.c_str ()); @@ -914,9 +906,9 @@ const load_save_format& fmt, bool save_as_floats) { - call_stack& cs = m_interpreter.get_call_stack (); + tree_evaluator& tw = m_interpreter.get_evaluator (); - symbol_info_list syminfo_list = cs.glob_symbol_info (pattern); + symbol_info_list syminfo_list = tw.glob_symbol_info (pattern); size_t saved = 0; @@ -1032,9 +1024,9 @@ { write_header (os, fmt); - call_stack& cs = m_interpreter.get_call_stack (); + tree_evaluator& tw = m_interpreter.get_evaluator (); - symbol_info_list syminfo_list = cs.top_scope_symbol_info (); + symbol_info_list syminfo_list = tw.top_scope_symbol_info (); double save_mem_size = 0; @@ -1070,24 +1062,7 @@ bool global, const std::string& /*doc*/) { - symbol_table& symtab = m_interpreter.get_symbol_table (); - - symbol_scope scope = symtab.require_current_scope ("load_save_system::install_loaded_variable"); - - if (global) - { - symbol_record sym = scope.find_symbol (name); - - if (! sym.is_global ()) - { - symbol_scope global_scope = symtab.global_scope (); - symbol_record global_sym = global_scope.find_symbol (name); - - sym.bind_fwd_rep (global_scope.get_rep (), global_sym); - } - } - - scope.assign (name, val); + m_interpreter.install_variable (name, val, global); } std::string load_save_system::init_save_header_format (void) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/ls-hdf5.cc --- a/libinterp/corefcn/ls-hdf5.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/ls-hdf5.cc Fri Jul 12 12:14:43 2019 -0400 @@ -414,6 +414,24 @@ #if defined (HAVE_HDF5) +// The following subroutine creates an HDF5 representation of the way +// we will store Octave range types (triplets of floating-point numbers). +// NUM_TYPE is the HDF5 numeric type to use for storage +// (e.g., H5T_NATIVE_DOUBLE to save as 'double'). +// Note that any necessary conversions are handled automatically by HDF5. + +static hid_t +hdf5_make_range_type (hid_t num_type) +{ + hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3); + + H5Tinsert (type_id, "base", 0 * sizeof (double), num_type); + H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type); + H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type); + + return type_id; +} + // This function is designed to be passed to H5Giterate, which calls it // on each data item in an HDF5 file. For the item whose name is NAME in // the group GROUP_ID, this function sets dv->tc to an Octave representation @@ -686,6 +704,7 @@ else if (type_class_id == H5T_COMPOUND) { hid_t complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE); + hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE); if (hdf5_types_compatible (type_id, complex_type)) { @@ -700,17 +719,25 @@ H5Sclose (space_id); } - else - // Assume that if its not complex its a range. - // If its not, it'll be rejected later in the range code. - d->tc = type_info.lookup_type ("range"); + else if (hdf5_types_compatible (type_id, range_type)) + { + // If it's not a complex, check if it's a range + d->tc = octave_value_typeinfo::lookup_type ("range"); + } + else // Otherwise, just ignore it with a warning. + { + warning ("load: can't read '%s' (unknown datatype)", name); + retval = 0; // unknown datatype; skip + return retval; + } + H5Tclose (range_type); H5Tclose (complex_type); } else { warning ("load: can't read '%s' (unknown datatype)", name); - retval = 0; // unknown datatype; skip + retval = 0; // unknown datatype; skip return retval; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/ls-mat5.cc --- a/libinterp/corefcn/ls-mat5.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/ls-mat5.cc Fri Jul 12 12:14:43 2019 -0400 @@ -51,7 +51,6 @@ #include "str-vec.h" #include "Cell.h" -#include "call-stack.h" #include "defaults.h" #include "defun.h" #include "error.h" @@ -70,6 +69,7 @@ #include "ovl.h" #include "pager.h" #include "parse.h" +#include "pt-eval.h" #include "sysdep.h" #include "unwind-prot.h" #include "utils.h" @@ -861,7 +861,7 @@ { if (fpath.empty ()) // We have a builtin function - tc = make_fcn_handle (fname); + tc = make_fcn_handle (interp, fname); else { std::string mroot = @@ -976,15 +976,9 @@ // Set up temporary scope to use for evaluating the text // that defines the anonymous function. - octave::symbol_table& symtab = interp.get_symbol_table (); - - octave::symbol_scope local_scope; - - symtab.set_scope (local_scope); - - octave::call_stack& cs = interp.get_call_stack (); - cs.push (local_scope, 0); - frame.add_method (cs, &octave::call_stack::pop); + octave::tree_evaluator& tw = interp.get_evaluator (); + tw.push_dummy_scope ("read_mat5_binary_element"); + frame.add_method (tw, &octave::tree_evaluator::pop_scope); if (m2.nfields () > 0) { @@ -995,7 +989,7 @@ std::string key = m2.key (p0); octave_value val = m2.contents (p0); - local_scope.assign (key, val, 0); + interp.assign (key, val); } } @@ -1013,8 +1007,6 @@ error ("load: failed to load anonymous function handle"); tc = new octave_fcn_handle (fh->fcn_val (), "@"); - - frame.run (); } else error ("load: invalid function handle type"); @@ -1191,7 +1183,7 @@ } else { - cdef_manager& cdm = interp.get_cdef_manager (); + octave::cdef_manager& cdm = interp.get_cdef_manager (); if (cdm.find_class (classname, false, true).ok ()) { @@ -1495,9 +1487,9 @@ is.read (reinterpret_cast (&magic), 2); if (magic == 0x4d49) - swap = 0; + swap = false; else if (magic == 0x494d) - swap = 1; + swap = true; else { if (! quiet) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/ls-oct-text.cc --- a/libinterp/corefcn/ls-oct-text.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/ls-oct-text.cc Fri Jul 12 12:14:43 2019 -0400 @@ -251,8 +251,7 @@ return ""; } - if (! (name == ".nargin." || name == ".nargout." - || name == CELL_ELT_TAG || octave::valid_identifier (name))) + if (! (name == CELL_ELT_TAG || octave::valid_identifier (name))) error ("load: invalid identifier '%s' found in file '%s'", name.c_str (), filename.c_str ()); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/lsode.cc --- a/libinterp/corefcn/lsode.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/lsode.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,6 +24,7 @@ # include "config.h" #endif +#include #include #include "LSODE.h" @@ -32,6 +33,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter-private.h" #include "ovl.h" #include "ov-fcn.h" #include "ov-cell.h" @@ -45,10 +47,10 @@ #include "LSODE-opts.cc" // Global pointer for user defined function required by lsode. -static octave_function *lsode_fcn; +static octave_value lsode_fcn; // Global pointer for optional user defined jacobian function used by lsode. -static octave_function *lsode_jac; +static octave_value lsode_jac; // Have we warned about imaginary values returned from user function? static bool warned_fcn_imaginary = false; @@ -66,7 +68,7 @@ args(1) = t; args(0) = x; - if (lsode_fcn) + if (lsode_fcn.is_defined ()) { octave_value_list tmp; @@ -106,7 +108,7 @@ args(1) = t; args(0) = x; - if (lsode_jac) + if (lsode_jac.is_defined ()) { octave_value_list tmp; @@ -275,11 +277,14 @@ octave::symbol_table& symtab = interp.get_symbol_table (); std::string fcn_name, fname, jac_name, jname; - lsode_fcn = nullptr; - lsode_jac = nullptr; + + lsode_fcn = octave_value (); + lsode_jac = octave_value (); octave_value f_arg = args(0); + std::list parameter_names ({"x", "t"}); + if (f_arg.iscell ()) { Cell c = f_arg.cell_value (); @@ -287,92 +292,49 @@ f_arg = c(0); else if (c.numel () == 2) { - if (c(0).is_function_handle () || c(0).is_inline_function ()) - lsode_fcn = c(0).function_value (); - else - { - fcn_name = unique_symbol_name ("__lsode_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, t) y = "); - lsode_fcn = extract_function (c(0), "lsode", fcn_name, fname, - "; endfunction"); - } + lsode_fcn = octave::get_function_handle (interp, c(0), + parameter_names); - if (lsode_fcn) + if (lsode_fcn.is_defined ()) { - if (c(1).is_function_handle () || c(1).is_inline_function ()) - lsode_jac = c(1).function_value (); - else - { - jac_name = unique_symbol_name ("__lsode_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, t) jac = "); - lsode_jac = extract_function (c(1), "lsode", jac_name, - jname, "; endfunction"); + lsode_jac = octave::get_function_handle (interp, c(1), + parameter_names); - if (! lsode_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - lsode_fcn = nullptr; - } - } + if (lsode_jac.is_undefined ()) + lsode_fcn = octave_value (); } } else error ("lsode: incorrect number of elements in cell array"); } - if (! lsode_fcn && ! f_arg.iscell ()) + if (lsode_fcn.is_undefined () && ! f_arg.iscell ()) { if (f_arg.is_function_handle () || f_arg.is_inline_function ()) - lsode_fcn = f_arg.function_value (); + lsode_fcn = f_arg; else { switch (f_arg.rows ()) { case 1: - do - { - fcn_name = unique_symbol_name ("__lsode_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, t) y = "); - lsode_fcn = extract_function (f_arg, "lsode", fcn_name, - fname, "; endfunction"); - } - while (0); + lsode_fcn = octave::get_function_handle (interp, f_arg, + parameter_names); break; case 2: { string_vector tmp = f_arg.string_vector_value (); - fcn_name = unique_symbol_name ("__lsode_fcn__"); - fname = "function y = "; - fname.append (fcn_name); - fname.append (" (x, t) y = "); - lsode_fcn = extract_function (tmp(0), "lsode", fcn_name, - fname, "; endfunction"); + lsode_fcn = octave::get_function_handle (interp, tmp(0), + parameter_names); - if (lsode_fcn) + if (lsode_fcn.is_defined ()) { - jac_name = unique_symbol_name ("__lsode_jac__"); - jname = "function jac = "; - jname.append (jac_name); - jname.append (" (x, t) jac = "); - lsode_jac = extract_function (tmp(1), "lsode", - jac_name, jname, - "; endfunction"); + lsode_jac = octave::get_function_handle (interp, tmp(1), + parameter_names); - if (! lsode_jac) - { - if (fcn_name.length ()) - symtab.clear_function (fcn_name); - lsode_fcn = nullptr; - } + if (lsode_jac.is_undefined ()) + lsode_fcn = octave_value (); } } break; @@ -383,7 +345,7 @@ } } - if (! lsode_fcn) + if (lsode_fcn.is_undefined ()) error ("lsode: FCN argument is not a valid function name or handle"); ColumnVector state = args(1).xvector_value ("lsode: initial state X_0 must be a vector"); @@ -402,7 +364,8 @@ double tzero = out_times (0); ODEFunc func (lsode_user_function); - if (lsode_jac) + + if (lsode_jac.is_defined ()) func.set_jacobian_function (lsode_user_jacobian); LSODE ode (state, tzero, func); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/mappers.cc --- a/libinterp/corefcn/mappers.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/mappers.cc Fri Jul 12 12:14:43 2019 -0400 @@ -131,7 +131,7 @@ %! v = [0, pi, pi/2, pi/2]; %! assert (real (acos (x)), v); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac and %! ## Windows. Their trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif @@ -162,7 +162,7 @@ %! v = [0, pi/2*i, pi*i, pi/2*i]; %! assert (acosh (x), v, sqrt (eps)); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac. %! ## Mac trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif @@ -185,7 +185,7 @@ %! v = single ([0, pi/2*i, pi*i, pi/2*i]); %! assert (acosh (x), v, sqrt (eps ("single"))); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac. %! ## Mac trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif @@ -206,7 +206,7 @@ %! v = [0, pi, pi/2, -pi/2]; %! assert (imag (acosh (x)), v); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac and %! ## Windows. Their trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif @@ -334,7 +334,7 @@ %! v = [pi/2, -pi/2, 0, -0]; %! assert (real (asin (x)), v); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac and %! ## Windows. Their trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif @@ -377,7 +377,7 @@ %! v = [0, 0, pi/2, -pi/2]; %! assert (imag (asinh (x)), v); -%!xtest <52627> +%!xtest <*52627> %! ## Same test code as above, but intended only for test statistics on Mac and %! ## Windows. Their trig/hyperbolic functions have huge tolerances. %! if (! ismac ()), return; endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/mex.cc --- a/libinterp/corefcn/mex.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/mex.cc Fri Jul 12 12:14:43 2019 -0400 @@ -40,7 +40,6 @@ #include "quit.h" #include "Cell.h" -#include "call-stack.h" #include "error.h" #include "interpreter-private.h" #include "interpreter.h" @@ -50,6 +49,7 @@ #include "oct-map.h" #include "ovl.h" #include "ov.h" +#include "ov-classdef.h" #include "ov-mex-fcn.h" #include "ov-usr-fcn.h" #include "pager.h" @@ -1490,12 +1490,15 @@ mxArray_sparse (mxClassID id_arg, mwSize m, mwSize n, mwSize nzmax_arg, mxComplexity flag = mxREAL) - : mxArray_matlab (id_arg, m, n), nzmax (nzmax_arg), - pr (mxArray::calloc (nzmax, get_element_size ())), - pi (flag == mxCOMPLEX ? mxArray::calloc (nzmax, get_element_size ()) : nullptr), - ir (static_cast (mxArray::calloc (nzmax, sizeof (mwIndex)))), - jc (static_cast (mxArray::calloc (n + 1, sizeof (mwIndex)))) - { } + : mxArray_matlab (id_arg, m, n) + { + nzmax = (nzmax_arg > 0 ? nzmax_arg : 1); + pr = mxArray::calloc (nzmax, get_element_size ()); + pi = (flag == mxCOMPLEX ? mxArray::calloc (nzmax, get_element_size ()) + : nullptr); + ir = (static_cast (mxArray::calloc (nzmax, sizeof (mwIndex)))); + jc = (static_cast (mxArray::calloc (n + 1, sizeof (mwIndex)))); + } private: @@ -1560,7 +1563,11 @@ void set_jc (mwIndex *jc_arg) { jc = jc_arg; } - void set_nzmax (mwSize nzmax_arg) { nzmax = nzmax_arg; } + void set_nzmax (mwSize nzmax_arg) + { + /* Require storage for at least 1 element */ + nzmax = (nzmax_arg > 0 ? nzmax_arg : 1); + } octave_value as_octave_value (void) const { @@ -2155,10 +2162,10 @@ { if (! fname) { - octave::call_stack& cs - = octave::__get_call_stack__ ("mex::function_name"); - - octave_function *fcn = cs.current (); + octave::tree_evaluator& tw + = octave::__get_evaluator__ ("mex::function_name"); + + octave_function *fcn = tw.current_function (); if (fcn) { @@ -3484,13 +3491,10 @@ octave_value val; + octave::interpreter& interp = octave::__get_interpreter__ ("mexGetVariable"); + if (! strcmp (space, "global")) - { - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("mexGetVariable"); - - val = symtab.global_varval (name); - } + val = interp.global_varval (name); else { // FIXME: should this be in variables.cc? @@ -3507,18 +3511,15 @@ if (base) { - octave::call_stack& cs - = octave::__get_call_stack__ ("mexGetVariable"); - - cs.goto_base_frame (); - - frame.add_method (cs, &octave::call_stack::pop); + octave::tree_evaluator& tw = interp.get_evaluator (); + + frame.add_method (tw, &octave::tree_evaluator::restore_frame, + tw.current_call_stack_frame_number ()); + + tw.goto_base_frame (); } - octave::symbol_scope scope - = octave::__require_current_scope__ ("mexGetVariable"); - - val = scope.varval (name); + val = interp.varval (name); } else mexErrMsgTxt ("mexGetVariable: symbol table does not exist"); @@ -3555,13 +3556,10 @@ if (! name || name[0] == '\0') return 1; + octave::interpreter& interp = octave::__get_interpreter__ ("mexPutVariable"); + if (! strcmp (space, "global")) - { - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("mexPutVariable"); - - symtab.global_assign (name, mxArray::as_octave_value (ptr)); - } + interp.global_assign (name, mxArray::as_octave_value (ptr)); else { // FIXME: should this be in variables.cc? @@ -3578,18 +3576,15 @@ if (base) { - octave::call_stack& cs - = octave::__get_call_stack__ ("mexPutVariable"); - - cs.goto_base_frame (); - - frame.add_method (cs, &octave::call_stack::pop); + octave::tree_evaluator& tw = interp.get_evaluator (); + + frame.add_method (tw, &octave::tree_evaluator::restore_frame, + tw.current_call_stack_frame_number ()); + + tw.goto_base_frame (); } - octave::symbol_scope scope - = octave::__require_current_scope__ ("mexPutVariable"); - - scope.assign (name, mxArray::as_octave_value (ptr)); + interp.assign (name, mxArray::as_octave_value (ptr)); } else mexErrMsgTxt ("mexPutVariable: symbol table does not exist"); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/module.mk --- a/libinterp/corefcn/module.mk Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/module.mk Fri Jul 12 12:14:43 2019 -0400 @@ -68,6 +68,7 @@ %reldir%/oct-obj.h \ %reldir%/oct-prcstrm.h \ %reldir%/oct-procbuf.h \ + %reldir%/oct-process.h \ %reldir%/oct-stdstrm.h \ %reldir%/oct-stream.h \ %reldir%/oct-strstrm.h \ @@ -82,7 +83,10 @@ %reldir%/sighandlers.h \ %reldir%/sparse-xdiv.h \ %reldir%/sparse-xpow.h \ + %reldir%/stack-frame.h \ + %reldir%/stack-frame-walker.h \ %reldir%/syminfo.h \ + %reldir%/syminfo-accumulator.h \ %reldir%/symrec.h \ %reldir%/symscope.h \ %reldir%/symtab.h \ @@ -205,6 +209,7 @@ %reldir%/oct-map.cc \ %reldir%/oct-prcstrm.cc \ %reldir%/oct-procbuf.cc \ + %reldir%/oct-process.cc \ %reldir%/oct-stream.cc \ %reldir%/oct-strstrm.cc \ %reldir%/oct-tex-lexer.ll \ @@ -232,6 +237,7 @@ %reldir%/sparse.cc \ %reldir%/spparms.cc \ %reldir%/sqrtm.cc \ + %reldir%/stack-frame.cc \ %reldir%/strfind.cc \ %reldir%/strfns.cc \ %reldir%/sub2ind.cc \ diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/nproc.cc --- a/libinterp/corefcn/nproc.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/nproc.cc Fri Jul 12 12:14:43 2019 -0400 @@ -24,8 +24,10 @@ # include "config.h" #endif +#include "nproc-wrapper.h" + #include "defun.h" -#include "nproc-wrapper.h" +#include "error.h" DEFUN (nproc, args, , doc: /* -*- texinfo -*- @@ -54,7 +56,7 @@ if (nargin > 1) print_usage (); - octave_nproc_query query = OCTAVE_NPROC_CURRENT; + octave_nproc_query query = OCTAVE_NPROC_CURRENT_OVERRIDABLE; if (nargin == 1) { @@ -81,6 +83,9 @@ %!assert (nproc ("all") >= 1) %!assert (nproc ("current") >= 1) +## Test that "overridable" is the default +%!assert (nproc ("overridable"), nproc ()) + %!test %! c = nproc ("current"); %! unwind_protect diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-errno.in.cc --- a/libinterp/corefcn/oct-errno.in.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-errno.in.cc Fri Jul 12 12:14:43 2019 -0400 @@ -296,14 +296,9 @@ if (! instance) { instance = new octave_errno (); - - if (instance) - singleton_cleanup_list::add (cleanup_instance); + singleton_cleanup_list::add (cleanup_instance); } - if (! instance) - error ("unable to create errno object!"); - return retval; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-fstrm.cc --- a/libinterp/corefcn/oct-fstrm.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-fstrm.cc Fri Jul 12 12:14:43 2019 -0400 @@ -40,11 +40,11 @@ octave_fstream::octave_fstream (const std::string& nm_arg, std::ios::openmode arg_md, octave::mach_info::float_format ff) - : octave::base_stream (arg_md, ff), nm (nm_arg) + : octave::base_stream (arg_md, ff), m_name (nm_arg) { - fs.open (nm.c_str (), arg_md); + m_fstream.open (m_name.c_str (), arg_md); - if (! fs) + if (! m_fstream) // Note: error is inherited from octave::base_stream, not ::error. error (std::strerror (errno)); } @@ -76,13 +76,13 @@ bool octave_fstream::eof (void) const { - return fs.eof (); + return m_fstream.eof (); } void octave_fstream::do_close (void) { - fs.close (); + m_fstream.close (); } std::istream * @@ -91,7 +91,7 @@ std::istream *retval = nullptr; if (mode () & std::ios::in) - retval = &fs; + retval = &m_fstream; return retval; } @@ -102,7 +102,7 @@ std::ostream *retval = nullptr; if (mode () & std::ios::out) - retval = &fs; + retval = &m_fstream; return retval; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-fstrm.h --- a/libinterp/corefcn/oct-fstrm.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-fstrm.h Fri Jul 12 12:14:43 2019 -0400 @@ -68,7 +68,7 @@ // The name of the file. - std::string name (void) const { return nm; } + std::string name (void) const { return m_name; } std::istream * input_stream (void); @@ -80,9 +80,9 @@ private: - std::string nm; + std::string m_name; - std::fstream fs; + std::fstream m_fstream; }; #endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-handle.h --- a/libinterp/corefcn/oct-handle.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-handle.h Fri Jul 12 12:14:43 2019 -0400 @@ -28,6 +28,7 @@ #include "dMatrix.h" #include "lo-ieee.h" +#include "error.h" #include "ov.h" // --------------------------------------------------------------------- diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-hist.cc --- a/libinterp/corefcn/oct-hist.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-hist.cc Fri Jul 12 12:14:43 2019 -0400 @@ -134,7 +134,7 @@ tmp.resize (len - 1); if (! tmp.empty ()) - if (octave::command_history::add (tmp)) + if (command_history::add (tmp)) octave_link::append_history (tmp); } } @@ -162,7 +162,7 @@ mk_tmp_hist_file (const octave_value_list& args, bool insert_curr, const char *warn_for) { - string_vector hlist = octave::command_history::list (); + string_vector hlist = command_history::list (); int hist_count = hlist.numel () - 1; // switch to zero-based indexing @@ -172,7 +172,7 @@ // but the actual commands performed will. if (! insert_curr) - octave::command_history::remove (hist_count); + command_history::remove (hist_count); hist_count--; // skip last entry in history list @@ -225,7 +225,7 @@ reverse = true; } - std::string name = octave::sys::tempnam ("", "oct-"); + std::string name = sys::tempnam ("", "oct-"); std::fstream file (name.c_str (), std::ios::out); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-iostrm.cc --- a/libinterp/corefcn/oct-iostrm.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-iostrm.cc Fri Jul 12 12:14:43 2019 -0400 @@ -69,7 +69,7 @@ bool octave_istream::eof (void) const { - return is && is->eof (); + return m_istream && m_istream->eof (); } octave::stream @@ -83,7 +83,7 @@ bool octave_ostream::eof (void) const { - return os && os->eof (); + return m_ostream && m_ostream->eof (); } octave::stream diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-iostrm.h --- a/libinterp/corefcn/oct-iostrm.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-iostrm.h Fri Jul 12 12:14:43 2019 -0400 @@ -38,7 +38,7 @@ std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format ()) - : octave::base_stream (m, ff), nm (n) { } + : octave::base_stream (m, ff), m_name (n) { } // No copying! @@ -66,7 +66,7 @@ // The name of the file. - std::string name (void) const { return nm; } + std::string name (void) const { return m_name; } protected: @@ -74,7 +74,7 @@ private: - std::string nm; + std::string m_name; virtual const char * stream_type (void) const = 0; }; @@ -87,7 +87,7 @@ octave_istream (std::istream *arg = nullptr, const std::string& n = "") : octave_base_iostream (n, std::ios::in, octave::mach_info::native_float_format ()), - is (arg) + m_istream (arg) { } static octave::stream @@ -97,7 +97,7 @@ bool eof (void) const; - std::istream * input_stream (void) { return is; } + std::istream * input_stream (void) { return m_istream; } std::ostream * output_stream (void) { return nullptr; } @@ -107,7 +107,7 @@ private: - std::istream *is; + std::istream *m_istream; const char * stream_type (void) const { return "octave_istream"; } @@ -126,7 +126,7 @@ octave_ostream (std::ostream *arg, const std::string& n = "") : octave_base_iostream (n, std::ios::out, octave::mach_info::native_float_format ()), - os (arg) + m_ostream (arg) { } static octave::stream @@ -138,7 +138,7 @@ std::istream * input_stream (void) { return nullptr; } - std::ostream * output_stream (void) { return os; } + std::ostream * output_stream (void) { return m_ostream; } protected: @@ -146,7 +146,7 @@ private: - std::ostream *os; + std::ostream *m_ostream; const char * stream_type (void) const { return "octave_ostream"; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-map.cc --- a/libinterp/corefcn/oct-map.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-map.cc Fri Jul 12 12:14:43 2019 -0400 @@ -557,11 +557,11 @@ %! assert (fieldnames (reshape (x, 3, 8)), {"d"; "a"; "f"}); ## test chopping of trailing singletons -%!test <51634> +%!test <*51634> %! x(1,1).d = 10; x(4,6).a = "b"; x(2,4).f = 27; %! reshape (x, 3, 8, 1, 1); -%!test <46385> +%!test <*46385> %! M = repmat (struct ('a', ones(100), 'b', true), 1, 2); %! M = repmat(M, 1, 2); %! assert (size (M), [1, 4]); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-prcstrm.cc --- a/libinterp/corefcn/oct-prcstrm.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-prcstrm.cc Fri Jul 12 12:14:43 2019 -0400 @@ -31,16 +31,18 @@ octave::stream octave_iprocstream::create (const std::string& n, std::ios::openmode arg_md, - octave::mach_info::float_format ff) + octave::mach_info::float_format ff, + const std::string& encoding) { - return octave::stream (new octave_iprocstream (n, arg_md, ff)); + return octave::stream (new octave_iprocstream (n, arg_md, ff, encoding)); } octave_iprocstream::octave_iprocstream (const std::string& n, std::ios::openmode arg_md, - octave::mach_info::float_format ff) + octave::mach_info::float_format ff, + const std::string& encoding) : octave_stdiostream (n, octave::popen (n.c_str (), "r"), - arg_md, ff, octave::pclose) + arg_md, ff, encoding, octave::pclose) { } octave_iprocstream::~octave_iprocstream (void) @@ -50,16 +52,18 @@ octave::stream octave_oprocstream::create (const std::string& n, std::ios::openmode arg_md, - octave::mach_info::float_format ff) + octave::mach_info::float_format ff, + const std::string& encoding) { - return octave::stream (new octave_oprocstream (n, arg_md, ff)); + return octave::stream (new octave_oprocstream (n, arg_md, ff, encoding)); } octave_oprocstream::octave_oprocstream (const std::string& n, std::ios::openmode arg_md, - octave::mach_info::float_format ff) + octave::mach_info::float_format ff, + const std::string& encoding) : octave_stdiostream (n, octave::popen (n.c_str (), "w"), - arg_md, ff, octave::pclose) + arg_md, ff, encoding, octave::pclose) { } octave_oprocstream::~octave_oprocstream (void) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-prcstrm.h --- a/libinterp/corefcn/oct-prcstrm.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-prcstrm.h Fri Jul 12 12:14:43 2019 -0400 @@ -38,7 +38,8 @@ octave_iprocstream (const std::string& n, std::ios::openmode arg_md = std::ios::in, octave::mach_info::float_format flt_fmt - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); // No copying! @@ -49,7 +50,8 @@ static octave::stream create (const std::string& n, std::ios::openmode arg_md = std::ios::in, octave::mach_info::float_format flt_fmt - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); protected: @@ -64,7 +66,8 @@ octave_oprocstream (const std::string& n, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format flt_fmt - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); // No copying! @@ -75,7 +78,8 @@ static octave::stream create (const std::string& n, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format flt_fmt - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); protected: diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-process.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/oct-process.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,88 @@ +/* + +Copyright (C) 2019 Andrew Janke + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#include +#include + +#include "oct-process.h" +#include "procstream.h" +#include "sysdep.h" +#include "oct-syscalls.h" + +namespace octave +{ + OCTINTERP_API + process_execution_result + process_execution_result::of_success (int exit_status, + const std::string& stdout_output) + { + return process_execution_result (0, exit_status, stdout_output, ""); + } + + OCTINTERP_API + process_execution_result + process_execution_result::of_error (int status, const std::string& err_msg) + { + return process_execution_result (status, -1, "", err_msg); + } + + // Execute a shell command, returning results as a C++ object + OCTINTERP_API + process_execution_result + run_command_and_return_output (const std::string& cmd_str) + { + iprocstream cmd (cmd_str.c_str ()); + + if (! cmd) + { + std::string msg = "unable to start subprocess for '" + cmd_str + "'"; + + return process_execution_result::of_error (-1, msg); + } + + std::ostringstream output_buf; + + char ch; + + for (;;) + { + if (cmd.get (ch)) + output_buf.put (ch); + else + { + if (! cmd.eof () && errno == EAGAIN) + cmd.clear (); + else + break; + } + } + + int cmd_status = cmd.close (); + + if (sys::wifexited (cmd_status)) + cmd_status = sys::wexitstatus (cmd_status); + else + cmd_status = 127; + + return process_execution_result::of_success (cmd_status, output_buf.str ()); + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-process.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/oct-process.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,82 @@ +/* + +Copyright (C) 2019 Andrew Janke + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (oct_process_h) +#define oct_process_h 1 + +#include "octave-config.h" + +#include + +namespace octave +{ + OCTINTERP_API + class + process_execution_result + { + public: + + process_execution_result (void) + : m_status (-1), m_err_msg (), m_exit_status (-1), m_stdout_output () + { } + + process_execution_result (int status, int exit_status, + const std::string& stdout_output, + const std::string& err_msg) + : m_status (status), m_err_msg (err_msg), m_exit_status (exit_status), + m_stdout_output (stdout_output) + { } + + static process_execution_result + of_success (int exit_status, const std::string& stdout_output); + + static process_execution_result + of_error (int status, const std::string& err_msg); + + int status (void) const { return m_status; } + + int exit_status (void) const { return m_exit_status; } + + std::string err_msg (void) const { return m_err_msg; } + + std::string stdout_output (void) const { return m_stdout_output; } + + private: + + // Launch status of the process, 0 for success, nonzero for error. + int m_status; + + // Error message if executing command failed. + std::string m_err_msg; + + // Exit status of the process. + int m_exit_status; + + // Collected stdout output of the process. + std::string m_stdout_output; + }; + + extern OCTINTERP_API process_execution_result + run_command_and_return_output (const std::string& cmd_str); +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-stdstrm.h --- a/libinterp/corefcn/oct-stdstrm.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-stdstrm.h Fri Jul 12 12:14:43 2019 -0400 @@ -40,9 +40,10 @@ std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8", typename BUF_T::close_fcn cf = BUF_T::file_close) - : octave::base_stream (m, ff), nm (n), md (m), - s (f ? new STREAM_T (f, cf) : nullptr), fnum (fid) + : octave::base_stream (m, ff, encoding), m_name (n), m_mode (m), + m_stream (f ? new STREAM_T (f, cf) : nullptr), fnum (fid) { } // No copying! @@ -54,48 +55,66 @@ // Position a stream at OFFSET relative to ORIGIN. int seek (off_t offset, int origin) - { return s ? s->seek (offset, origin) : -1; } + { + return m_stream ? m_stream->seek (offset, origin) : -1; + } // Return current stream position. - off_t tell (void) { return s ? s->tell () : -1; } + off_t tell (void) { return m_stream ? m_stream->tell () : -1; } // Return nonzero if EOF has been reached on this stream. - bool eof (void) const { return s ? s->eof () : true; } + bool eof (void) const { return m_stream ? m_stream->eof () : true; } // The name of the file. - std::string name (void) const { return nm; } + std::string name (void) const { return m_name; } - std::istream * input_stream (void) { return (md & std::ios::in) ? s : nullptr; } + std::istream * input_stream (void) + { + return (m_mode & std::ios::in) ? m_stream : nullptr; + } - std::ostream * output_stream (void) { return (md & std::ios::out) ? s : nullptr; } + std::ostream * output_stream (void) + { + return (m_mode & std::ios::out) ? m_stream : nullptr; + } // FIXME: should not have to cast away const here. BUF_T * rdbuf (void) const - { return s ? (const_cast (s))->rdbuf () : nullptr; } + { + return m_stream ? (const_cast (m_stream))->rdbuf () : nullptr; + } int file_number (void) const { return fnum; } - bool bad (void) const { return s ? s->bad () : true; } + bool bad (void) const { return m_stream ? m_stream->bad () : true; } - void clear (void) { if (s) s->clear (); } + void clear (void) + { + if (m_stream) + m_stream->clear (); + } - void do_close (void) { if (s) s->stream_close (); } + void do_close (void) + { + if (m_stream) + m_stream->stream_close (); + } protected: - std::string nm; + std::string m_name; - std::ios::openmode md; + std::ios::openmode m_mode; - STREAM_T *s; + STREAM_T *m_stream; // The file number associated with this file. int fnum; - ~octave_tstdiostream (void) { delete s; } + ~octave_tstdiostream (void) { delete m_stream; } }; class @@ -108,18 +127,20 @@ std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8", c_file_ptr_buf::close_fcn cf = c_file_ptr_buf::file_close) : octave_tstdiostream - (n, f, f ? fileno (f) : -1, m, ff, cf) { } + (n, f, f ? fileno (f) : -1, m, ff, encoding, cf) { } static octave::stream create (const std::string& n, FILE *f = nullptr, std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8", c_file_ptr_buf::close_fcn cf = c_file_ptr_buf::file_close) { - return octave::stream (new octave_stdiostream (n, f, m, ff, cf)); + return octave::stream (new octave_stdiostream (n, f, m, ff, encoding, cf)); } // No copying! @@ -145,19 +166,22 @@ std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8", c_zfile_ptr_buf::close_fcn cf = c_zfile_ptr_buf::file_close) : octave_tstdiostream - (n, f, fid, m, ff, cf) { } + (n, f, fid, m, ff, encoding, cf) { } static octave::stream create (const std::string& n, gzFile f = nullptr, int fid = 0, std::ios::openmode m = std::ios::in | std::ios::out, octave::mach_info::float_format ff = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8", c_zfile_ptr_buf::close_fcn cf = c_zfile_ptr_buf::file_close) { - return octave::stream (new octave_zstdiostream (n, f, fid, m, ff, cf)); + return octave::stream (new octave_zstdiostream (n, f, fid, m, ff, encoding, + cf)); } // No copying! diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-stream.cc --- a/libinterp/corefcn/oct-stream.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-stream.cc Fri Jul 12 12:14:43 2019 -0400 @@ -54,6 +54,7 @@ #include "octave.h" #include "oct-iostrm.h" #include "oct-stdstrm.h" +#include "oct-string.h" #include "oct-stream.h" #include "ov.h" #include "ovl.h" @@ -64,7 +65,7 @@ { // Programming Note: There are two very different error functions used // in the stream code. When invoked with "error (...)" the member - // function from octave::stream or octave::base_stream is called. This + // function from stream or base_stream is called. This // function sets the error state on the stream AND returns control to // the caller. The caller must then return a value at the end of the // function. When invoked with "::error (...)" the exception-based @@ -1640,11 +1641,11 @@ // or 64. For floating point values, bitwidth may be 32 or 64. int bitwidth; - // The class of characters in a `[' or `^' format. + // The class of characters in a '[' or '^' format. std::string char_class; // Type of conversion - // -- `d', `u', `f', `n', `s', `q', `c', `%', `C', `D', `[' or `^'. + // -- 'd', 'u', 'f', 'n', 's', 'q', 'c', '%', 'C', 'D', '[' or '^'. char type; // TRUE if we are not storing the result of this conversion. @@ -1774,7 +1775,8 @@ { public: - textscan (const std::string& who_arg = "textscan"); + textscan (const std::string& who_arg = "textscan", + const std::string& encoding = "utf-8"); // No copying! @@ -1796,6 +1798,8 @@ // What function name should be shown when reporting errors. std::string who; + std::string m_encoding; + std::string buf; // Three cases for delim_table and delim_list @@ -2302,13 +2306,13 @@ if (mask[ch]++ == 0) retval[out++] = ch; else if (ch != '-') - warning_with_id ("octave:textscan-pattern", + warning_with_id ("Octave:textscan-pattern", "%s: [...] contains two '%c's", who.c_str (), ch); if (prev == '-' && mask['-'] >= 2) warning_with_id - ("octave:textscan-pattern", + ("Octave:textscan-pattern", "%s: [...] contains two '-'s outside range expressions", who.c_str ()); } @@ -2421,8 +2425,8 @@ std::cerr << elt->type << "\n"; std::cerr - << "char_class: `" << undo_string_escapes (elt->char_class) << "'\n" - << "text: `" << undo_string_escapes (elt->text) << "'\n\n"; + << "char_class: '" << undo_string_escapes (elt->char_class) << "'\n" + << "text: '" << undo_string_escapes (elt->text) << "'\n\n"; } } @@ -2505,13 +2509,13 @@ return retval; // May have returned 4 above. } - textscan::textscan (const std::string& who_arg) - : who (who_arg), buf (), whitespace_table (), delim_table (), - delims (), comment_style (), comment_len (0), comment_char (-2), - buffer_size (0), date_locale (), inf_nan (init_inf_nan ()), - empty_value (numeric_limits::NaN ()), exp_chars ("edED"), - header_lines (0), treat_as_empty (), treat_as_empty_len (0), - whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'), + textscan::textscan (const std::string& who_arg, const std::string& encoding) + : who (who_arg), m_encoding (encoding), buf (), whitespace_table (), + delim_table (), delims (), comment_style (), comment_len (0), + comment_char (-2), buffer_size (0), date_locale (), + inf_nan (init_inf_nan ()), empty_value (numeric_limits::NaN ()), + exp_chars ("edED"), header_lines (0), treat_as_empty (), + treat_as_empty_len (0), whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'), return_on_error (1), collect_output (false), multiple_delims_as_one (false), default_exp (true), lines (0) { } @@ -3147,6 +3151,10 @@ ends[i++] = eol2; val = textscan::read_until (is, delim_list, ends); } + + // convert from codepage + if (m_encoding.compare ("utf-8")) + val = string::u8_from_encoding ("textscan", val, m_encoding); } // Return in VAL the run of characters from IS contained in PATTERN. @@ -3194,6 +3202,10 @@ is.get_undelim (); } } + + // convert from codepage + if (m_encoding.compare ("utf-8")) + val = string::u8_from_encoding ("textscan", val, m_encoding); } // Read from IS into VAL a string of the next fmt.width characters, @@ -3216,6 +3228,10 @@ break; } } + + // convert from codepage + if (m_encoding.compare ("utf-8")) + val = string::u8_from_encoding ("textscan", val, m_encoding); } // Read a single '%...' conversion and place it in position ROW of OV. @@ -3956,22 +3972,22 @@ void base_stream::error (const std::string& msg) { - fail = true; - errmsg = msg; + m_fail = true; + m_errmsg = msg; } void base_stream::error (const std::string& who, const std::string& msg) { - fail = true; - errmsg = who + ": " + msg; + m_fail = true; + m_errmsg = who + ": " + msg; } void base_stream::clear (void) { - fail = false; - errmsg = ""; + m_fail = false; + m_errmsg = ""; } void @@ -4253,14 +4269,40 @@ { // Limit input to fmt.width characters by reading into a // temporary stringstream buffer. - std::string tmp; + std::string strbuf; + + auto orig_pos = is.tellg (); is.width (fmt.width); - is >> tmp; - - std::istringstream ss (tmp); + is >> strbuf; + + std::istringstream ss (strbuf); octave_scan_1 (ss, fmt, valptr); + + if (! ss.eof ()) + { + // If fewer characters than width were used to read a number then + // the original istream object positioning is incorrect. + // Rather than attempt to update istream state and positioning, + // just redo the '>>' operation with the correct width so that + // all flags get set correctly. + + is.clear (); // Clear EOF, FAILBIT, BADBIT + is.seekg (orig_pos, is.beg); + + int chars_read = ss.tellg (); + if (chars_read > 0) + { + is.width (chars_read); + is >> strbuf; + } + } + + // If pattern failed to match then propagate fail bit to 'is' stream. + if (ss.fail ()) + is.setstate (std::ios::failbit); + } else octave_scan_1 (is, fmt, valptr); @@ -4529,6 +4571,8 @@ #define FINISH_CHARACTER_CONVERSION() \ do \ { \ + if (encoding ().compare ("utf-8")) \ + tmp = string::u8_from_encoding (who, tmp, encoding ()); \ width = tmp.length (); \ \ if (is) \ @@ -4847,11 +4891,11 @@ break; case 'p': - error ("%s: unsupported format specifier", who.c_str ()); + error (who, "unsupported format specifier"); break; default: - error ("%s: internal format error", who.c_str ()); + error (who, "internal format error"); break; } @@ -4859,7 +4903,7 @@ { break; } - else if (! is) + else if (is.eof () || ! is) { if (all_char_conv) { @@ -4901,7 +4945,10 @@ // If it looks like we have a matching failure, then // reset the failbit in the stream state. if (is.rdstate () & std::ios::failbit) - is.clear (is.rdstate () & (~std::ios::failbit)); + { + error (who, "format failed to match"); + is.clear (is.rdstate () & (~std::ios::failbit)); + } // FIXME: is this the right thing to do? if (application::interactive () @@ -4920,7 +4967,7 @@ } else { - error ("%s: internal format error", who.c_str ()); + error (who, "internal format error"); break; } @@ -4952,15 +4999,12 @@ } } - if (ok ()) - { - mval.resize (final_nr, final_nc, 0.0); - - retval = mval; - - if (all_char_conv) - retval = retval.convert_to_str (false, true); - } + mval.resize (final_nr, final_nc, 0.0); + + retval = mval; + + if (all_char_conv) + retval = retval.convert_to_str (false, true); return retval; } @@ -5185,11 +5229,11 @@ break; case 'p': - error ("%s: unsupported format specifier", who.c_str ()); + error (who, "unsupported format specifier"); break; default: - error ("%s: internal format error", who.c_str ()); + error (who, "internal format error"); break; } } @@ -5306,7 +5350,7 @@ invalid_operation (who, "reading"); else { - textscan scanner (who); + textscan scanner (who, encoding ()); retval = scanner.scan (*isp, fmt, ntimes, options, read_count); } @@ -5535,15 +5579,15 @@ switch (nsa) { case 2: - retval = octave::format (os, fmt, sa_1, sa_2, arg); + retval = format (os, fmt, sa_1, sa_2, arg); break; case 1: - retval = octave::format (os, fmt, sa_1, arg); + retval = format (os, fmt, sa_1, arg); break; case 0: - retval = octave::format (os, fmt, arg); + retval = format (os, fmt, arg); break; default: @@ -5557,7 +5601,7 @@ static size_t do_printf_string (std::ostream& os, const printf_format_elt *elt, int nsa, int sa_1, int sa_2, const std::string& arg, - const std::string& who) + const std::string& encoding, const std::string& who) { if (nsa > 2) ::error ("%s: internal error handling format", who.c_str ()); @@ -5568,12 +5612,19 @@ size_t len = arg.length (); + size_t prec = (nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec)); + + std::string print_str = prec < arg.length () ? arg.substr (0, prec) : arg; + if (encoding.compare ("utf-8")) + { + size_t src_len = print_str.length (); + print_str = string::u8_to_encoding (who, print_str, encoding); + len -= src_len - print_str.length (); + } + size_t fw = (nsa > 0 ? sa_1 : (elt->fw == -1 ? len : elt->fw)); - size_t prec = (nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec)); - - os << std::setw (fw) - << (left ? std::left : std::right) - << (prec < len ? arg.substr (0, prec) : arg); + + os << std::setw (fw) << (left ? std::left : std::right) << print_str; return len > fw ? len : fw; } @@ -5764,7 +5815,7 @@ default: // Note: error is member fcn from base_stream, not ::error. // This error does not halt execution so "return ..." must exist. - error ("%s: invalid format specifier", who.c_str ()); + error (who, "invalid format specifier"); return -1; break; } @@ -5846,7 +5897,8 @@ std::string sval = val.string_value (); retval += do_printf_string (os, elt, nsa, sa_1, - sa_2, sval, who); + sa_2, sval, encoding (), + who); } else retval += do_numeric_printf_conv (os, elt, nsa, sa_1, @@ -5871,7 +5923,7 @@ if (! os) { - error ("%s: write error", who.c_str ()); + error (who, "write error"); break; } @@ -5886,10 +5938,13 @@ } int - base_stream::printf (const std::string& fmt, + base_stream::printf (std::string fmt, const octave_value_list& args, const std::string& who) { + if (encoding ().compare ("utf-8")) + fmt = string::u8_to_encoding (who, fmt, encoding ()); + printf_format_list fmt_list (fmt); if (fmt_list.num_conversions () == -1) @@ -5914,7 +5969,7 @@ os << s; if (! os) - error ("%s: write error", who.c_str ()); + error (who, "write error"); else { // FIXME: why does this seem to be necessary? @@ -5928,7 +5983,7 @@ if (os) retval = 0; else - error ("%s: write error", who.c_str ()); + error (who, "write error"); } } @@ -5940,9 +5995,9 @@ std::string base_stream::error (bool clear_err, int& err_num) { - err_num = (fail ? -1 : 0); - - std::string tmp = errmsg; + err_num = (m_fail ? -1 : 0); + + std::string tmp = m_errmsg; if (clear_err) clear (); @@ -5958,37 +6013,37 @@ } stream::stream (base_stream *bs) - : rep (bs) - { - if (rep) - rep->count = 1; + : m_rep (bs) + { + if (m_rep) + m_rep->m_count = 1; } stream::~stream (void) { - if (rep && --rep->count == 0) - delete rep; + if (m_rep && --m_rep->m_count == 0) + delete m_rep; } stream::stream (const stream& s) - : rep (s.rep) - { - if (rep) - rep->count++; + : m_rep (s.m_rep) + { + if (m_rep) + m_rep->m_count++; } stream& stream::operator = (const stream& s) { - if (rep != s.rep) - { - if (rep && --rep->count == 0) - delete rep; - - rep = s.rep; - - if (rep) - rep->count++; + if (m_rep != s.m_rep) + { + if (m_rep && --m_rep->m_count == 0) + delete m_rep; + + m_rep = s.m_rep; + + if (m_rep) + m_rep->m_count++; } return *this; @@ -6000,7 +6055,7 @@ int retval = -1; if (stream_ok ()) - retval = rep->flush (); + retval = m_rep->flush (); return retval; } @@ -6011,7 +6066,7 @@ std::string retval; if (stream_ok ()) - retval = rep->getl (max_len, err, who); + retval = m_rep->getl (max_len, err, who); return retval; } @@ -6046,7 +6101,7 @@ std::string retval; if (stream_ok ()) - retval = rep->gets (max_len, err, who); + retval = m_rep->gets (max_len, err, who); return retval; } @@ -6081,7 +6136,7 @@ off_t retval = -1; if (stream_ok ()) - retval = rep->skipl (count, err, who); + retval = m_rep->skipl (count, err, who); return retval; } @@ -6126,31 +6181,31 @@ clearerr (); // Find current position so we can return to it if needed. - off_t orig_pos = rep->tell (); + off_t orig_pos = m_rep->tell (); // Move to end of file. If successful, find the offset of the end. - status = rep->seek (0, SEEK_END); + status = m_rep->seek (0, SEEK_END); if (status == 0) { - off_t eof_pos = rep->tell (); + off_t eof_pos = m_rep->tell (); if (origin == SEEK_CUR) { // Move back to original position, otherwise we will be seeking // from the end of file which is probably not the original // location. - rep->seek (orig_pos, SEEK_SET); + m_rep->seek (orig_pos, SEEK_SET); } // Attempt to move to desired position; may be outside bounds of // existing file. - status = rep->seek (offset, origin); + status = m_rep->seek (offset, origin); if (status == 0) { // Where are we after moving to desired position? - off_t desired_pos = rep->tell (); + off_t desired_pos = m_rep->tell (); // I don't think save_pos can be less than zero, // but we'll check anyway... @@ -6158,7 +6213,7 @@ { // Seek outside bounds of file. // Failure should leave position unchanged. - rep->seek (orig_pos, SEEK_SET); + m_rep->seek (orig_pos, SEEK_SET); status = -1; } @@ -6167,7 +6222,7 @@ { // Seeking to the desired position failed. // Move back to original position and return failure status. - rep->seek (orig_pos, SEEK_SET); + m_rep->seek (orig_pos, SEEK_SET); status = -1; } @@ -6239,7 +6294,7 @@ off_t retval = -1; if (stream_ok ()) - retval = rep->tell (); + retval = m_rep->tell (); return retval; } @@ -6256,7 +6311,7 @@ bool retval = false; if (stream_ok ()) - retval = rep->is_open (); + retval = m_rep->is_open (); return retval; } @@ -6265,7 +6320,7 @@ stream::close (void) { if (stream_ok ()) - rep->close (); + m_rep->close (); } // FIXME: maybe these should be defined in lo-ieee.h? @@ -7050,7 +7105,7 @@ octave_value retval; if (stream_ok ()) - retval = rep->scanf (fmt, size, count, who); + retval = m_rep->scanf (fmt, size, count, who); return retval; } @@ -7085,7 +7140,7 @@ octave_value_list retval; if (stream_ok ()) - retval = rep->oscanf (fmt, who); + retval = m_rep->oscanf (fmt, who); return retval; } @@ -7119,7 +7174,7 @@ const std::string& who, octave_idx_type& count) { return (stream_ok () - ? rep->do_textscan (fmt, ntimes, options, who, count) + ? m_rep->do_textscan (fmt, ntimes, options, who, count) : octave_value ()); } @@ -7130,7 +7185,7 @@ int retval = -1; if (stream_ok ()) - retval = rep->printf (fmt, args, who); + retval = m_rep->printf (fmt, args, who); return retval; } @@ -7165,7 +7220,7 @@ int retval = -1; if (stream_ok ()) - retval = rep->puts (s, who); + retval = m_rep->puts (s, who); return retval; } @@ -7197,7 +7252,7 @@ int retval = -1; if (stream_ok ()) - retval = rep->eof (); + retval = m_rep->eof (); return retval; } @@ -7208,7 +7263,7 @@ std::string retval = "invalid stream object"; if (stream_ok (false)) - retval = rep->error (clear, err_num); + retval = m_rep->error (clear, err_num); return retval; } @@ -7219,7 +7274,7 @@ std::string retval; if (stream_ok ()) - retval = rep->name (); + retval = m_rep->name (); return retval; } @@ -7230,7 +7285,7 @@ int retval = 0; if (stream_ok ()) - retval = rep->mode (); + retval = m_rep->mode (); return retval; } @@ -7241,7 +7296,7 @@ mach_info::float_format retval = mach_info::flt_fmt_unknown; if (stream_ok ()) - retval = rep->float_format (); + retval = m_rep->float_format (); return retval; } @@ -7285,7 +7340,7 @@ } stream_list::stream_list (interpreter& interp) - : list (), lookup_cache (list.end ()), m_stdin_file (-1), + : m_list (), m_lookup_cache (m_list.end ()), m_stdin_file (-1), m_stdout_file (-1), m_stderr_file (-1) { stream stdin_stream = octave_istream::create (&std::cin, "stdin"); @@ -7323,8 +7378,8 @@ // Should we test for // - // (list.find (stream_number) != list.end () - // && list[stream_number].is_open ()) + // (m_list.find (stream_number) != m_list.end () + // && m_list[stream_number].is_open ()) // // and respond with "error ("internal error: ...")"? It should not // happen except for some bug or if the user has opened a stream with @@ -7334,10 +7389,10 @@ // overwrite this entry, although the wrong entry might have done harm // before. - if (list.size () >= list.max_size ()) + if (m_list.size () >= m_list.max_size ()) ::error ("could not create file id"); - list[stream_number] = os; + m_list[stream_number] = os; return stream_number; } @@ -7359,17 +7414,17 @@ if (fid < 0) err_invalid_file_id (fid, who); - if (lookup_cache != list.end () && lookup_cache->first == fid) - retval = lookup_cache->second; + if (m_lookup_cache != m_list.end () && m_lookup_cache->first == fid) + retval = m_lookup_cache->second; else { - ostrl_map::const_iterator iter = list.find (fid); - - if (iter == list.end ()) + ostrl_map::const_iterator iter = m_list.find (fid); + + if (iter == m_list.end ()) err_invalid_file_id (fid, who); retval = iter->second; - lookup_cache = iter; + m_lookup_cache = iter; } return retval; @@ -7389,14 +7444,14 @@ if (fid < 3) err_invalid_file_id (fid, who); - auto iter = list.find (fid); - - if (iter == list.end ()) + auto iter = m_list.find (fid); + + if (iter == m_list.end ()) err_invalid_file_id (fid, who); stream os = iter->second; - list.erase (iter); - lookup_cache = list.end (); + m_list.erase (iter); + m_lookup_cache = m_list.end (); // FIXME: is this check redundant? if (! os.is_valid ()) @@ -7432,11 +7487,11 @@ if (flush) { // Flush stdout and stderr. - list[1].flush (); - list[2].flush (); - } - - for (auto iter = list.begin (); iter != list.end (); ) + m_list[1].flush (); + m_list[2].flush (); + } + + for (auto iter = m_list.begin (); iter != m_list.end (); ) { int fid = iter->first; if (fid < 3) // Don't delete stdin, stdout, stderr @@ -7458,35 +7513,35 @@ continue; } - // Normal file handle. Close and delete from list. + // Normal file handle. Close and delete from m_list. if (os.is_valid ()) os.close (); - list.erase (iter++); - } - - lookup_cache = list.end (); + m_list.erase (iter++); + } + + m_lookup_cache = m_list.end (); } string_vector stream_list::get_info (int fid) const { - string_vector retval (3); + string_vector retval (4); if (fid < 0) return retval; stream os; - if (lookup_cache != list.end () && lookup_cache->first == fid) - os = lookup_cache->second; + if (m_lookup_cache != m_list.end () && m_lookup_cache->first == fid) + os = m_lookup_cache->second; else { - ostrl_map::const_iterator iter = list.find (fid); - - if (iter == list.end ()) + ostrl_map::const_iterator iter = m_list.find (fid); + + if (iter == m_list.end ()) return retval; os = iter->second; - lookup_cache = iter; + m_lookup_cache = iter; } if (! os.is_valid ()) @@ -7495,6 +7550,7 @@ retval(0) = os.name (); retval(1) = stream::mode_as_string (os.mode ()); retval(2) = mach_info::float_format_as_string (os.float_format ()); + retval(3) = os.encoding (); return retval; } @@ -7522,7 +7578,7 @@ << " number mode arch name\n" << " ------ ---- ---- ----\n"; - for (const auto& fid_strm : list) + for (const auto& fid_strm : m_list) { stream os = fid_strm.second; @@ -7548,11 +7604,11 @@ octave_value stream_list::open_file_numbers (void) const { - Matrix retval (1, list.size (), 0.0); + Matrix retval (1, m_list.size (), 0.0); int num_open = 0; - for (const auto& fid_strm : list) + for (const auto& fid_strm : m_list) { // Skip stdin, stdout, and stderr. if (fid_strm.first > 2 && fid_strm.second) @@ -7572,7 +7628,7 @@ { std::string nm = fid.string_value (); - for (const auto& fid_strm : list) + for (const auto& fid_strm : m_list) { // stdin, stdout, and stderr are unnamed. if (fid_strm.first > 2) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-stream.h --- a/libinterp/corefcn/oct-stream.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-stream.h Fri Jul 12 12:14:43 2019 -0400 @@ -67,9 +67,10 @@ public: base_stream (std::ios::openmode arg_md = std::ios::in | std::ios::out, - mach_info::float_format ff = mach_info::native_float_format ()) - : count (0), md (arg_md), flt_fmt (ff), fail (false), open_state (true), - errmsg () + mach_info::float_format ff = mach_info::native_float_format (), + const std::string& encoding = "utf-8") + : m_count (0), m_mode (arg_md), m_flt_fmt (ff), m_encoding (encoding), + m_fail (false), m_open_state (true), m_errmsg () { } // No copying! @@ -113,7 +114,7 @@ // Return TRUE if this stream is open. - bool is_open (void) const { return open_state; } + bool is_open (void) const { return m_open_state; } virtual void do_close (void) { } @@ -121,7 +122,7 @@ { if (is_open ()) { - open_state = false; + m_open_state = false; do_close (); } } @@ -140,7 +141,7 @@ return -1; } - bool ok (void) const { return ! fail; } + bool ok (void) const { return ! m_fail; } // Return current error message for this stream. @@ -148,9 +149,11 @@ protected: - int mode (void) const { return md; } + int mode (void) const { return m_mode; } - mach_info::float_format float_format (void) const { return flt_fmt; } + mach_info::float_format float_format (void) const { return m_flt_fmt; } + + std::string encoding (void) const { return m_encoding; } // Set current error state and set fail to TRUE. @@ -168,23 +171,26 @@ private: // A reference count. - refcount count; + refcount m_count; // The permission bits for the file. Should be some combination of // std::ios::open_mode bits. - int md; + int m_mode; // Data format. - mach_info::float_format flt_fmt; + mach_info::float_format m_flt_fmt; + + // Code page + std::string m_encoding; // TRUE if an error has occurred. - bool fail; + bool m_fail; // TRUE if this stream is open. - bool open_state; + bool m_open_state; // Should contain error message if fail is TRUE. - std::string errmsg; + std::string m_errmsg; // Functions that are defined for all input streams (input streams // are those that define is). @@ -229,7 +235,7 @@ int do_printf (printf_format_list& fmt_list, const octave_value_list& args, const std::string& who /* = "printf" */); - int printf (const std::string& fmt, const octave_value_list& args, + int printf (std::string fmt, const octave_value_list& args, const std::string& who /* = "printf" */); int puts (const std::string& s, const std::string& who /* = "puts" */); @@ -341,17 +347,17 @@ void error (const std::string& msg) { - if (rep) - rep->error (msg); + if (m_rep) + m_rep->error (msg); } void error (const char *msg) { error (std::string (msg)); } - int file_number (void) { return rep ? rep->file_number () : -1; } + int file_number (void) { return m_rep ? m_rep->file_number () : -1; } - bool is_valid (void) const { return (rep != nullptr); } + bool is_valid (void) const { return (m_rep != nullptr); } - bool ok (void) const { return rep && rep->ok (); } + bool ok (void) const { return m_rep && m_rep->ok (); } operator bool () const { return ok (); } @@ -363,31 +369,36 @@ static std::string mode_as_string (int mode); + std::string encoding (void) + { + return m_rep ? m_rep->encoding () : std::string (); + } + std::istream * input_stream (void) { - return rep ? rep->input_stream () : nullptr; + return m_rep ? m_rep->input_stream () : nullptr; } std::ostream * output_stream (void) { - return rep ? rep->output_stream () : nullptr; + return m_rep ? m_rep->output_stream () : nullptr; } - void clearerr (void) { if (rep) rep->clearerr (); } + void clearerr (void) { if (m_rep) m_rep->clearerr (); } private: // The actual representation of this stream. - base_stream *rep; + base_stream *m_rep; bool stream_ok (bool clear = true) const { bool retval = true; - if (rep) + if (m_rep) { if (clear) - rep->clear (); + m_rep->clear (); } else retval = false; @@ -397,8 +408,8 @@ void invalid_operation (const std::string& who, const char *rw) { - if (rep) - rep->invalid_operation (who, rw); + if (m_rep) + m_rep->invalid_operation (who, rw); } octave_value @@ -451,9 +462,9 @@ typedef std::map ostrl_map; - ostrl_map list; + ostrl_map m_list; - mutable ostrl_map::const_iterator lookup_cache; + mutable ostrl_map::const_iterator m_lookup_cache; int m_stdin_file; int m_stdout_file; @@ -461,17 +472,4 @@ }; } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::base_stream' instead") -typedef octave::base_stream octave_base_stream; - -OCTAVE_DEPRECATED (4.4, "use 'octave::stream' instead") -typedef octave::stream octave_stream; - -OCTAVE_DEPRECATED (4.4, "use 'octave::stream_list' instead") -typedef octave::stream_list octave_stream_list; - #endif - -#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-strstrm.cc --- a/libinterp/corefcn/oct-strstrm.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-strstrm.cc Fri Jul 12 12:14:43 2019 -0400 @@ -50,21 +50,27 @@ octave::stream octave_istrstream::create (const char *data, std::ios::openmode arg_md, - octave::mach_info::float_format flt_fmt) + octave::mach_info::float_format flt_fmt, + const std::string& encoding) { - return octave::stream (new octave_istrstream (data, arg_md, flt_fmt)); + return octave::stream (new octave_istrstream (data, arg_md, flt_fmt, + encoding)); } octave::stream octave_istrstream::create (const std::string& data, std::ios::openmode arg_md, - octave::mach_info::float_format flt_fmt) + octave::mach_info::float_format flt_fmt, + const std::string& encoding) { - return octave::stream (new octave_istrstream (data, arg_md, flt_fmt)); + return octave::stream (new octave_istrstream (data, arg_md, flt_fmt, + encoding)); } octave::stream octave_ostrstream::create (std::ios::openmode arg_md, - octave::mach_info::float_format flt_fmt) + octave::mach_info::float_format flt_fmt, + const std::string& encoding) { - return octave::stream (new octave_ostrstream (arg_md, flt_fmt)); + return octave::stream (new octave_ostrstream (arg_md, flt_fmt, + encoding)); } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/oct-strstrm.h --- a/libinterp/corefcn/oct-strstrm.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/oct-strstrm.h Fri Jul 12 12:14:43 2019 -0400 @@ -37,8 +37,9 @@ octave_base_strstream (std::ios::openmode m = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()) - : octave::base_stream (m, ff) { } + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8") + : octave::base_stream (m, ff, encoding) { } // No copying! @@ -79,14 +80,16 @@ octave_istrstream (const char *data, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()) - : octave_base_strstream (arg_md, ff), is (data) { } + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8") + : octave_base_strstream (arg_md, ff, encoding), m_istream (data) { } octave_istrstream (const std::string& data, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()) - : octave_base_strstream (arg_md, ff), is (data) { } + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8") + : octave_base_strstream (arg_md, ff, encoding), m_istream (data) { } // No copying! @@ -104,32 +107,37 @@ static octave::stream create (const char *data, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); static octave::stream create (const std::string& data, std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); // Return nonzero if EOF has been reached on this stream. - bool eof (void) const { return is.eof (); } + bool eof (void) const { return m_istream.eof (); } - std::istream * input_stream (void) { return &is; } + std::istream * input_stream (void) { return &m_istream; } std::ostream * output_stream (void) { return nullptr; } - off_t tell (void) { return is.tellg (); } - - std::streambuf * rdbuf (void) { return is ? is.rdbuf () : nullptr; } + off_t tell (void) { return m_istream.tellg (); } - bool bad (void) const { return is.bad (); } + std::streambuf * rdbuf (void) + { + return m_istream ? m_istream.rdbuf () : nullptr; + } - void clear (void) { is.clear (); } + bool bad (void) const { return m_istream.bad (); } + + void clear (void) { m_istream.clear (); } private: - std::istringstream is; + std::istringstream m_istream; }; class @@ -139,8 +147,9 @@ octave_ostrstream (std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()) - : octave_base_strstream (arg_md, ff), os () { } + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8") + : octave_base_strstream (arg_md, ff, encoding), m_ostream () { } // No copying! @@ -157,27 +166,31 @@ static octave::stream create (std::ios::openmode arg_md = std::ios::out, octave::mach_info::float_format ff - = octave::mach_info::native_float_format ()); + = octave::mach_info::native_float_format (), + const std::string& encoding = "utf-8"); // Return nonzero if EOF has been reached on this stream. - bool eof (void) const { return os.eof (); } + bool eof (void) const { return m_ostream.eof (); } std::istream * input_stream (void) { return nullptr; } - std::ostream * output_stream (void) { return &os; } + std::ostream * output_stream (void) { return &m_ostream; } - std::string str (void) { return os.str (); } + std::string str (void) { return m_ostream.str (); } - std::streambuf * rdbuf (void) { return os ? os.rdbuf () : nullptr; } + std::streambuf * rdbuf (void) + { + return m_ostream ? m_ostream.rdbuf () : nullptr; + } - bool bad (void) const { return os.bad (); } + bool bad (void) const { return m_ostream.bad (); } - void clear (void) { os.clear (); } + void clear (void) { m_ostream.clear (); } private: - std::ostringstream os; + std::ostringstream m_ostream; }; #endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/octave-link.cc --- a/libinterp/corefcn/octave-link.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/octave-link.cc Fri Jul 12 12:14:43 2019 -0400 @@ -37,7 +37,6 @@ #include "ovl.h" #include "pager.h" #include "syminfo.h" -#include "symtab.h" static int octave_readline_hook (void) @@ -69,15 +68,12 @@ { if (enabled ()) { - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("octave_link::set_workspace"); + octave::tree_evaluator& tw + = octave::__get_evaluator__ ("octave_link::set_workspace"); - octave::call_stack& cs - = octave::__get_call_stack__ ("octave_link::set_workspace"); - - instance->do_set_workspace (symtab.at_top_level (), + instance->do_set_workspace (tw.at_top_level (), instance->debugging, - cs.get_symbol_info (), true); + tw.get_symbol_info (), true); } } @@ -251,7 +247,7 @@ DEFUN (__octave_link_list_dialog__, args, , doc: /* -*- texinfo -*- -@deftypefn {} {} __octave_link_list_dialog__ (@var{list}, @var{mode}, @var{size}, @var{intial}, @var{name}, @var{prompt}, @var{ok_string}, @var{cancel_string}) +@deftypefn {} {} __octave_link_list_dialog__ (@var{list}, @var{mode}, @var{size}, @var{initial}, @var{name}, @var{prompt}, @var{ok_string}, @var{cancel_string}) Undocumented internal function. @end deftypefn */) { @@ -465,9 +461,7 @@ warning ("openvar: GUI is not running, can't start Variable Editor"); else { - octave::symbol_scope scope = interp.require_current_scope ("openvar"); - - octave_value val = scope.varval (name); + octave_value val = interp.varval (name); if (val.is_undefined ()) error ("openvar: '%s' is not a variable", name.c_str ()); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/octave-link.h --- a/libinterp/corefcn/octave-link.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/octave-link.h Fri Jul 12 12:14:43 2019 -0400 @@ -104,6 +104,14 @@ return retval; } + template + static void + post_event (F&& fcn, Args&&... args) + { + if (enabled ()) + instance->do_post_event (fcn, std::forward (args)...); + } + template static void post_event (T *obj, void (T::*method) (Params...), Args&&... args) @@ -465,6 +473,12 @@ void do_process_events (void); void do_discard_events (void); + template + void do_post_event (F&& fcn, Args&&... args) + { + gui_event_queue.add (fcn, std::forward (args)...); + } + template void do_post_event (T *obj, void (T::*method) (Params...), Args&&... args) { diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/pager.cc --- a/libinterp/corefcn/pager.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/pager.cc Fri Jul 12 12:14:43 2019 -0400 @@ -58,7 +58,7 @@ if (pid > 0) { - if (octave::sys::wifexited (status) || octave::sys::wifsignaled (status)) + if (sys::wifexited (status) || sys::wifsignaled (status)) { // Avoid warning() since that will put us back in the pager, // which would be bad news. @@ -85,9 +85,9 @@ { if (s) { - int available_rows = octave::command_editor::terminal_rows () - 2; + int available_rows = command_editor::terminal_rows () - 2; - int cols = octave::command_editor::terminal_cols (); + int cols = command_editor::terminal_cols (); int count = 0; @@ -124,8 +124,7 @@ int pager_buf::sync (void) { - octave::output_system& output_sys - = octave::__get_output_system__ ("pager_buf::sync"); + output_system& output_sys = __get_output_system__ ("pager_buf::sync"); char *buf = eback (); @@ -426,13 +425,10 @@ { m_external_pager = new oprocstream (pgr.c_str ()); - if (m_external_pager) - { - octave::child_list& kids = m_interpreter.get_child_list (); + child_list& kids = m_interpreter.get_child_list (); - kids.insert (m_external_pager->pid (), - pager_event_handler); - } + kids.insert (m_external_pager->pid (), + pager_event_handler); } } @@ -612,19 +608,44 @@ return ovl (); } -DEFUN (terminal_size, , , +DEFUN (terminal_size, args, , doc: /* -*- texinfo -*- @deftypefn {} {} terminal_size () -Return a two-element row vector containing the current size of the terminal -window in characters (rows and columns). +Query or set the size of the terminal window. If called with no +arguments, return a two-element row vector containing the current size +of the terminal window in characters (rows and columns). If called with +a two-element vector of integer values, set the terminal size and return +the previous setting. Setting the size manually should not be needed +when using readline for command-line editing. @seealso{list_in_columns} @end deftypefn */) { + int nargin = args.length (); + + if (nargin > 1) + print_usage (); + RowVector size (2, 0.0); size(0) = octave::command_editor::terminal_rows (); size(1) = octave::command_editor::terminal_cols (); + if (nargin == 1) + { + Matrix m = args(0).xmatrix_value ("argument must be a 2-element array"); + + if (m.numel () != 2) + error ("terminal_size: argument must be a 2-element array"); + + int rows = octave::math::x_nint (m(0)); + int cols = octave::math::x_nint (m(1)); + + if (rows <= 0 || cols <= 0) + error ("terminal_size: rows and columns must be positive integers"); + + octave::command_editor::set_screen_size (rows, cols); + } + return ovl (size); } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/pager.h --- a/libinterp/corefcn/pager.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/pager.h Fri Jul 12 12:14:43 2019 -0400 @@ -307,29 +307,6 @@ extern std::ostream& __diary__ (void); } -#if defined (OCTAVE_USE_DEPRECATED_FUNCTIONS) - -OCTAVE_DEPRECATED (4.4, "use 'octave::diary_buf' instead") -typedef octave::diary_buf octave_diary_buf; - -OCTAVE_DEPRECATED (4.4, "use 'octave::diary_stream' instead") -typedef octave::diary_stream octave_diary_stream; - -OCTAVE_DEPRECATED (4.4, "use 'octave::pager_buf' instead") -typedef octave::pager_buf octave_pager_buf; - -OCTAVE_DEPRECATED (4.4, "use 'octave::pager_stream' instead") -typedef octave::pager_stream octave_pager_stream; - -OCTAVE_DEPRECATED (4.4, "use 'octave::flush_stdout' instead") -static inline void -flush_octave_stdout (void) -{ - return octave::flush_stdout (); -} - -#endif - #define octave_stdout (octave::__stdout__ ()) #define octave_diary (octave::__diary__ ()) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/pr-flt-fmt.h --- a/libinterp/corefcn/pr-flt-fmt.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/pr-flt-fmt.h Fri Jul 12 12:14:43 2019 -0400 @@ -47,10 +47,10 @@ public: float_format (int w = 0, int p = output_precision (), int f = 0) - : fw (w), ex (0), prec (p), fmt (f), up (0), sp (0) { } + : m_fw (w), m_ex (0), m_prec (p), m_fmt (f), m_up (0), m_sp (0) { } float_format (int w, int e, int p, int f) - : fw (w), ex (e), prec (p), fmt (f), up (0), sp (0) { } + : m_fw (w), m_ex (e), m_prec (p), m_fmt (f), m_up (0), m_sp (0) { } float_format (const float_format&) = default; @@ -60,53 +60,114 @@ float_format& scientific (void) { - fmt = std::ios::scientific; + m_fmt = std::ios::scientific; return *this; } float_format& fixed (void) { - fmt = std::ios::fixed; + m_fmt = std::ios::fixed; return *this; } float_format& general (void) { - fmt = 0; + m_fmt = 0; return *this; } float_format& uppercase (void) { - up = std::ios::uppercase; + m_up = std::ios::uppercase; return *this; } float_format& lowercase (void) { - up = 0; + m_up = 0; return *this; } float_format& precision (int p) { - prec = p; + m_prec = p; return *this; } float_format& width (int w) { - fw = w; + m_fw = w; + return *this; + } + + float_format& exponent_width (int w) + { + m_ex = w; return *this; } float_format& trailing_zeros (bool tz = true) { - sp = (tz ? std::ios::showpoint : 0); + m_sp = (tz ? std::ios::showpoint : 0); return *this; } + std::ios::fmtflags format_flags (void) const + { + return static_cast (m_fmt | m_up | m_sp); + } + + int format (void) const + { + return m_fmt; + } + + bool is_scientific (void) const + { + return m_fmt == std::ios::scientific; + } + + bool is_fixed (void) const + { + return m_fmt == std::ios::fixed; + } + + bool is_general (void) const + { + return m_fmt == 0; + } + + bool is_uppercase (void) const + { + return m_up == std::ios::uppercase; + } + + bool is_lowercase (void) const + { + return m_up == 0; + } + + int precision (void) const + { + return m_prec; + } + + int width (void) const + { + return m_fw; + } + + int exponent_width (void) const + { + return m_ex; + } + + bool show_trailing_zeros (void) const + { + return m_sp == std::ios::showpoint; + } + template friend std::ostream& operator << (std::ostream& os, const pr_engineering_float& pef); @@ -119,23 +180,25 @@ friend std::ostream& operator << (std::ostream& os, const pr_rational_float& prf); +private: + // Field width. Zero means as wide as necessary. - int fw; + int m_fw; // Exponent Field width. Zero means as wide as necessary. - int ex; + int m_ex; // Precision. - int prec; + int m_prec; // Format. - int fmt; + int m_fmt; // E or e. - int up; + int m_up; // Show trailing zeros. - int sp; + int m_sp; }; class @@ -169,8 +232,8 @@ void set_precision (int prec) { - m_real_fmt.prec = prec; - m_imag_fmt.prec = prec; + m_real_fmt.precision (prec); + m_imag_fmt.precision (prec); } private: diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/pr-output.cc --- a/libinterp/corefcn/pr-output.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/pr-output.cc Fri Jul 12 12:14:43 2019 -0400 @@ -188,14 +188,13 @@ float_format real_fmt = pef.m_ff; - if (real_fmt.fw >= 0) - os << std::setw (real_fmt.fw - real_fmt.ex); - - if (real_fmt.prec >= 0) - os << std::setprecision (real_fmt.prec); - - os.flags (static_cast - (real_fmt.fmt | real_fmt.up | real_fmt.sp)); + if (real_fmt.width () >= 0) + os << std::setw (real_fmt.width () - real_fmt.exponent_width ()); + + if (real_fmt.precision () >= 0) + os << std::setprecision (real_fmt.precision ()); + + os.flags (real_fmt.format_flags ()); os << pef.mantissa (); @@ -208,7 +207,8 @@ else os << std::setw (0) << "e+"; - os << std::setw (real_fmt.ex - 2) << std::setfill ('0') << ex; + os << std::setw (real_fmt.exponent_width () - 2) + << std::setfill ('0') << ex; return os; } @@ -221,14 +221,13 @@ float_format real_fmt = pff.m_ff; - if (real_fmt.fw >= 0) - os << std::setw (real_fmt.fw); - - if (real_fmt.prec >= 0) - os << std::setprecision (real_fmt.prec); - - os.flags (static_cast - (real_fmt.fmt | real_fmt.up | real_fmt.sp)); + if (real_fmt.width () >= 0) + os << std::setw (real_fmt.width ()); + + if (real_fmt.precision () >= 0) + os << std::setprecision (real_fmt.precision ()); + + os.flags (real_fmt.format_flags ()); os << pff.m_val; @@ -243,14 +242,13 @@ float_format real_fmt = prf.m_ff; - int fw = (rat_string_len > 0 ? rat_string_len : real_fmt.fw); + int fw = (rat_string_len > 0 ? rat_string_len : real_fmt.width ()); std::string s = rational_approx (prf.m_val, fw); if (fw >= 0) os << std::setw (fw); - os.flags (static_cast - (real_fmt.fmt | real_fmt.up | real_fmt.sp)); + os.flags (real_fmt.format_flags ()); if (fw > 0 && s.length () > static_cast (fw)) os << '*'; @@ -1300,7 +1298,7 @@ // {bit,hex}_format == 1: print big-endian // {bit,hex}_format == 2: print native - int fw = fmt.fw; + int fw = fmt.width (); if (hex_format) { @@ -1755,8 +1753,8 @@ static inline int get_column_width (const float_display_format& fmt) { - int r_fw = fmt.real_format().fw; - int i_fw = fmt.imag_format().fw; + int r_fw = fmt.real_format().width (); + int i_fw = fmt.imag_format().width (); int retval = r_fw + i_fw + 2; @@ -2807,7 +2805,7 @@ { float_format r_fmt = fmt.real_format (); - pr_int (os, val, r_fmt.fw); + pr_int (os, val, r_fmt.width ()); } } } @@ -3169,17 +3167,23 @@ } /* -%!assert (rats (2.0005, 9), "4001/2000") -%!assert (rats (-2.0005, 10), "-4001/2000") -%!assert (strtrim (rats (2.0005, 30)), "4001/2000") -%!assert (pi - str2num (rats (pi, 30)), 0, 4 * eps) -%!assert (e - str2num (rats (e, 30)), 0, 4 * eps) -%!assert (rats (123, 2), " *") - %!test -%! v = 1 / double (intmax); -%! err = v - str2num (rats(v, 12)); -%! assert (err, 0, 4 * eps); +%! [old_fmt, old_spacing] = format (); +%! unwind_protect +%! format short; +%! assert (rats (2.0005, 9), "4001/2000"); +%! assert (rats (-2.0005, 10), "-4001/2000"); +%! assert (strtrim (rats (2.0005, 30)), "4001/2000"); +%! assert (pi - str2num (rats (pi, 30)), 0, 4 * eps); +%! assert (e - str2num (rats (e, 30)), 0, 4 * eps); +%! assert (rats (123, 2), " *"); +%! v = 1 / double (intmax); +%! err = v - str2num (rats (v, 12)); +%! assert (err, 0, 4 * eps); +%! unwind_protect_cleanup +%! format (old_fmt); +%! format (old_spacing); +%! end_unwind_protect */ DEFUN (disp, args, nargout, diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/procstream.cc --- a/libinterp/corefcn/procstream.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/procstream.cc Fri Jul 12 12:14:43 2019 -0400 @@ -32,7 +32,7 @@ { pb_init (); - if (! pb.open (command.c_str (), mode)) + if (! m_pb.open (command.c_str (), mode)) std::ios::setstate (std::ios::badbit); } @@ -40,7 +40,7 @@ { pb_init (); - if (! pb.open (command, mode)) + if (! m_pb.open (command, mode)) std::ios::setstate (std::ios::badbit); } @@ -49,7 +49,7 @@ { clear (); - if (! pb.open (command, mode)) + if (! m_pb.open (command, mode)) std::ios::setstate (std::ios::badbit); } @@ -60,10 +60,10 @@ if (is_open ()) { - if (! pb.close ()) + if (! m_pb.close ()) std::ios::setstate (std::ios::failbit); - status = pb.wait_status (); + status = m_pb.wait_status (); } return status; diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/procstream.h --- a/libinterp/corefcn/procstream.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/procstream.h Fri Jul 12 12:14:43 2019 -0400 @@ -38,7 +38,7 @@ { public: - procstreambase (void) : pb () { pb_init (); } + procstreambase (void) : m_pb () { pb_init (); } procstreambase (const std::string& name, int mode); @@ -47,23 +47,30 @@ ~procstreambase (void) { close (); } void open (const std::string& name, int mode) - { open (name.c_str (), mode); } + { + open (name.c_str (), mode); + } void open (const char *name, int mode); - int is_open (void) const { return pb.is_open (); } + int is_open (void) const { return m_pb.is_open (); } int close (void); - pid_t pid (void) const { return pb.pid (); } + pid_t pid (void) const { return m_pb.pid (); } - int file_number (void) const { return pb.file_number (); } + int file_number (void) const { return m_pb.file_number (); } private: - octave_procbuf pb; + octave_procbuf m_pb; - void pb_init (void) { init (&pb); } + void pb_init (void) + { + // Explicit initialization of the std::ios object is needed. + // FIXME: is there a better way to organize these classes? + init (&m_pb); + } procstreambase (const procstreambase&); @@ -73,25 +80,30 @@ class OCTINTERP_API iprocstream : public std::istream, public procstreambase -// iprocstream : public procstreambase, public std::istream { public: iprocstream (void) : std::istream (nullptr), procstreambase () { } iprocstream (const std::string& name, int mode = std::ios::in) - : std::istream (nullptr), procstreambase (name, mode) { } + : std::istream (nullptr), procstreambase (name, mode) + { } iprocstream (const char *name, int mode = std::ios::in) - : std::istream (nullptr), procstreambase (name, mode) { } + : std::istream (nullptr), procstreambase (name, mode) + { } ~iprocstream (void) = default; void open (const std::string& name, int mode = std::ios::in) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } void open (const char *name, int mode = std::ios::in) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } private: @@ -103,7 +115,6 @@ class OCTINTERP_API oprocstream : public std::ostream, public procstreambase -// oprocstream : public procstreambase, public std::ostream { public: @@ -118,10 +129,14 @@ ~oprocstream (void) = default; void open (const std::string& name, int mode = std::ios::out) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } void open (const char *name, int mode = std::ios::out) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } private: @@ -133,25 +148,30 @@ class OCTINTERP_API procstream : public std::iostream, public procstreambase -// procstream : public procstreambase, public std::iostream { public: procstream (void) : std::iostream (nullptr), procstreambase () { } procstream (const std::string& name, int mode) - : std::iostream (nullptr), procstreambase (name, mode) { } + : std::iostream (nullptr), procstreambase (name, mode) + { } procstream (const char *name, int mode) - : std::iostream (nullptr), procstreambase (name, mode) { } + : std::iostream (nullptr), procstreambase (name, mode) + { } ~procstream (void) = default; void open (const std::string& name, int mode) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } void open (const char *name, int mode) - { procstreambase::open (name, mode); } + { + procstreambase::open (name, mode); + } private: diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/quad.cc --- a/libinterp/corefcn/quad.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/quad.cc Fri Jul 12 12:14:43 2019 -0400 @@ -32,10 +32,11 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter-private.h" #include "pager.h" #include "parse.h" +#include "ov.h" #include "ovl.h" -#include "ov-fcn.h" #include "unwind-prot.h" #include "utils.h" #include "variables.h" @@ -43,7 +44,7 @@ #include "Quad-opts.cc" // Global pointer for user defined function required by quadrature functions. -static octave_function *quad_fcn; +static octave_value quad_fcn; // Have we warned about imaginary values returned from user function? static bool warned_imaginary = false; @@ -59,7 +60,7 @@ octave_value_list args; args(0) = x; - if (quad_fcn) + if (quad_fcn.is_defined ()) { octave_value_list tmp; @@ -95,7 +96,7 @@ octave_value_list args; args(0) = x; - if (quad_fcn) + if (quad_fcn.is_defined ()) { octave_value_list tmp; @@ -184,24 +185,7 @@ if (call_depth > 1) error ("quad: invalid recursive call"); - std::string fcn_name; - - if (args(0).is_function_handle () || args(0).is_inline_function ()) - quad_fcn = args(0).function_value (); - else - { - fcn_name = unique_symbol_name ("__quad_fcn__"); - std::string fname = "function y = "; - fname.append (fcn_name); - fname.append ("(x) y = "); - quad_fcn = extract_function (args(0), "quad", fcn_name, fname, - "; endfunction"); - octave::symbol_table& symtab = interp.get_symbol_table (); - frame.add_method (symtab, &octave::symbol_table::clear_function, fcn_name); - } - - if (! quad_fcn) - error ("quad: FCN argument is not a valid function name or handle"); + quad_fcn = octave::get_function_handle (interp, args(0), "x"); octave_value_list retval; @@ -430,6 +414,33 @@ %! assert (nfun > 0); %!test +%! [v, ier, nfun, err] = quad (@__f, 0.001, 3); +%! assert (ier == 0 || ier == 1); +%! assert (v, 1.98194120273598, sqrt (eps)); +%! assert (nfun > 0); + +%!test +%! fstr = "x .* sin (1 ./ x) .* sqrt (abs (1 - x))"; +%! [v, ier, nfun, err] = quad (fstr, 0.001, 3); +%! assert (ier == 0 || ier == 1); +%! assert (v, 1.98194120273598, sqrt (eps)); +%! assert (nfun > 0); + +%!test +%! anon_fcn = @(x) x .* sin (1 ./ x) .* sqrt (abs (1 - x)); +%! [v, ier, nfun, err] = quad (anon_fcn, 0.001, 3); +%! assert (ier == 0 || ier == 1); +%! assert (v, 1.98194120273598, sqrt (eps)); +%! assert (nfun > 0); + +%!test +%! inline_fcn = inline ("x .* sin (1 ./ x) .* sqrt (abs (1 - x))", "x"); +%! [v, ier, nfun, err] = quad (inline_fcn, 0.001, 3); +%! assert (ier == 0 || ier == 1); +%! assert (v, 1.98194120273598, sqrt (eps)); +%! assert (nfun > 0); + +%!test %! [v, ier, nfun, err] = quad ("__f", single (0.001), single (3)); %! assert (ier == 0 || ier == 1); %! assert (v, 1.98194120273598, sqrt (eps ("single"))); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/quadcc.cc --- a/libinterp/corefcn/quadcc.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/quadcc.cc Fri Jul 12 12:14:43 2019 -0400 @@ -33,6 +33,7 @@ #include "defun.h" #include "error.h" +#include "interpreter-private.h" #include "ovl.h" #include "parse.h" #include "utils.h" @@ -1483,8 +1484,8 @@ // The actual integration routine. -DEFUN (quadcc, args, , - doc: /* -*- texinfo -*- +DEFMETHOD (quadcc, interp, args, , + doc: /* -*- texinfo -*- @deftypefn {} {@var{q} =} quadcc (@var{f}, @var{a}, @var{b}) @deftypefnx {} {@var{q} =} quadcc (@var{f}, @var{a}, @var{b}, @var{tol}) @deftypefnx {} {@var{q} =} quadcc (@var{f}, @var{a}, @var{b}, @var{tol}, @var{sing}) @@ -1575,7 +1576,7 @@ // Arguments left and right. int nargin = args.length (); - octave_function *fcn; + octave_value fcn; double a, b, abstol, reltol, *sing; bool issingle; @@ -1600,17 +1601,7 @@ if (nargin < 3) print_usage (); - if (args(0).is_function_handle () || args(0).is_inline_function ()) - fcn = args(0).function_value (); - else - { - std::string fcn_name = unique_symbol_name ("__quadcc_fcn__"); - std::string fname = "function y = "; - fname.append (fcn_name); - fname.append ("(x) y = "); - fcn = extract_function (args(0), "quadcc", fcn_name, fname, - "; endfunction"); - } + fcn = octave::get_function_handle (interp, args(0), "x"); if (! args(1).is_real_scalar ()) error ("quadcc: lower limit of integration (A) must be a real scalar"); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/stack-frame-walker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/stack-frame-walker.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,65 @@ +/* + +Copyright (C) 2018 John W. Eaton + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_stack_frame_walker_h) +#define octave_stack_frame_walker_h 1 + +#include "octave-config.h" + +namespace octave +{ + class compiled_fcn_stack_frame; + class script_stack_frame; + class user_fcn_stack_frame; + class scope_stack_frame; + + class stack_frame_walker + { + protected: + + stack_frame_walker (void) { } + + virtual ~stack_frame_walker (void) = default; + + public: + + // No copying! + + stack_frame_walker (const stack_frame_walker&) = delete; + + stack_frame_walker& operator = (const stack_frame_walker&) = delete; + + virtual void + visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0; + + virtual void + visit_script_stack_frame (script_stack_frame&) = 0; + + virtual void + visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0; + + virtual void + visit_scope_stack_frame (scope_stack_frame&) = 0; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/stack-frame.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/stack-frame.cc Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,1546 @@ +/* + +Copyright (C) 1995-2018 John W. Eaton + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "lo-regexp.h" +#include "str-vec.h" + +#include "defun.h" +#include "interpreter.h" +#include "interpreter-private.h" +#include "oct-map.h" +#include "ov.h" +#include "ov-fcn.h" +#include "ov-fcn-handle.h" +#include "ov-usr-fcn.h" +#include "pager.h" +#include "parse.h" +#include "pt-eval.h" +#include "stack-frame.h" +#include "stack-frame-walker.h" +#include "syminfo.h" +#include "syminfo-accumulator.h" +#include "symrec.h" +#include "symscope.h" +#include "variables.h" + +namespace octave +{ + // FIXME: There should probably be a display method for the script, + // fcn, and scope objects and the script and function objects should + // be responsible for displaying the scopes they contain. + + static void display_scope (std::ostream& os, const symbol_scope& scope) + { + if (scope) + { + os << "scope: " << scope.name () << std::endl; + + if (scope.num_symbols () > 0) + { + os << "name (frame offset, data offset, storage class):" + << std::endl; + + std::list symbols = scope.symbol_list (); + + for (auto& sym : symbols) + { + os << " " << sym.name () << " (" << sym.frame_offset () + << ", " << sym.data_offset () << ", " << sym.storage_class () + << ")" << std::endl; + } + } + } + } + + class symbol_cleaner : public stack_frame_walker + { + public: + + symbol_cleaner (const std::string& pattern, bool have_regexp = false) + : stack_frame_walker (), m_patterns (pattern), + m_clear_all_names (false), m_clear_objects (false), + m_have_regexp (have_regexp), m_cleared_names () + { } + + symbol_cleaner (const string_vector& patterns, bool have_regexp = false) + : stack_frame_walker (), m_patterns (patterns), + m_clear_all_names (false), m_clear_objects (false), + m_have_regexp (have_regexp), m_cleared_names () + { } + + symbol_cleaner (bool clear_all_names = true, bool clear_objects = false) + : stack_frame_walker (), m_patterns (), + m_clear_all_names (clear_all_names), m_clear_objects (clear_objects), + m_have_regexp (false), m_cleared_names () + { } + + symbol_cleaner (const symbol_cleaner&) = delete; + + symbol_cleaner& operator = (const symbol_cleaner&) = delete; + + ~symbol_cleaner (void) = default; + + void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame) + { + // This one follows static link always. Hmm, should the access + // link for a compiled_fcn_stack_frame be the same as the static + // link? + + stack_frame *slink = frame.static_link (); + + if (slink) + slink->accept (*this); + } + + void visit_script_stack_frame (script_stack_frame& frame) + { + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame) + { + clean_frame (frame); + + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_scope_stack_frame (scope_stack_frame& frame) + { + clean_frame (frame); + + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + private: + + void maybe_clear_symbol (stack_frame& frame, const symbol_record& sym) + { + std::string name = sym.name (); + + if (m_cleared_names.find (name) == m_cleared_names.end ()) + { + // FIXME: Should we check that the name is defined and skip if + // it is not? Is it possible for another symbol with the same + // name to appear in a later stack frame? + + // FIXME: If we are clearing objects and a symbol is found, + // should we add it to the list of cleared names (since + // we did find a symbol) but skip clearing the object? + + if (m_clear_objects && ! frame.is_object (sym)) + return; + + m_cleared_names.insert (name); + + frame.clear (sym); + } + } + + // FIXME: It would be nice to avoid the duplication in the following + // function. + + void clear_symbols (stack_frame& frame, + const std::list& symbols) + { + if (m_clear_all_names) + { + for (const auto& sym : symbols) + maybe_clear_symbol (frame, sym); + } + else if (m_have_regexp) + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + regexp pat (pattern); + + for (const auto& sym : symbols) + { + if (pat.is_match (sym.name ())) + maybe_clear_symbol (frame, sym); + } + } + } + else + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + glob_match pat (pattern); + + for (const auto& sym : symbols) + { + if (pat.match (sym.name ())) + maybe_clear_symbol (frame, sym); + } + } + } + } + + void clean_frame (stack_frame& frame) + { + symbol_scope scope = frame.get_scope (); + + std::list symbols = scope.symbol_list (); + + if (m_clear_all_names || ! m_patterns.empty ()) + clear_symbols (frame, symbols); + } + + string_vector m_patterns; + + bool m_clear_all_names; + bool m_clear_objects; + bool m_have_regexp; + + std::set m_cleared_names; + }; + + // This function is only implemented for user_fcn stack frames and + // only called for those objects using unwind_protect and the + // call_stack::clear_current_frame_values function. Anything else + // indicates an error in the implementation. + + void stack_frame::clear_values (void) + { + panic_impossible (); + } + + // Return first occurrence of variables in current stack frame and any + // parent frames reachable through access links. + + symbol_info_list stack_frame::all_variables (void) + { + symbol_info_accumulator sia (true, true); + + accept (sia); + + return sia.symbol_info (); + } + + std::list + stack_frame::glob (const std::string& pattern) const + { + std::list retval; + + symbol_scope scope = get_scope (); + + const std::map& symbols = scope.symbols (); + + glob_match pat (pattern); + + for (const auto& nm_sr : symbols) + { + if (pat.match (nm_sr.first)) + { + symbol_record sr = nm_sr.second; + + if (! is_variable (sr)) + continue; + + retval.push_back (sr); + } + } + + return retval; + } + + std::list + stack_frame::regexp (const std::string& pattern) const + { + std::list retval; + + symbol_scope scope = get_scope (); + + const std::map& symbols = scope.symbols (); + + octave::regexp pat (pattern); + + for (const auto& nm_sr : symbols) + { + if (pat.is_match (nm_sr.first)) + { + symbol_record sr = nm_sr.second; + + if (! is_variable (sr)) + continue; + + retval.push_back (sr); + } + } + + return retval; + } + + std::list stack_frame::variable_names (void) const + { + std::list retval; + + symbol_scope scope = get_scope (); + + const std::map& symbols = scope.symbols (); + + for (const auto& nm_sr : symbols) + { + if (is_variable (nm_sr.second)) + retval.push_back (nm_sr.first); + } + + retval.sort (); + + return retval; + } + + size_t stack_frame::size (void) const + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + void stack_frame::resize (size_t) + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + stack_frame::scope_flags stack_frame::get_scope_flag (size_t) const + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + void stack_frame::set_scope_flag (size_t, scope_flags) + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + void stack_frame::install_variable (const symbol_record& sym, + const octave_value& value, bool global) + { + if (global && ! is_global (sym)) + { + octave_value val = varval (sym); + + if (val.is_defined ()) + { + std::string nm = sym.name (); + + warning_with_id ("Octave:global-local-conflict", + "global: '%s' is defined in the current scope.\n", + nm.c_str ()); + warning_with_id ("Octave:global-local-conflict", + "global: in a future version, global variables must be declared before use.\n"); + + // If the symbol is defined in the local but not the + // global scope, then use the local value as the + // initial value. This value will also override any + // initializer in the global statement. + octave_value global_val = m_evaluator.global_varval (nm); + + if (global_val.is_defined ()) + { + warning_with_id ("Octave:global-local-conflict", + "global: global value overrides existing local value"); + + clear (sym); + } + else + { + warning_with_id ("Octave:global-local-conflict", + "global: existing local value used to initialize global variable"); + + m_evaluator.global_varref (nm) = val; + } + } + + mark_global (sym); + } + + if (value.is_defined ()) + assign (sym, value); + } + + octave_value stack_frame::varval (size_t) const + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + octave_value& stack_frame::varref (size_t) + { + // This function should only be called for user_fcn_stack_frame or + // scope_stack_frame objects. Anything else indicates an error in + // the implementation. + + panic_impossible (); + } + + void stack_frame::clear_objects (void) + { + symbol_cleaner sc (true, true); + + accept (sc); + } + + void stack_frame::clear_variable (const std::string& name) + { + symbol_cleaner sc (name); + + accept (sc); + } + + void stack_frame::clear_variable_pattern (const std::string& pattern) + { + symbol_cleaner sc (pattern); + + accept (sc); + } + + void stack_frame::clear_variable_pattern (const string_vector& patterns) + { + symbol_cleaner sc (patterns); + + accept (sc); + } + + void stack_frame::clear_variable_regexp (const std::string& pattern) + { + symbol_cleaner sc (pattern, true); + + accept (sc); + } + + void stack_frame::clear_variable_regexp (const string_vector& patterns) + { + symbol_cleaner sc (patterns, true); + + accept (sc); + } + + void stack_frame::clear_variables (void) + { + symbol_cleaner sc; + + accept (sc); + } + + void stack_frame::display_stopped_in_message (std::ostream& os) const + { + if (index () == 0) + os << "at top level" << std::endl; + else + { + os << "stopped in " << fcn_name (); + + int l = line (); + if (l > 0) + os << " at line " << line (); + + os << " [" << fcn_file_name () << "] " << std::endl; + } + } + + void stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [stack_frame] (" << this << ") --" << std::endl; + + os << "static link: "; + if (m_static_link) + os << m_static_link; + else + os << "NULL"; + os << std::endl; + + os << "access link: "; + if (m_access_link) + os << m_access_link; + else + os << "NULL"; + os << std::endl; + + os << "line: " << m_line << std::endl; + os << "column: " << m_column << std::endl; + os << "index: " << m_index << std::endl; + + os << std::endl; + + if (! follow) + return; + + os << "FOLLOWING ACCESS LINKS:" << std::endl; + const stack_frame *frm = access_link (); + while (frm) + { + frm->display (false); + os << std::endl; + + frm = frm->access_link (); + } + } + + compiled_fcn_stack_frame * + compiled_fcn_stack_frame::dup (void) const + { + return new compiled_fcn_stack_frame (*this); + } + + void compiled_fcn_stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [compiled_fcn_stack_frame] (" << this << ") --" << std::endl; + stack_frame::display (follow); + + os << "fcn: " << m_fcn->name () + << " (" << m_fcn->type_name () << ")" << std::endl; + } + + void compiled_fcn_stack_frame::accept (stack_frame_walker& sfw) + { + sfw.visit_compiled_fcn_stack_frame (*this); + } + + script_stack_frame::script_stack_frame (tree_evaluator& tw, + octave_user_script *script, + unwind_protect *up_frame, + size_t index, + stack_frame *static_link) + : stack_frame (tw, index, static_link, get_access_link (static_link)), + m_script (script), m_unwind_protect_frame (up_frame), + m_lexical_frame_offsets (get_num_symbols (script), 1), + m_value_offsets (get_num_symbols (script), 0) + { + set_script_offsets (); + } + + script_stack_frame * + script_stack_frame::dup (void) const + { + return new script_stack_frame (*this); + } + + size_t script_stack_frame::get_num_symbols (octave_user_script *script) + { + symbol_scope script_scope = script->scope (); + + return script_scope.num_symbols (); + } + + void script_stack_frame::set_script_offsets (void) + { + // Set frame and data offsets inside stack frame based on enclosing + // scope(s). + + symbol_scope script_scope = m_script->scope (); + + size_t num_script_symbols = script_scope.num_symbols (); + + resize (num_script_symbols); + + const std::map& script_symbols + = script_scope.symbols (); + + set_script_offsets_internal (script_symbols); + } + + void script_stack_frame::set_script_offsets_internal + (const std::map& script_symbols) + { + // This scope will be used to evaluate the script. Find (or + // possibly insert) symbols from the dummy script scope here. + + symbol_scope eval_scope = m_access_link->get_scope (); + + if (eval_scope.is_nested ()) + { + bool found = false; + + for (const auto& nm_sr : script_symbols) + { + std::string name = nm_sr.first; + symbol_record script_sr = nm_sr.second; + + symbol_scope parent_scope = eval_scope; + + size_t count = 1; + + while (parent_scope) + { + const std::map& parent_scope_symbols + = parent_scope.symbols (); + + auto p = parent_scope_symbols.find (name); + + if (p != parent_scope_symbols.end ()) + { + found = true; + symbol_record parent_scope_sr = p->second; + + size_t script_sr_data_offset = script_sr.data_offset (); + + m_lexical_frame_offsets.at (script_sr_data_offset) + = parent_scope_sr.frame_offset () + 1; + + m_value_offsets.at (script_sr_data_offset) + = parent_scope_sr.data_offset (); + + break; + } + else + { + count++; + parent_scope = parent_scope.parent_scope (); + } + } + + if (! found) + error ("symbol '%s' cannot be added to static scope", + name.c_str ()); + } + } + else + { + const std::map& eval_scope_symbols + = eval_scope.symbols (); + + for (const auto& nm_sr : script_symbols) + { + std::string name = nm_sr.first; + symbol_record script_sr = nm_sr.second; + + auto p = eval_scope_symbols.find (name); + + symbol_record eval_scope_sr; + + if (p == eval_scope_symbols.end ()) + eval_scope_sr = eval_scope.insert (name); + else + eval_scope_sr = p->second; + + size_t script_sr_data_offset = script_sr.data_offset (); + + // The +1 is for going from the script frame to the eval + // frame. Only one access_link should need to be followed. + + m_lexical_frame_offsets.at (script_sr_data_offset) + = eval_scope_sr.frame_offset () + 1; + + m_value_offsets.at (script_sr_data_offset) + = eval_scope_sr.data_offset (); + } + } + } + + void script_stack_frame::resize_and_update_script_offsets (const symbol_record& sym) + { + size_t data_offset = sym.data_offset (); + + // This function is called when adding new symbols to a script + // scope. If the symbol wasn't present before, it should be outside + // the range so we need to resize then update offsets. + + assert (data_offset >= size ()); + + resize (data_offset+1); + + // FIXME: We should be able to avoid creating the map object and the + // looping in the set_scripts_offsets_internal function. Can we do + // that without (or with minimal) code duplication? + + std::map tmp_symbols; + tmp_symbols[sym.name ()] = sym; + set_script_offsets_internal (tmp_symbols); + } + + // If this is a nested scope, set access_link to nearest parent + // stack frame that corresponds to the lexical parent of this scope. + + stack_frame * + script_stack_frame::get_access_link (stack_frame *static_link) + { + stack_frame *alink = nullptr; + + // If this script is called from another script, set access + // link to ultimate parent stack frame. + + alink = static_link; + + while (alink->is_user_script_frame ()) + { + if (alink->access_link ()) + alink = alink->access_link (); + else + break; + } + + return alink; + } + + symbol_record script_stack_frame::lookup_symbol (const std::string& name) const + { + symbol_scope scope = get_scope (); + + symbol_record sym = scope.lookup_symbol (name); + + if (sym) + { + assert (sym.frame_offset () == 0); + + return sym; + } + + sym = m_access_link->lookup_symbol (name); + + // Return symbol record with adjusted frame offset. + symbol_record new_sym = sym.dup (); + + new_sym.set_frame_offset (sym.frame_offset () + 1); + + return new_sym; + } + + symbol_record script_stack_frame::insert_symbol (const std::string& name) + { + // If the symbols is already in the immediate scope, there is + // nothing more to do. + + symbol_scope scope = get_scope (); + + symbol_record sym = scope.lookup_symbol (name); + + if (sym) + { + // All symbol records in a script scope should have zero offset, + // which means we redirect our lookup using + // lexical_frame_offsets and values_offets. + assert (sym.frame_offset () == 0); + + return sym; + } + + // Insert the symbol in the current scope then resize and update + // offsets. This operation should never fail. + + sym = scope.find_symbol (name); + + assert (sym); + + resize_and_update_script_offsets (sym); + + return sym; + } + + // Similar to set_script_offsets_internal except that we only return + // frame and data offsets for symbols found by name in parent scopes + // instead of updating the offsets stored in the script frame itself. + + bool + script_stack_frame::get_val_offsets_internal (const symbol_record& script_sr, + size_t& frame_offset, + size_t& data_offset) const + { + bool found = false; + + // This scope will be used to evaluate the script. Find symbols + // here by name. + + symbol_scope eval_scope = m_access_link->get_scope (); + + if (eval_scope.is_nested ()) + { + std::string name = script_sr.name (); + + symbol_scope parent_scope = eval_scope; + + size_t count = 1; + + while (parent_scope) + { + const std::map& parent_scope_symbols + = parent_scope.symbols (); + + auto p = parent_scope_symbols.find (name); + + if (p != parent_scope_symbols.end ()) + { + found = true; + symbol_record parent_scope_sr = p->second; + + frame_offset = parent_scope_sr.frame_offset () + 1; + + data_offset = parent_scope_sr.data_offset (); + + break; + } + else + { + count++; + parent_scope = parent_scope.parent_scope (); + } + } + } + else + { + const std::map& eval_scope_symbols + = eval_scope.symbols (); + + std::string name = script_sr.name (); + + auto p = eval_scope_symbols.find (name); + + symbol_record eval_scope_sr; + + if (p != eval_scope_symbols.end ()) + { + found = true; + eval_scope_sr = p->second; + + // The +1 is for going from the script frame to the eval + // frame. Only one access_link should need to be followed. + + frame_offset = eval_scope_sr.frame_offset () + 1; + + data_offset = eval_scope_sr.data_offset (); + } + } + + return found; + } + + bool script_stack_frame::get_val_offsets (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset) const + { + data_offset = sym.data_offset (); + frame_offset = sym.frame_offset (); + + if (frame_offset == 0) + { + // An out of range data_offset value here means that we have a + // symbol that was not originally in the script. But this + // function is called in places where we can't insert a new + // symbol, so we fail and it is up to the caller to decide what + // to do. + + if (data_offset >= size ()) + return get_val_offsets_internal (sym, frame_offset, data_offset); + + // Use frame and value offsets stored in this stack frame, + // indexed by data_offset from the symbol_record to find the + // values. These offsets were determined by + // script_stack_frame::set_script_offsets when this script was + // invoked. + + frame_offset = m_lexical_frame_offsets.at (data_offset); + + if (frame_offset == 0) + { + // If the frame offset stored in m_lexical_frame_offsets is + // zero, then the data offset in the evaluation scope has + // not been determined so try to do that now. The symbol + // may have been added by eval and without calling + // resize_and_update_script_offsets. + + return get_val_offsets_internal (sym, frame_offset, data_offset); + } + + data_offset = m_value_offsets.at (data_offset); + } + else + { + // If frame_offset is not zero, then then we must have a symbol + // that was not originally in the script. The values should + // have been determined by the script_stack_frame::lookup function. + } + + return true; + } + + void script_stack_frame::get_val_offsets_with_insert (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset) + { + data_offset = sym.data_offset (); + frame_offset = sym.frame_offset (); + + if (frame_offset == 0) + { + if (data_offset >= size ()) + { + // If the data_offset is out of range, then we must have a + // symbol that was not originally in the script. Resize and + // update the offsets. + + resize_and_update_script_offsets (sym); + } + + // Use frame and value offsets stored in this stack frame, + // indexed by data_offset from the symbol_record to find the + // values. These offsets were determined by + // script_stack_frame::set_script_offsets when this script was + // invoked. + + frame_offset = m_lexical_frame_offsets.at (data_offset); + + if (frame_offset == 0) + { + // If the frame offset stored in m_lexical_frame_offsets is + // zero, then the data offset in the evaluation scope has + // not been determined so try to do that now. The symbol + // may have been added by eval and without calling + // resize_and_update_script_offsets. + + // We don't need to resize here. That case is handled above. + + // FIXME: We should be able to avoid creating the map object + // and the looping in the set_scripts_offsets_internal + // function. Can we do that without (or with minimal) code + // duplication? + + std::map tmp_symbols; + tmp_symbols[sym.name ()] = sym; + set_script_offsets_internal (tmp_symbols); + } + + data_offset = m_value_offsets.at (data_offset); + } + else + { + // If frame_offset is not zero, then then we must have a symbol + // that was not originally in the script. The values were + // determined by the script_stack_frame::lookup function. + } + } + + stack_frame::scope_flags + script_stack_frame::scope_flag (const symbol_record& sym) const + { + size_t frame_offset; + size_t data_offset; + + bool found = get_val_offsets (sym, frame_offset, data_offset); + + // It can't be global or persistent, so call it local. + if (! found) + return LOCAL; + + // Follow frame_offset access links to stack frame that holds + // the value. + + const stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (! frame) + error ("internal error: invalid access link in function call stack"); + + if (data_offset >= frame->size ()) + return LOCAL; + + return frame->get_scope_flag (data_offset); + } + + octave_value script_stack_frame::varval (const symbol_record& sym) const + { + size_t frame_offset; + size_t data_offset; + + bool found = get_val_offsets (sym, frame_offset, data_offset); + + if (! found) + return octave_value (); + + // Follow frame_offset access links to stack frame that holds + // the value. + + const stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (! frame) + error ("internal error: invalid access link in function call stack"); + + if (data_offset >= frame->size ()) + return octave_value (); + + switch (frame->get_scope_flag (data_offset)) + { + case LOCAL: + return frame->varval (data_offset); + + case PERSISTENT: + { + symbol_scope scope = frame->get_scope (); + + return scope.persistent_varval (data_offset); + } + + case GLOBAL: + return m_evaluator.global_varval (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + octave_value& script_stack_frame::varref (const symbol_record& sym) + { + size_t frame_offset; + size_t data_offset; + get_val_offsets_with_insert (sym, frame_offset, data_offset); + + // Follow frame_offset access links to stack frame that holds + // the value. + + stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (data_offset >= frame->size ()) + frame->resize (data_offset+1); + + switch (frame->get_scope_flag (data_offset)) + { + case LOCAL: + return frame->varref (data_offset); + + case PERSISTENT: + { + symbol_scope scope = frame->get_scope (); + + return scope.persistent_varref (data_offset); + } + + case GLOBAL: + return m_evaluator.global_varref (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + void script_stack_frame::mark_scope (const symbol_record& sym, + scope_flags flag) + { + size_t data_offset = sym.data_offset (); + + // Redirection to evaluation context for the script. + + size_t frame_offset = m_lexical_frame_offsets.at (data_offset); + data_offset = m_value_offsets.at (data_offset); + + if (frame_offset > 1) + error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used"); + + stack_frame *frame = access_link (); + + if (data_offset >= frame->size ()) + frame->resize (data_offset+1); + + frame->set_scope_flag (data_offset, flag); + } + + void script_stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [script_stack_frame] (" << this << ") --" << std::endl; + stack_frame::display (follow); + + os << "script: " << m_script->name () + << " (" << m_script->type_name () << ")" << std::endl; + + os << "lexical_offsets: " << m_lexical_frame_offsets.size () + << " elements:"; + + for (size_t i = 0; i < m_lexical_frame_offsets.size (); i++) + os << " " << m_lexical_frame_offsets.at (i); + os << std::endl; + + os << "value_offsets: " << m_value_offsets.size () << " elements:"; + for (size_t i = 0; i < m_value_offsets.size (); i++) + os << " " << m_value_offsets.at (i); + os << std::endl; + + display_scope (os, get_scope ()); + } + + void script_stack_frame::accept (stack_frame_walker& sfw) + { + sfw.visit_script_stack_frame (*this); + } + + void base_value_stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [base_value_stack_frame] (" << this << ") --" << std::endl; + stack_frame::display (follow); + + os << "values: " << m_values.size () + << " elements (idx, scope flag, type):" << std::endl; + + for (size_t i = 0; i < m_values.size (); i++) + { + os << " (" << i << ", " << m_flags.at (i) << ", "; + + octave_value val = varval (i); + + os << (val.is_defined () ? val.type_name () : " UNDEFINED") << ")" + << std::endl; + } + } + + user_fcn_stack_frame * + user_fcn_stack_frame::dup (void) const + { + return new user_fcn_stack_frame (*this); + } + + // If this is a nested scope, set access_link to nearest parent + // stack frame that corresponds to the lexical parent of this scope. + + stack_frame * + user_fcn_stack_frame::get_access_link (octave_user_function *fcn, + stack_frame *static_link) + { + stack_frame *alink = nullptr; + + symbol_scope fcn_scope = fcn->scope (); + + if (fcn_scope.is_nested ()) + { + if (! static_link) + error ("internal call stack error (invalid static link)"); + + symbol_scope caller_scope = static_link->get_scope (); + + int nesting_depth = fcn_scope.nesting_depth (); + int caller_nesting_depth = caller_scope.nesting_depth (); + + if (caller_nesting_depth < nesting_depth) + { + // FIXME: do we need to ensure that the called + // function is a child of the caller? Does it hurt + // to assert this condition, at least for now? + + alink = static_link; + } + else + { + // FIXME: do we need to check that the parent of the + // called function is also a parent of the caller? + // Does it hurt to assert this condition, at least + // for now? + + int links_to_follow = caller_nesting_depth - nesting_depth + 1; + + alink = static_link; + + while (alink && --links_to_follow >= 0) + alink = alink->access_link (); + + if (! alink) + error ("internal function nesting error (invalid access link)"); + } + } + + return alink; + } + + void user_fcn_stack_frame::clear_values (void) + { + symbol_scope fcn_scope = m_fcn->scope (); + + const std::list& symbols = fcn_scope.symbol_list (); + + if (size () == 0) + return; + + for (const auto& sym : symbols) + { + size_t frame_offset = sym.frame_offset (); + + if (frame_offset > 0) + continue; + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + continue; + + if (get_scope_flag (data_offset) == LOCAL) + { + octave_value& ref = m_values.at (data_offset); + + if (ref.get_count () == 1) + { + ref.call_object_destructor (); + ref = octave_value (); + } + } + } + } + + symbol_record user_fcn_stack_frame::lookup_symbol (const std::string& name) const + { + const stack_frame *frame = this; + + while (frame) + { + symbol_scope scope = frame->get_scope (); + + symbol_record sym = scope.lookup_symbol (name); + + if (sym) + return sym; + + frame = frame->access_link (); + } + + return symbol_record (); + } + + symbol_record user_fcn_stack_frame::insert_symbol (const std::string& name) + { + // If the symbols is already in the immediate scope, there is + // nothing more to do. + + symbol_scope scope = get_scope (); + + symbol_record sym = scope.lookup_symbol (name); + + if (sym) + return sym; + + // FIXME: This needs some thought... We may need to add a symbol to + // a static workspace, but the symbol can never be defined as a + // variable. This currently works by tagging the added symbol as + // "added_static". Aside from the bad name, this doesn't seem like + // the best solution. Maybe scopes should have a separate set of + // symbols that may only be defined as functions? + + // Insert the symbol in the current scope. This is not possible for + // anonymous functions, nested functions, or functions that contain + // nested functions (their scopes will all be marked static). + + // if (scope.is_static ()) + // error ("can not add variable '%s' to a static workspace", + // name.c_str ()); + + // At this point, non-local references are not possible so we only + // need to look in the current scope and insert there. This + // operation should never fail. + + sym = scope.find_symbol (name); + + assert (sym); + + return sym; + } + + stack_frame::scope_flags + user_fcn_stack_frame::scope_flag (const symbol_record& sym) const + { + size_t frame_offset = sym.frame_offset (); + size_t data_offset = sym.data_offset (); + + // Follow frame_offset access links to stack frame that holds + // the value. + + const stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (! frame) + error ("internal error: invalid access link in function call stack"); + + if (data_offset >= frame->size ()) + return LOCAL; + + return frame->get_scope_flag (data_offset); + } + + octave_value user_fcn_stack_frame::varval (const symbol_record& sym) const + { + size_t frame_offset = sym.frame_offset (); + size_t data_offset = sym.data_offset (); + + // Follow frame_offset access links to stack frame that holds + // the value. + + const stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (! frame) + error ("internal error: invalid access link in function call stack"); + + if (data_offset >= frame->size ()) + return octave_value (); + + switch (frame->get_scope_flag (data_offset)) + { + case LOCAL: + return frame->varval (data_offset); + + case PERSISTENT: + { + symbol_scope scope = frame->get_scope (); + + return scope.persistent_varval (data_offset); + } + + case GLOBAL: + return m_evaluator.global_varval (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + octave_value& user_fcn_stack_frame::varref (const symbol_record& sym) + { + size_t frame_offset = sym.frame_offset (); + size_t data_offset = sym.data_offset (); + + // Follow frame_offset access links to stack frame that holds + // the value. + + stack_frame *frame = this; + + for (size_t i = 0; i < frame_offset; i++) + frame = frame->access_link (); + + if (data_offset >= frame->size ()) + frame->resize (data_offset+1); + + switch (frame->get_scope_flag (data_offset)) + { + case LOCAL: + return frame->varref (data_offset); + + case PERSISTENT: + { + symbol_scope scope = frame->get_scope (); + + return scope.persistent_varref (data_offset); + } + + case GLOBAL: + return m_evaluator.global_varref (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + void user_fcn_stack_frame::mark_scope (const symbol_record& sym, scope_flags flag) + { + size_t frame_offset = sym.frame_offset (); + + if (frame_offset > 0 && (flag == PERSISTENT || flag == GLOBAL)) + error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used"); + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + resize (data_offset+1); + + set_scope_flag (data_offset, flag); + } + + void user_fcn_stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [user_fcn_stack_frame] (" << this << ") --" << std::endl; + base_value_stack_frame::display (follow); + + os << "fcn: " << m_fcn->name () + << " (" << m_fcn->type_name () << ")" << std::endl; + + display_scope (os, get_scope ()); + } + + void user_fcn_stack_frame::accept (stack_frame_walker& sfw) + { + sfw.visit_user_fcn_stack_frame (*this); + } + + scope_stack_frame * + scope_stack_frame::dup (void) const + { + return new scope_stack_frame (*this); + } + + symbol_record scope_stack_frame::insert_symbol (const std::string& name) + { + // There is no access link for scope frames, so there is no other + // frame to search in and the offset must be zero. + + symbol_record sym = m_scope.lookup_symbol (name); + + if (sym) + return sym; + + // If the symbol is not found, insert it. We only need to search in + // the local scope object. This operation should never fail. + + sym = m_scope.find_symbol (name); + + assert (sym); + + return sym; + } + + stack_frame::scope_flags + scope_stack_frame::scope_flag (const symbol_record& sym) const + { + // There is no access link for scope frames, so the frame + // offset must be zero. + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + return LOCAL; + + return get_scope_flag (data_offset); + } + + octave_value scope_stack_frame::varval (const symbol_record& sym) const + { + // There is no access link for scope frames, so the frame + // offset must be zero. + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + return octave_value (); + + switch (get_scope_flag (data_offset)) + { + case LOCAL: + return m_values.at (data_offset); + + case PERSISTENT: + return m_scope.persistent_varval (data_offset); + + case GLOBAL: + return m_evaluator.global_varval (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + octave_value& scope_stack_frame::varref (const symbol_record& sym) + { + // There is no access link for scope frames, so the frame + // offset must be zero. + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + resize (data_offset+1); + + switch (get_scope_flag (data_offset)) + { + case LOCAL: + return m_values.at (data_offset); + + case PERSISTENT: + return m_scope.persistent_varref (data_offset); + + case GLOBAL: + return m_evaluator.global_varref (sym.name ()); + } + + error ("internal error: invalid switch case"); + } + + void scope_stack_frame::mark_scope (const symbol_record& sym, + scope_flags flag) + { + // There is no access link for scope frames, so the frame + // offset must be zero. + + size_t data_offset = sym.data_offset (); + + if (data_offset >= size ()) + resize (data_offset+1); + + set_scope_flag (data_offset, flag); + } + + void scope_stack_frame::display (bool follow) const + { + std::ostream& os = octave_stdout; + + os << "-- [scope_stack_frame] (" << this << ") --" << std::endl; + base_value_stack_frame::display (follow); + + display_scope (os, m_scope); + } + + void scope_stack_frame::accept (stack_frame_walker& sfw) + { + sfw.visit_scope_stack_frame (*this); + } +} diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/stack-frame.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/stack-frame.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,1063 @@ +/* + +Copyright (C) 1993-2018 John W. Eaton + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_stack_frame_h) +#define octave_stack_frame_h 1 + +#include "octave-config.h" + +#include +#include +#include +#include +#include + +class octave_value; +class octave_value_list; + +#include "error.h" +#include "ov-fcn.h" +#include "ov-fcn.h" +#include "ov-fcn-handle.h" +#include "ov-usr-fcn.h" +#include "syminfo.h" +#include "symscope.h" + +// Variable values are stored in the stack_frame objects that make up +// the call_stack. There are four separate stack_frame objects +// corresponding to the following language elements: +// +// * user-defined functions +// +// These are .m files. They have local variables. +// +// * scripts +// +// These are .m files, but not functions. They access variables, +// but do not store any values directly. All values are stored in +// the stack frame corresponding to the scope in which they are +// executed. +// +// * scopes that do not correspond to functions +// +// This is primarily used by the top-level scope but the +// interpreter may also create temporary scopes in which to +// evaluate functions or scripts. +// +// * compiled functions +// +// These are built-in functions and dynamically-loaded compiled +// functions (.mex and .oct files) and do not contain variable +// values of their own. They are skipped when Octave displays a +// stack trace. +// +// All stack frames also contain the following data: +// +// * a reference to the evaluator that contains the frame +// +// Global variables are now stored in the evaluator and this link +// gives us immediate access to them. +// +// * line and column in the source file where the stack frame was created +// +// These values are used to print stack traces. +// +// * A pointer to the nearest parent frame that contains variable +// info (the "static" link) +// +// A frame that contains variable info may be a user-defined +// function, script, or scope frame. This pointer should never +// point to a compiled function stack frame. +// +// * A pointer to the nearest lexical parent frame (the "access" link) +// +// Used to access non-local variables for nested and anonymous +// functions or as a link to the parent frame in which a script is +// executed. This pointer should only point to a parent function +// stack frame. + +namespace octave +{ + class tree_evaluator; + class symbol_info_list; + class unwind_protect; + + class compiled_fcn_stack_frame; + class script_stack_frame; + class user_fcn_stack_frame; + class scope_stack_frame; + + class stack_frame_walker; + + class stack_frame + { + public: + + // Markers indicating the type of a variable. Values for local + // variables are stored in the stack frame. Values for + // global variables are stored in the tree_evaluator object that + // contains the stack frame. Values for persistent variables are + // stored in the function scope corresponding to the stack frame. + + enum scope_flags + { + LOCAL, + GLOBAL, + PERSISTENT + }; + + // Index into the list of automatic variables for user-defined + // function stack frames. + + enum auto_var_type + { + ARG_NAMES, + IGNORED, + NARGIN, + NARGOUT, + SAVED_WARNING_STATES, + NUM_AUTO_VARS + }; + + stack_frame (void) = delete; + + stack_frame (tree_evaluator& tw, size_t index, + stack_frame *static_link, stack_frame *access_link) + : m_evaluator (tw), m_line (-1), m_column (-1), m_index (index), + m_static_link (static_link), m_access_link (access_link), + m_dispatch_class () + { } + + stack_frame (const stack_frame& elt) = default; + + stack_frame& operator = (const stack_frame& elt) = delete; + + virtual ~stack_frame (void) = default; + + virtual stack_frame * dup (void) const = 0; + + // FIXME: It would be nice to eliminate these but there are a few + // places where we still need to know the specific type of the + // stack frame that we are handling. + + virtual bool is_compiled_fcn_frame (void) const { return false; } + virtual bool is_user_script_frame (void) const { return false; } + virtual bool is_user_fcn_frame (void) const { return false; } + virtual bool is_scope_frame (void) const { return false; } + + virtual void clear_values (void); + + size_t index (void) const { return m_index; } + + void line (int l) { m_line = l; } + int line (void) const { return m_line; } + + void column (int c) { m_column = c; } + int column (void) const { return m_column; } + + std::string fcn_file_name (void) const + { + octave_function *fcn = function (); + + return fcn ? fcn->fcn_file_name () : ""; + } + + std::string fcn_name (bool print_subfn = true) const + { + std::string retval; + + octave_function *fcn = function (); + + if (fcn) + { + std::string parent_fcn_name = fcn->parent_fcn_name (); + + if (print_subfn && ! parent_fcn_name.empty ()) + retval = parent_fcn_name + '>'; + + if (fcn->is_anonymous_function ()) + retval += octave_fcn_handle::anonymous; + else + retval += fcn->name (); + } + else + retval = ""; + + return retval; + } + + virtual symbol_scope get_scope (void) const = 0; + + virtual octave_function * function (void) const { return nullptr; } + + virtual unwind_protect * + unwind_protect_frame (void) const { return nullptr; } + + // FIXME: Should this function be private? + + symbol_info_list + make_symbol_info_list (const std::list& symrec_list) const + { + symbol_info_list symbol_stats; + + for (const auto& sym : symrec_list) + { + octave_value value = varval (sym); + + if (value.is_defined ()) + { + symbol_info syminf (sym.name (), value, sym.is_formal (), + is_global (sym), is_persistent (sym)); + + symbol_stats.append (syminf); + } + } + + return symbol_stats; + } + + symbol_info_list all_variables (void); + + // FIXME: Should these exist? Probably we should avoid returning + // lists of symbol_record objects, so maybe they should be + // private functions? + + std::list glob (const std::string& pattern) const; + + std::list regexp (const std::string& pattern) const; + + std::list variable_names (void) const; + + // Look for named symbol visible from current scope. Don't + // attempt to insert if missing. + virtual symbol_record lookup_symbol (const std::string&) const = 0; + + // Look for named symbol visible from current scope. Attempt to + // insert if missing. + virtual symbol_record insert_symbol (const std::string&) = 0; + + // FIXME: should these functions should return all symbols visible in + // the current stack frame including those that come from a parent + // scope/frame? + + symbol_info_list glob_symbol_info (const std::string& pattern) const + { + return make_symbol_info_list (glob (pattern)); + } + + symbol_info_list regexp_symbol_info (const std::string& pattern) const + { + return make_symbol_info_list (regexp (pattern)); + } + + symbol_info_list get_symbol_info (void) + { + return all_variables (); + } + + void make_persistent (const symbol_record& sym) + { + if (sym.is_formal ()) + { + std::string nm = sym.name (); + error ("can't make function parameter %s persistent", nm.c_str ()); + } + + if (is_global (sym)) + { + std::string nm = sym.name (); + error ("can't make global variable '%s' persistent", nm.c_str ()); + } + + install_variable (sym, octave_value (), false); + + mark_persistent (sym); + } + + void make_global (const symbol_record& sym) + { + if (is_persistent (sym)) + { + std::string nm = sym.name (); + error ("can't make persistent variable '%s' global", nm.c_str ()); + } + + install_variable (sym, octave_value (), true); + + mark_global (sym); + } + + stack_frame * static_link (void) const {return m_static_link; } + + stack_frame * access_link (void) const {return m_access_link; } + + void set_closure_links (stack_frame *dup_frame) + { + m_static_link = dup_frame; + m_access_link = dup_frame; + } + + virtual size_t size (void) const; + + virtual void resize (size_t); + + void mark_global (const symbol_record& sym) + { + mark_scope (sym, GLOBAL); + } + + void unmark_global (const symbol_record& sym) + { + mark_scope (sym, LOCAL); + } + + void mark_persistent (const symbol_record& sym) + { + mark_scope (sym, PERSISTENT); + } + + void unmark_persistent (const symbol_record& sym) + { + mark_scope (sym, LOCAL); + } + + bool is_defined (const symbol_record& sym) const + { + octave_value val = varval (sym); + + return val.is_defined (); + } + + bool is_variable (const symbol_record& sym) const + { + octave_value val = varval (sym); + + return val.is_defined (); + } + + bool is_variable (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? is_variable (sym) : false; + } + + bool is_local_variable (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? (is_variable (sym) && ! is_global (sym)) : false; + } + + bool is_object (const symbol_record& sym) const + { + octave_value val = varval (sym); + + return val.isobject (); + } + + bool is_object (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? is_object (sym) : false; + } + + virtual scope_flags scope_flag (const symbol_record&) const = 0; + + virtual scope_flags get_scope_flag (size_t) const; + + virtual void set_scope_flag (size_t, scope_flags); + + bool is_global (const symbol_record& sym) const + { + return scope_flag (sym) == GLOBAL; + } + + bool is_global (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? is_global (sym) : false; + } + + bool is_persistent (const symbol_record& sym) const + { + return scope_flag (sym) == PERSISTENT; + } + + bool is_persistent (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? is_persistent (sym) : false; + } + + void install_variable (const symbol_record& sym, + const octave_value& value, bool global); + + void install_variable (const std::string& name, + const octave_value& value, bool global) + { + symbol_record sym = insert_symbol (name); + + install_variable (sym, value, global); + } + + virtual octave_value get_auto_fcn_var (auto_var_type) const = 0; + + virtual void set_auto_fcn_var (auto_var_type, const octave_value&) = 0; + + virtual octave_value varval (const symbol_record& sym) const = 0;; + + virtual octave_value varval (size_t data_offset) const; + + octave_value varval (const std::string& name) const + { + symbol_record sym = lookup_symbol (name); + + return sym ? varval (sym) : octave_value (); + } + + virtual octave_value& varref (const symbol_record& sym) = 0; + + virtual octave_value& varref (size_t data_offset); + + void assign (const symbol_record& sym, const octave_value& val) + { + octave_value& lhs = varref (sym); + + if (lhs.get_count () == 1) + lhs.call_object_destructor (); + + // Regularize a null matrix if stored into a variable. + lhs = val.storable_value (); + } + + void assign (const std::string& name, const octave_value& val) + { + symbol_record sym = insert_symbol (name); + + assign (sym, val); + } + + void assign (octave_value::assign_op op, const symbol_record& sym, + const std::string& type, + const std::list& idx, + const octave_value& rhs) + { + if (idx.empty ()) + { + if (op == octave_value::op_asn_eq) + assign (sym, rhs); + else + varref (sym).assign (op, rhs); + } + else + varref (sym).assign (op, type, idx, rhs); + } + + void do_non_const_unary_op (octave_value::unary_op op, + const symbol_record& sym, + const std::string& type, + const std::list& idx) + { + if (idx.empty ()) + varref (sym).do_non_const_unary_op (op); + else + varref (sym).do_non_const_unary_op (op, type, idx); + } + + octave_value value (const symbol_record& sym, const std::string& type, + const std::list& idx) const + { + octave_value retval = varval (sym); + + if (! idx.empty ()) + { + if (retval.is_constant ()) + retval = retval.subsref (type, idx); + else + { + octave_value_list t = retval.subsref (type, idx, 1); + + retval = t.length () > 0 ? t(0) : octave_value (); + } + } + + return retval; + } + + octave_value find_subfunction (const std::string& name) const + { + symbol_scope scope = get_scope (); + + return scope.find_subfunction (name); + } + + void clear (const symbol_record& sym) + { + if (is_global (sym)) + unmark_global (sym); + + assign (sym, octave_value ()); + + if (is_persistent (sym)) + unmark_persistent (sym); + } + + void clear_objects (void); + + void clear_variable (const std::string& name); + + void clear_variable_pattern (const std::string& pattern); + void clear_variable_pattern (const string_vector& patterns); + + void clear_variable_regexp (const std::string& pattern); + void clear_variable_regexp (const string_vector& patterns); + + void clear_variables (void); + + std::string get_dispatch_class (void) const { return m_dispatch_class; } + + void set_dispatch_class (const std::string& class_name) + { + m_dispatch_class = class_name; + } + + void display_stopped_in_message (std::ostream& os) const; + + virtual void mark_scope (const symbol_record&, scope_flags) = 0; + + virtual void display (bool follow = true) const; + + virtual void accept (stack_frame_walker& sfw) = 0; + + protected: + + // Reference to the call stack that contains this frame. Global + // variables are stored in the call stack. This link gives us + // immediate access to them. + tree_evaluator& m_evaluator; + + // The line and column of the source file where this stack frame + // was created. Used to print stack traces. + int m_line; + int m_column; + + // Index in call stack. + size_t m_index; + + // Pointer to the nearest parent frame that contains variable + // information (script, function, or scope). + stack_frame *m_static_link; + + // Pointer to the nearest lexical parent frame. Used to access + // non-local variables for nested and anonymous functions or as a + // link to the parent frame in which a script is executed. + stack_frame *m_access_link; + + // Allow function handles to temporarily store their dispatch class + // in the call stack. + std::string m_dispatch_class; + }; + + class compiled_fcn_stack_frame : public stack_frame + { + public: + + compiled_fcn_stack_frame (void) = delete; + + compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn, + size_t index, stack_frame *static_link) + : stack_frame (tw, index, static_link, static_link->access_link ()), + m_fcn (fcn) + { } + + compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default; + + compiled_fcn_stack_frame& + operator = (const compiled_fcn_stack_frame& elt) = delete; + + ~compiled_fcn_stack_frame (void) = default; + + compiled_fcn_stack_frame * dup (void) const; + + bool is_compiled_fcn_frame (void) const { return true; } + + symbol_scope get_scope (void) const + { + return m_static_link->get_scope (); + } + + octave_function * function (void) const { return m_fcn; } + + symbol_record lookup_symbol (const std::string& name) const + { + return m_static_link->lookup_symbol (name); + } + + symbol_record insert_symbol (const std::string& name) + { + return m_static_link->insert_symbol (name); + } + + stack_frame::scope_flags scope_flag (const symbol_record& sym) const + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->scope_flag (sym); + } + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_static_link->set_auto_fcn_var (avt, val); + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_static_link->get_auto_fcn_var (avt); + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (const symbol_record& sym) const + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->varval (sym); + } + + octave_value& varref (const symbol_record& sym) + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->varref (sym); + } + + void mark_scope (const symbol_record& sym, + scope_flags flag) + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + m_static_link->mark_scope (sym, flag); + } + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // Compiled function object associated with this stack frame. + // Should always be a built-in, .oct or .mex file function and + // should always be valid. + octave_function *m_fcn; + }; + + // Scripts have a symbol_scope object to store the set of variables + // in the script, but values for those variables are stored in the + // stack frame corresponding to the nearest calling function or in + // the top-level scope (the evaluation stack frame). + // + // Accessing values in a scope requires a mapping from the index of + // the variable for the script scope to the list of values in the + // evaluation frame(s). The frame offset tells us how many access + // links we must follow to find the stack frame that holds the + // value. The value offset is the index into the vector of values + // in that stack frame that we should use to find the value. + // + // Frame and value offsets are set in this stack frame when it is + // created using information from the script and enclosing scopes. + // + // If a script is invoked in a nested function context, the frame + // offsets for individual values may be different. Some may be + // accessed from the invoking function and some may come from a + // parent function. + + class script_stack_frame : public stack_frame + { + public: + + script_stack_frame (void) = delete; + + script_stack_frame (tree_evaluator& tw, octave_user_script *script, + unwind_protect *up_frame, size_t index, + stack_frame *static_link); + + script_stack_frame (const script_stack_frame& elt) = default; + + script_stack_frame& operator = (const script_stack_frame& elt) = delete; + + ~script_stack_frame (void) = default; + + script_stack_frame * dup (void) const; + + bool is_user_script_frame (void) const { return true; } + + static stack_frame * get_access_link (stack_frame *static_link); + + static size_t get_num_symbols (octave_user_script *script); + + void set_script_offsets (void); + + void set_script_offsets_internal (const std::map& symbols); + + void resize_and_update_script_offsets (const symbol_record& sym); + + symbol_scope get_scope (void) const { return m_script->scope (); } + + octave_function * function (void) const { return m_script; } + + unwind_protect * + unwind_protect_frame (void) const { return m_unwind_protect_frame; } + + symbol_record lookup_symbol (const std::string& name) const; + + symbol_record insert_symbol (const std::string&); + + size_t size (void) const { return m_lexical_frame_offsets.size (); } + + void resize (size_t size) + { + m_lexical_frame_offsets.resize (size, 0); + m_value_offsets.resize (size, 0); + } + + void get_val_offsets_with_insert (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset); + + bool get_val_offsets_internal (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset) const; + + bool get_val_offsets (const symbol_record& sym, size_t& frame_offset, + size_t& data_offset) const; + + scope_flags scope_flag (const symbol_record& sym) const; + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_access_link->set_auto_fcn_var (avt, val); + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_access_link->get_auto_fcn_var (avt); + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // Script object associated with this stack frame. Should always + // be valid. + octave_user_script *m_script; + + // The nearest unwind protect frame that was active when this + // stack frame was created. Should always be valid. + unwind_protect *m_unwind_protect_frame; + + // Mapping between the symbols in the symbol_scope object of the + // script to the stack frame in which the script is executed. The + // frame offsets may be greater than one if the script is executed + // in a nested function context. + + std::vector m_lexical_frame_offsets; + std::vector m_value_offsets; + }; + + // Base class for values and offsets shared by user_fcn and scope + // frames. + + class base_value_stack_frame : public stack_frame + { + public: + + base_value_stack_frame (void) = delete; + + base_value_stack_frame (tree_evaluator& tw, size_t num_symbols, + size_t index, stack_frame *static_link, + stack_frame *access_link) + : stack_frame (tw, index, static_link, access_link), + m_values (num_symbols, octave_value ()), + m_flags (num_symbols, LOCAL), + m_auto_vars (NUM_AUTO_VARS, octave_value ()) + { } + + base_value_stack_frame (const base_value_stack_frame& elt) = default; + + base_value_stack_frame& + operator = (const base_value_stack_frame& elt) = delete; + + ~base_value_stack_frame (void) = default; + + size_t size (void) const + { + return m_values.size (); + } + + void resize (size_t size) + { + m_values.resize (size, octave_value ()); + m_flags.resize (size, LOCAL); + } + + stack_frame::scope_flags get_scope_flag (size_t data_offset) const + { + return m_flags.at (data_offset); + } + + void set_scope_flag (size_t data_offset, scope_flags flag) + { + m_flags.at (data_offset) = flag; + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_auto_vars.at (avt); + } + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_auto_vars.at (avt) = val; + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (size_t data_offset) const + { + return m_values.at (data_offset); + } + + octave_value& varref (size_t data_offset) + { + return m_values.at (data_offset); + } + + void display (bool follow = true) const; + + protected: + + // Variable values. This array is indexed by the data_offset + // value stored in the symbol_record objects of the scope + // associated with this stack frame. + std::vector m_values; + + // The type of each variable (local, global, persistent) of each + // value. This array is indexed by the data_offset value stored + // in the symbol_record objects of the scope associated with this + // stack frame. Local values are found in the M_VALUES array. + // Global values are stored in the tree_evaluator object that contains + // the stack frame. Persistent values are stored in the function + // scope corresponding to the stack frame. + std::vector m_flags; + + // A fixed list of Automatic variables created for this function. + // The elements of this vector correspond to the auto_var_type + // enum. + std::vector m_auto_vars; + }; + + // User-defined functions have a symbol_scope object to store the set + // of variables in the function and values are stored in the stack + // frame corresponding to the invocation of the function or one of + // its parents. The frame offset tells us how many access links we + // must follow to find the stack frame that holds the value. The + // value offset is the index into the vector of values in that stack + // frame that we should use to find the value. + // + // Frame and value offsets are determined when the corresponding + // function is parsed. + + class user_fcn_stack_frame : public base_value_stack_frame + { + public: + + user_fcn_stack_frame (void) = delete; + + user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn, + unwind_protect *up_frame, size_t index, + stack_frame *static_link, + stack_frame *access_link = nullptr) + : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link, + (access_link + ? access_link + : get_access_link (fcn, static_link))), + m_fcn (fcn), m_unwind_protect_frame (up_frame) + { } + + user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default; + + user_fcn_stack_frame& + operator = (const user_fcn_stack_frame& elt) = delete; + + ~user_fcn_stack_frame (void) = default; + + user_fcn_stack_frame * dup (void) const; + + bool is_user_fcn_frame (void) const { return true; } + + static stack_frame * + get_access_link (octave_user_function *fcn, stack_frame *static_link); + + static size_t get_num_symbols (octave_user_function *fcn) + { + symbol_scope fcn_scope = fcn->scope (); + + return fcn_scope.num_symbols (); + } + + void clear_values (void); + + symbol_scope get_scope (void) const { return m_fcn->scope (); } + + octave_function * function (void) const { return m_fcn; } + + unwind_protect * + unwind_protect_frame (void) const { return m_unwind_protect_frame; } + + symbol_record lookup_symbol (const std::string& name) const; + + symbol_record insert_symbol (const std::string&); + + scope_flags scope_flag (const symbol_record& sym) const; + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using base_value_stack_frame::varval; + using base_value_stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // User-defined object associated with this stack frame. Should + // always be valid. + octave_user_function *m_fcn; + + // The nearest unwind protect frame that was active when this + // stack frame was created. Should always be valid. + unwind_protect *m_unwind_protect_frame; + }; + + // Pure scope stack frames (primarily the top-level workspace) have + // a set of variables and values are stored in the stack frame. All + // variable accesses are direct as there are no parent stack frames. + // + // Value offsets are determined when the corresponding variable is + // entered into the symbol_scope object corresponding to the frame. + + class scope_stack_frame : public base_value_stack_frame + { + public: + + scope_stack_frame (void) = delete; + + scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope, + size_t index, stack_frame *static_link) + : base_value_stack_frame (tw, scope.num_symbols (), index, + static_link, nullptr), + m_scope (scope) + { } + + scope_stack_frame (const scope_stack_frame& elt) = default; + + scope_stack_frame& operator = (const scope_stack_frame& elt) = delete; + + ~scope_stack_frame (void) = default; + + scope_stack_frame * dup (void) const; + + bool is_scope_frame (void) const { return true; } + + symbol_scope get_scope (void) const { return m_scope; } + + symbol_record lookup_symbol (const std::string& name) const + { + return m_scope.lookup_symbol (name); + } + + symbol_record insert_symbol (const std::string&); + + scope_flags scope_flag (const symbol_record& sym) const; + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using base_value_stack_frame::varval; + using base_value_stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // The scope object associated with this stack frame. + symbol_scope m_scope; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/strfind.cc --- a/libinterp/corefcn/strfind.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/strfind.cc Fri Jul 12 12:14:43 2019 -0400 @@ -36,6 +36,7 @@ #include "builtin-defun-decls.h" #include "defun.h" #include "errwarn.h" +#include "error.h" #include "ov.h" #include "unwind-prot.h" #include "utils.h" diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/strfns.cc --- a/libinterp/corefcn/strfns.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/strfns.cc Fri Jul 12 12:14:43 2019 -0400 @@ -1030,6 +1030,42 @@ %!assert (unicode_idx (["aäou"; "Ä∞"]), [1 2 2 3 4; 5 5 6 6 6]); */ +DEFUN (newline, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} newline +Return the character corresponding to a newline. + +This is equivalent to @qcode{"@xbackslashchar{}n"}. + +Example Code + +@example +@group +joined_string = [newline "line1" newline "line2"] +@result{} +line1 +line2 +@end group +@end example + +@seealso{strcat, strjoin, strsplit} +@end deftypefn */) +{ + if (args.length () != 0) + print_usage (); + + static octave_value_list retval = ovl ("\n"); + + return retval; +} + +/* +%!assert (newline (), "\n") + +%!error newline (1) +%!error [a, b] = newline (); +*/ + DEFUN (list_in_columns, args, , doc: /* -*- texinfo -*- @deftypefn {} {} list_in_columns (@var{arg}, @var{width}, @var{prefix}) diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/svd.cc --- a/libinterp/corefcn/svd.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/svd.cc Fri Jul 12 12:14:43 2019 -0400 @@ -372,6 +372,8 @@ %! [~,~,v] = svd ([1, 1, 1], "econ"); %! assert (size (v), [3 1]); +%!assert <*55710> (1 / svd (-0), Inf) + %!error svd () %!error svd ([1, 2; 4, 5], 2, 3) */ diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/syminfo-accumulator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/syminfo-accumulator.h Fri Jul 12 12:14:43 2019 -0400 @@ -0,0 +1,304 @@ +/* + +Copyright (C) 2018 John W. Eaton + +This file is part of Octave. + +Octave 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. + +Octave 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 Octave; see the file COPYING. If not, see +. + +*/ + +#if ! defined (octave_syminfo_accumulator_h) +#define octave_syminfo_accumulator_h 1 + +#include "octave-config.h" + +#include + +#include "oct-map.h" +#include "ov.h" +#include "stack-frame.h" +#include "syminfo.h" +#include "symrec.h" + +namespace octave +{ + class symbol_info_accumulator : public stack_frame_walker + { + public: + + symbol_info_accumulator (const std::string& pattern, + bool have_regexp = false) + : stack_frame_walker (), m_patterns (pattern), m_match_all (false), + m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), + m_found_names () + { } + + symbol_info_accumulator (const string_vector& patterns, + bool have_regexp = false) + : stack_frame_walker (), m_patterns (patterns), m_match_all (false), + m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), + m_found_names () + { } + + symbol_info_accumulator (bool match_all = true, bool first_only = true) + : stack_frame_walker (), m_patterns (), m_match_all (match_all), + m_first_only (first_only), m_have_regexp (false), + m_sym_inf_list (), m_found_names () + { } + + symbol_info_accumulator (const symbol_info_accumulator&) = delete; + + symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete; + + ~symbol_info_accumulator (void) = default; + + bool is_empty (void) const + { + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + if (! lst.empty ()) + return false; + } + + return true; + } + + std::list names (void) const + { + std::list retval; + + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + std::list nm_list = lst.names (); + + for (const auto& nm : nm_list) + retval.push_back (nm); + } + + return retval; + } + + symbol_info_list symbol_info (void) const + { + symbol_info_list retval; + + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + for (const auto& syminf : lst) + retval.push_back (syminf); + } + + return retval; + } + + octave_map map_value (void) const + { + octave_map retval; + + // FIXME: is there a better way to concatenate structures? + + size_t n_frames = m_sym_inf_list.size (); + + OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames); + + size_t j = 0; + for (const auto& nm_sil : m_sym_inf_list) + { + std::string scope_name = nm_sil.first; + const symbol_info_list& lst = nm_sil.second; + + map_list[j] = lst.map_value (scope_name, n_frames-j); + + j++; + } + + return octave_map::cat (-1, n_frames, map_list); + } + + void display (std::ostream& os, const std::string& format) const + { + for (const auto& nm_sil : m_sym_inf_list) + { + os << "\nvariables in scope: " << nm_sil.first << "\n\n"; + + const symbol_info_list& lst = nm_sil.second; + + lst.display (os, format); + } + } + + void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame) + { + // This one follows static link always. Hmm, should the access + // link for a compiled_fcn_stack_frame be the same as the static + // link? + + stack_frame *slink = frame.static_link (); + + if (slink) + slink->accept (*this); + } + + void visit_script_stack_frame (script_stack_frame& frame) + { + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame) + { + append_list (frame); + + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_scope_stack_frame (scope_stack_frame& frame) + { + append_list (frame); + + stack_frame *alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + private: + + typedef std::pair syminf_list_elt; + + // FIXME: the following is too complex and duplicates too much + // code. Maybe it should be split up so we have separate classes + // that do each job that is needed? + + std::list + filter (stack_frame& frame, const std::list& symbols) + { + std::list new_symbols; + + if (m_match_all) + { + for (const auto& sym : symbols) + { + if (frame.is_defined (sym)) + { + std::string name = sym.name (); + + if (m_first_only + && m_found_names.find (name) != m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + else if (m_have_regexp) + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + regexp pat (pattern); + + for (const auto& sym : symbols) + { + std::string name = sym.name (); + + if (pat.is_match (name) && frame.is_defined (sym)) + { + if (m_first_only + && m_found_names.find (name) != m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + } + else + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + glob_match pat (pattern); + + for (const auto& sym : symbols) + { + std::string name = sym.name (); + + if (pat.match (name) && frame.is_defined (sym)) + { + if (m_first_only + && m_found_names.find (name) == m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + } + + return new_symbols; + } + + void append_list (stack_frame& frame) + { + symbol_scope scope = frame.get_scope (); + + std::list symbols = scope.symbol_list (); + + if (m_match_all || ! m_patterns.empty ()) + symbols = filter (frame, symbols); + + symbol_info_list syminf_list = frame.make_symbol_info_list (symbols); + + m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list)); + } + + string_vector m_patterns; + + bool m_match_all; + bool m_first_only; + bool m_have_regexp; + + std::list> m_sym_inf_list; + + std::set m_found_names; + }; +} + +#endif diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/syminfo.cc --- a/libinterp/corefcn/syminfo.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/syminfo.cc Fri Jul 12 12:14:43 2019 -0400 @@ -30,6 +30,7 @@ #include #include "Cell.h" +#include "error.h" #include "octave-preserve-stream-state.h" #include "ov.h" #include "oct-map.h" @@ -46,7 +47,7 @@ auto i = params.begin (); - octave::preserve_stream_state stream_state (os); + preserve_stream_state stream_state (os); while (i != params.end ()) { @@ -105,14 +106,13 @@ { case 'a': { - char tmp[6]; + char tmp[5]; - tmp[0] = (m_is_automatic ? 'a' : ' '); - tmp[1] = (m_is_complex ? 'c' : ' '); - tmp[2] = (m_is_formal ? 'f' : ' '); - tmp[3] = (m_is_global ? 'g' : ' '); - tmp[4] = (m_is_persistent ? 'p' : ' '); - tmp[5] = 0; + tmp[0] = (m_is_complex ? 'c' : ' '); + tmp[1] = (m_is_formal ? 'f' : ' '); + tmp[2] = (m_is_global ? 'g' : ' '); + tmp[3] = (m_is_persistent ? 'p' : ' '); + tmp[4] = 0; os << tmp; } @@ -173,6 +173,16 @@ return octave_value (); } + std::list symbol_info_list::names (void) const + { + std::list retval; + + for (const auto& syminfo : m_lst) + retval.push_back (syminfo.name ()); + + return retval; + } + octave_map symbol_info_list::map_value (const std::string& caller_function_name, int nesting_level) const @@ -235,7 +245,7 @@ { std::ostringstream param_buf; - octave::preserve_stream_state stream_state (os); + preserve_stream_state stream_state (os); for (const auto& param : params) { @@ -314,7 +324,8 @@ os << param_buf.str (); } - void symbol_info_list::display (std::ostream& os, const std::string& format) + void symbol_info_list::display (std::ostream& os, + const std::string& format) const { if (! m_lst.empty ()) { @@ -345,7 +356,7 @@ } std::list - symbol_info_list::parse_whos_line_format (const std::string& format) + symbol_info_list::parse_whos_line_format (const std::string& format) const { int idx; size_t format_len = format.length (); diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/syminfo.h --- a/libinterp/corefcn/syminfo.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/syminfo.h Fri Jul 12 12:14:43 2019 -0400 @@ -52,19 +52,16 @@ public: symbol_info (const std::string& name, const octave_value& value, - bool is_automatic, bool is_complex, bool is_formal, - bool is_global, bool is_persistent) - : m_name (name), m_value (value), m_is_automatic (is_automatic), - m_is_complex (is_complex), m_is_formal (is_formal), - m_is_global (is_global), m_is_persistent (is_persistent) + bool is_formal, bool is_global, bool is_persistent) + : m_name (name), m_value (value), m_is_complex (value.iscomplex ()), + m_is_formal (is_formal), m_is_global (is_global), + m_is_persistent (is_persistent) { } std::string name (void) const { return m_name; } octave_value value (void) const { return m_value; } - bool is_automatic (void) const { return m_is_automatic; } - bool is_complex (void) const { return m_is_complex; } bool is_formal (void) const { return m_is_formal; } @@ -79,7 +76,6 @@ std::string m_name; octave_value m_value; - bool m_is_automatic; bool m_is_complex; bool m_is_formal; bool m_is_global; @@ -100,6 +96,8 @@ octave_value varval (const std::string& name) const; + std::list names (void) const; + octave_map map_value (const std::string& caller_function_name, int nesting_level) const; @@ -107,13 +105,13 @@ void print_descriptor (std::ostream& os, const std::list params) const; - void display (std::ostream& os, const std::string& format); + void display (std::ostream& os, const std::string& format) const; // Parse FORMAT, and return a parameter list, // containing all information needed to print the given // attributes of the symbols. std::list - parse_whos_line_format (const std::string& format); + parse_whos_line_format (const std::string& format) const; }; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/symrec.cc --- a/libinterp/corefcn/symrec.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/symrec.cc Fri Jul 12 12:14:43 2019 -0400 @@ -36,86 +36,25 @@ #include "interpreter-private.h" #include "interpreter.h" #include "symrec.h" -#include "symscope.h" -#include "symtab.h" namespace octave { - symbol_record::context_id - symbol_record::symbol_record_rep::get_fwd_scope_context (void) const - { - // This should NOT recurse. We only want to know the current - // context of the immediately forwarded rep object. This is used - // only in symbol_record::symbol_record_rep::varref and - // symbol_record::symbol_record_rep::varval. - - auto t_fwd_scope = m_fwd_scope.lock (); - return t_fwd_scope ? t_fwd_scope->current_context () : 0; - } - - void - symbol_record::symbol_record_rep::init_persistent (void) + std::shared_ptr + symbol_record::symbol_record_rep::dup (void) const { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->init_persistent (); - return; - } - - mark_persistent (); - } - - std::shared_ptr - symbol_record::symbol_record_rep::dup (const std::shared_ptr& new_scope) const - { - // FIXME: is this the right thing do to? - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->dup (new_scope); - - static const context_id FIXME_CONTEXT = 0; - - return std::shared_ptr - (new symbol_record_rep (m_name, varval (FIXME_CONTEXT), - m_storage_class)); + return std::shared_ptr (new symbol_record_rep (*this)); } octave_value - symbol_record::symbol_record_rep::dump (context_id context) const + symbol_record::symbol_record_rep::dump (void) const { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->dump (context); - std::map m - = {{ "name", m_name }, + = {{ "frame_offset", m_frame_offset }, + { "data_offset", m_data_offset }, + { "name", m_name }, { "local", is_local () }, - { "automatic", is_automatic () }, - { "formal", is_formal () }, - { "hidden", is_hidden () }, - { "inherited", is_inherited () }, - { "global", is_global () }, - { "persistent", is_persistent () }}; - - octave_value val = varval (context); - - if (val.is_defined ()) - m["value"] = val; + { "formal", is_formal () }}; return octave_value (m); } - - octave_value - symbol_record::find_function (const std::string& name, - const octave_value_list& args) const - { - // FIXME: it would be better if the symbol_record object did not - // look back to the symbol_table when the value is undefined. More - // refactoring is needed... - - symbol_table& symtab - = __get_symbol_table__ ("symbol_record::find_function"); - - return symtab.find_function (name, args); - } - - octave_value symbol_record::dummy_octave_value; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/symrec.h --- a/libinterp/corefcn/symrec.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/symrec.h Fri Jul 12 12:14:43 2019 -0400 @@ -49,28 +49,12 @@ // generic variable static const unsigned int local = 1; - // varargin, argn, .nargin., .nargout. - // (FIXME: is this really used now?) - static const unsigned int automatic = 2; - // formal parameter - static const unsigned int formal = 4; - - // not listed or cleared (.nargin., .nargout.) - static const unsigned int hidden = 8; - - // inherited from parent scope; not cleared at function exit - static const unsigned int inherited = 16; - - // global (redirects to global scope) - static const unsigned int global = 32; - - // not cleared at function exit - static const unsigned int persistent = 64; + static const unsigned int formal = 2; // this symbol may NOT become a variable. // (symbol added to a static workspace) - static const unsigned int added_static = 128; + static const unsigned int added_static = 4; private: @@ -78,470 +62,78 @@ { public: - symbol_record_rep (const std::string& nm, const octave_value& v, - unsigned int sc) - : m_storage_class (sc), m_name (nm), m_fwd_scope (), - m_fwd_rep (), m_value_stack () - { - m_value_stack.push_back (v); - } + symbol_record_rep (const std::string& nm, unsigned int sc) + : m_frame_offset (0), m_data_offset (0), m_storage_class (sc), + m_name (nm) + { } - // No copying! + symbol_record_rep (const symbol_record_rep&) = default; - symbol_record_rep (const symbol_record_rep& ov) = delete; - - symbol_record_rep& operator = (const symbol_record_rep&) = delete; + symbol_record_rep& operator = (const symbol_record_rep&) = default; ~symbol_record_rep (void) = default; - void assign (const octave_value& value, context_id context) - { - varref(context) = value; - } - - void assign (octave_value::assign_op op, - const std::string& type, - const std::list& idx, - const octave_value& value, context_id context) - { - varref(context).assign (op, type, idx, value); - } - - void assign (octave_value::assign_op op, const octave_value& value, - context_id context) - { - varref(context).assign (op, value); - } - - void do_non_const_unary_op (octave_value::unary_op op, - context_id context) - { - varref(context).do_non_const_unary_op (op); - } + // FIXME: use special storage class instead? + bool is_valid (void) const { return ! m_name.empty (); } - void do_non_const_unary_op (octave_value::unary_op op, - const std::string& type, - const std::list& idx, - context_id context) - { - varref(context).do_non_const_unary_op (op, type, idx); - } - - context_id get_fwd_scope_context (void) const; - - octave_value& varref (context_id context) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->varref (get_fwd_scope_context ()); - - if (is_persistent ()) - context = 0; - - context_id n = m_value_stack.size (); - while (n++ <= context) - m_value_stack.push_back (octave_value ()); - - return m_value_stack[context]; - } - - octave_value varval (context_id context) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->varval (get_fwd_scope_context ()); - - if (is_persistent ()) - context = 0; - - if (context < m_value_stack.size ()) - return m_value_stack[context]; - else - return octave_value (); - } + void set_frame_offset (size_t offset) { m_frame_offset = offset; } - void push_context (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return; - - if (! (is_persistent () || is_global ())) - m_value_stack.push_back (octave_value ()); - } - - // If pop_context returns 0, we are out of values and this element - // of the symbol table should be deleted. This can happen for - // functions like - // - // function foo (n) - // if (n > 0) - // foo (n-1); - // else - // eval ("x = 1"); - // endif - // endfunction - // - // Here, X should only exist in the final stack frame. - - context_id pop_context (void) - { - context_id retval = 1; - - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return retval; - - if (! (is_persistent () || is_global ())) - { - m_value_stack.pop_back (); - retval = m_value_stack.size (); - } + size_t frame_offset (void) const { return m_frame_offset; } - return retval; - } - - void clear (context_id context) - { - // For globals, break the link to the global value first, then - // clear the local value. - - if (is_global ()) - unbind_global_rep (); - - if (! (is_hidden () || is_inherited ())) - { - assign (octave_value (), context); - - // For persistent values, we clear the value then unmark so - // that we clear the first element of the value stack. + void set_data_offset (size_t offset) { m_data_offset = offset; } - if (is_persistent ()) - unmark_persistent (); - } - } - - bool is_defined (context_id context) const - { - return varval (context).is_defined (); - } - - bool is_variable (context_id context) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_variable (context); - - return (! is_local () || is_defined (context)); - } + size_t data_offset (void) const { return m_data_offset; } bool is_local (void) const { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_local (); - return m_storage_class & local; } - bool is_automatic (void) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_automatic (); - - return m_storage_class & automatic; - } - bool is_formal (void) const { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_formal (); - return m_storage_class & formal; } - bool is_hidden (void) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_hidden (); - - return m_storage_class & hidden; - } - - bool is_inherited (void) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_inherited (); - - return m_storage_class & inherited; - } - - bool is_forwarded (void) const - { - return ! m_fwd_rep.expired (); - } - - bool is_global (void) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_global (); - - return is_marked_global (); - } - - bool is_persistent (void) const - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_persistent (); - - return m_storage_class & persistent; - } - bool is_added_static (void) const { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - return t_fwd_rep->is_added_static (); - return m_storage_class & added_static; } void mark_local (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_local (); - return; - } - m_storage_class |= local; } - void mark_automatic (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_automatic (); - return; - } - - m_storage_class |= automatic; - } - void mark_formal (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_formal (); - return; - } - m_storage_class |= formal; } - void mark_hidden (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_hidden (); - return; - } - - m_storage_class |= hidden; - } - - void mark_inherited (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_inherited (); - return; - } - - m_storage_class |= inherited; - } - - // This flag should only be set for a symbol record that is - // actually in the global symbol_scope, and that should only - // happen when it is added to the global symbol_scope. - - void mark_global (void) - { - m_storage_class |= global; - } - - bool is_marked_global (void) const - { - return m_storage_class & global; - } - - void mark_persistent (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_persistent (); - return; - } - - if (is_global ()) - error ("can't make global variable %s persistent", m_name.c_str ()); - - if (is_formal ()) - error ("can't make function parameter %s persistent", m_name.c_str ()); - m_storage_class |= persistent; - } - void mark_added_static (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->mark_added_static (); - return; - } - m_storage_class |= added_static; } void unmark_local (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_local (); - return; - } - m_storage_class &= ~local; } - void unmark_automatic (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_automatic (); - return; - } - - m_storage_class &= ~automatic; - } - void unmark_formal (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_formal (); - return; - } - m_storage_class &= ~formal; } - void unmark_hidden (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_hidden (); - return; - } - - m_storage_class &= ~hidden; - } - - void unmark_inherited (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_inherited (); - return; - } - - m_storage_class &= ~inherited; - } - - void unmark_persistent (void) - { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_persistent (); - return; - } - - m_storage_class &= ~persistent; - } - void unmark_added_static (void) { - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - t_fwd_rep->unmark_added_static (); - return; - } - m_storage_class &= ~added_static; } unsigned int storage_class (void) const { return m_storage_class; } - void init_persistent (void); - - void bind_fwd_rep (const std::shared_ptr& fwd_scope, - const std::shared_ptr& fwd_rep) - { - // If this object is already bound to another scope (for - // example, a variable in a script or nested function is bound - // to the enclosing scope), then bind that object to the next - // scope. FIXME: can this happen for any other reason than we - // are making a variable in a script global? - - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - // If this is the symbol in the global scope, then don't - // forward again! - - if (t_fwd_rep->is_marked_global ()) - return; - - t_fwd_rep->bind_fwd_rep (fwd_scope, fwd_rep); - } - - // Don't bind forward rep to this! Avoids crash reported in - // bug #55728. - - if (this == fwd_rep.get ()) - return; - - m_fwd_scope = fwd_scope; - m_fwd_rep = fwd_rep; - } + std::shared_ptr dup (void) const; - void unbind_fwd_rep (void) - { - // When unbinding variables in a script scope, we only break - // the immediate link. By doing that, we ensure that any - // variables that are made global in a script remain linked as - // globals in the enclosing scope. - - m_fwd_scope = std::weak_ptr (); - m_fwd_rep.reset (); - } - - void unbind_global_rep (void) - { - // Break the link to the global symbol_record_rep. These must - // forwarded, so we don't do anything unless the forward rep - // points to something. - - if (auto t_fwd_rep = m_fwd_rep.lock ()) - { - if (t_fwd_rep->is_marked_global ()) - { - // The rep this object points to is in the global - // scope, so delete the link to it. - - m_fwd_scope = std::weak_ptr (); - m_fwd_rep.reset (); - } - else - t_fwd_rep->unbind_global_rep (); - } - } - - std::shared_ptr - dup (const std::shared_ptr& new_scope) const; - - octave_value dump (context_id context) const; + octave_value dump (void) const; std::string name (void) const { return m_name; } @@ -549,23 +141,23 @@ private: + size_t m_frame_offset; + size_t m_data_offset; + unsigned int m_storage_class; std::string m_name; - - std::weak_ptr m_fwd_scope; - - std::weak_ptr m_fwd_rep; - - std::deque m_value_stack; }; public: - symbol_record (const std::string& nm = "", - const octave_value& v = octave_value (), + symbol_record (const std::string& nm = "", unsigned int sc = local) + : m_rep (new symbol_record_rep (nm, sc)) + { } + + symbol_record (const std::string& nm, const octave_value&, unsigned int sc = local) - : m_rep (new symbol_record_rep (nm, v, sc)) + : m_rep (new symbol_record_rep (nm, sc)) { } symbol_record (const symbol_record&) = default; @@ -574,132 +166,39 @@ ~symbol_record (void) = default; - symbol_record dup (const std::shared_ptr& sid) const - { - return symbol_record (m_rep->dup (sid)); - } + bool is_valid (void) const { return m_rep->is_valid (); } + + explicit operator bool () const { return is_valid (); } + + void set_frame_offset (size_t offset) { m_rep->set_frame_offset (offset); } + + size_t frame_offset (void) const { return m_rep->frame_offset (); } + + void set_data_offset (size_t offset) { m_rep->set_data_offset (offset); } + + size_t data_offset (void) const { return m_rep->data_offset (); } + + symbol_record dup (void) const { return symbol_record (m_rep->dup ()); } std::string name (void) const { return m_rep->name (); } void rename (const std::string& new_name) { m_rep->rename (new_name); } - octave_value - find (context_id context, - const octave_value_list& args = octave_value_list ()) const - { - octave_value retval = varval (context); - - if (retval.is_undefined ()) - return find_function (name (), args); - - return retval; - } - - void assign (const octave_value& value, context_id context) - { - m_rep->assign (value, context); - } - - void assign (octave_value::assign_op op, - const std::string& type, - const std::list& idx, - const octave_value& value, context_id context) - { - m_rep->assign (op, type, idx, value, context); - } - - void assign (octave_value::assign_op op, const octave_value& value, - context_id context) - { - m_rep->assign (op, value, context); - } - - void do_non_const_unary_op (octave_value::unary_op op, context_id context) - { - m_rep->do_non_const_unary_op (op, context); - } - - void do_non_const_unary_op (octave_value::unary_op op, - const std::string& type, - const std::list& idx, - context_id context) - { - m_rep->do_non_const_unary_op (op, type, idx, context); - } - - octave_value varval (context_id context) const - { - return m_rep->varval (context); - } - - void push_context (void) { m_rep->push_context (); } - - context_id pop_context (void) { return m_rep->pop_context (); } - - void clear (context_id context) { m_rep->clear (context); } - - bool is_defined (context_id context) const - { - return m_rep->is_defined (context); - } - - bool is_undefined (context_id context) const - { - return ! m_rep->is_defined (context); - } - - bool is_variable (context_id context) const - { - return m_rep->is_variable (context); - } - bool is_local (void) const { return m_rep->is_local (); } - bool is_automatic (void) const { return m_rep->is_automatic (); } bool is_formal (void) const { return m_rep->is_formal (); } - bool is_global (void) const { return m_rep->is_global (); } - bool is_hidden (void) const { return m_rep->is_hidden (); } - bool is_inherited (void) const { return m_rep->is_inherited (); } - bool is_forwarded (void) const { return m_rep->is_forwarded (); } - bool is_persistent (void) const { return m_rep->is_persistent (); } bool is_added_static (void) const { return m_rep->is_added_static (); } void mark_local (void) { m_rep->mark_local (); } - void mark_automatic (void) { m_rep->mark_automatic (); } void mark_formal (void) { m_rep->mark_formal (); } - void mark_hidden (void) { m_rep->mark_hidden (); } - void mark_inherited (void) { m_rep->mark_inherited (); } - void mark_persistent (void) { m_rep->mark_persistent (); } void mark_added_static (void) { m_rep->mark_added_static (); } void unmark_local (void) { m_rep->unmark_local (); } - void unmark_automatic (void) { m_rep->unmark_automatic (); } void unmark_formal (void) { m_rep->unmark_formal (); } - void unmark_hidden (void) { m_rep->unmark_hidden (); } - void unmark_inherited (void) { m_rep->unmark_inherited (); } - void unmark_persistent (void) { m_rep->unmark_persistent (); } void unmark_added_static (void) { m_rep->unmark_added_static (); } - bool is_marked_global (void) const { return m_rep->is_marked_global (); } - void mark_global (void) { m_rep->mark_global (); } - - void init_persistent (void) { m_rep->init_persistent (); } - unsigned int storage_class (void) const { return m_rep->storage_class (); } - void bind_fwd_rep (const std::shared_ptr& fwd_scope, - const symbol_record& sr) - { - m_rep->bind_fwd_rep (fwd_scope, sr.m_rep); - } - - void unbind_global_rep (void) { m_rep->unbind_global_rep (); } - - void unbind_fwd_rep (void) { m_rep->unbind_fwd_rep (); } - - octave_value dump (context_id context) const - { - return m_rep->dump (context); - } + octave_value dump (void) const { return m_rep->dump (); } private: @@ -709,11 +208,6 @@ symbol_record (const std::shared_ptr& new_rep) : m_rep (new_rep) { } - - octave_value find_function (const std::string& name, - const octave_value_list& args) const; - - static octave_value dummy_octave_value; }; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/symscope.cc --- a/libinterp/corefcn/symscope.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/symscope.cc Fri Jul 12 12:14:43 2019 -0400 @@ -34,66 +34,30 @@ #include "ov-usr-fcn.h" #include "symrec.h" #include "symscope.h" -#include "symtab.h" #include "utils.h" namespace octave { - void symbol_scope_rep::install_auto_fcn_vars (void) + symbol_record symbol_scope_rep::insert_local (const std::string& name) { - install_auto_fcn_var (".argn."); - install_auto_fcn_var (".ignored."); - install_auto_fcn_var (".nargin."); - install_auto_fcn_var (".nargout."); - install_auto_fcn_var (".saved_warning_states."); - } + symbol_record sym (name); - void symbol_scope_rep::install_auto_fcn_var (const std::string& name) - { - insert (name, true); - mark_hidden (name); - mark_automatic (name); + insert_symbol_record (sym); + + return sym; } - octave_value - symbol_scope_rep::find (const std::string& name) + void symbol_scope_rep::insert_symbol_record (symbol_record& sr) { - symbol_table& symtab - = __get_symbol_table__ ("symbol_scope_rep::find"); - - // Variable. - - table_iterator p = m_symbols.find (name); - - if (p != m_symbols.end ()) - { - symbol_record sr = p->second; + size_t data_offset = num_symbols (); + std::string name = sr.name (); - if (sr.is_global ()) - return symtab.global_varval (name); - else - { - octave_value val = sr.varval (m_context); - - if (val.is_defined ()) - return val; - } - } + sr.set_data_offset (data_offset); - // Subfunction. I think it only makes sense to check for - // subfunctions if we are currently executing a function defined - // from a .m file. - - octave_value fcn = find_subfunction (name); - - if (fcn.is_defined ()) - return fcn; - - return symtab.fcn_table_find (name, ovl ()); + m_symbols[name] = sr; } - symbol_record& - symbol_scope_rep::insert (const std::string& name, bool force_add) + symbol_record symbol_scope_rep::insert (const std::string& name) { table_iterator p = m_symbols.find (name); @@ -101,13 +65,20 @@ { symbol_record ret (name); + size_t data_offset = num_symbols (); + + ret.set_data_offset (data_offset); + auto t_parent = m_parent.lock (); - if (is_nested () && t_parent && t_parent->look_nonlocal (name, ret)) + size_t offset = 0; + + if (is_nested () && t_parent + && t_parent->look_nonlocal (name, offset, ret)) return m_symbols[name] = ret; else { - if (m_is_static && ! force_add) + if (m_is_static) ret.mark_added_static (); return m_symbols[name] = ret; @@ -139,13 +110,23 @@ for (const auto& nm_sr : m_symbols) { std::string nm = nm_sr.first; - const symbol_record& sr = nm_sr.second; - info_map[nm] = sr.dump (m_context); + symbol_record sr = nm_sr.second; + info_map[nm] = sr.dump (); } return octave_value (info_map); } + std::list symbol_scope_rep::symbol_list (void) const + { + std::list retval; + + for (const auto& nm_sr : m_symbols) + retval.push_back (nm_sr.second); + + return retval; + } + octave_value symbol_scope_rep::find_subfunction (const std::string& name) const { @@ -194,18 +175,30 @@ // Since is_nested is true, the following should always return a // valid scope. + auto t_parent = m_parent.lock (); + + if (t_parent) + { + // SCOPE is the parent of this scope: this scope is a child + // of SCOPE. + + if (t_parent == scope) + return true; + } + auto t_primary_parent = m_primary_parent.lock (); if (t_primary_parent) { // SCOPE is the primary parent of this scope: this scope is a - // child of SCOPE. + // child (or grandchild) of SCOPE. if (t_primary_parent == scope) return true; // SCOPE and this scope share the same primary parent: they are - // siblings. - if (t_primary_parent == scope->primary_parent_scope_rep ()) + // siblings (or cousins) + auto scope_primary_parent = scope->primary_parent_scope_rep (); + if (t_primary_parent == scope_primary_parent) return true; } } @@ -213,8 +206,7 @@ return false; } - void - symbol_scope_rep::update_nest (void) + void symbol_scope_rep::update_nest (void) { auto t_parent = m_parent.lock (); @@ -225,12 +217,10 @@ { symbol_record& ours = nm_sr.second; - if (! ours.is_formal () - && is_nested () && t_parent->look_nonlocal (nm_sr.first, ours)) - { - if (ours.is_global () || ours.is_persistent ()) - error ("global and persistent may only be used in the topmost level in which a nested variable is used"); - } + size_t offset = 0; + + if (! ours.is_formal () && is_nested ()) + t_parent->look_nonlocal (nm_sr.first, offset, ours); } // The scopes of nested functions are static. @@ -247,40 +237,37 @@ scope_obj.update_nest (); } - bool - symbol_scope_rep::look_nonlocal (const std::string& name, - symbol_record& result) + bool symbol_scope_rep::look_nonlocal (const std::string& name, + size_t offset, symbol_record& result) { + offset++; + table_iterator p = m_symbols.find (name); + if (p == m_symbols.end ()) { auto t_parent = m_parent.lock (); if (is_nested () && t_parent) - return t_parent->look_nonlocal (name, result); + return t_parent->look_nonlocal (name, offset, result); } - else if (! p->second.is_automatic ()) + else { - result.bind_fwd_rep (shared_from_this (), p->second); + // Add scope offsets because the one we found may be used in + // this scope but initially from another parent scope beyond + // that. The parent offset will already point to the first + // occurrence because we do the overall nesting update from the + // parent function down through the lists of all children. + + size_t t_frame_offset = offset + p->second.frame_offset (); + size_t t_data_offset = p->second.data_offset (); + + result.set_frame_offset (t_frame_offset); + result.set_data_offset (t_data_offset); + return true; } return false; } - - void - symbol_scope_rep::bind_script_symbols - (const std::shared_ptr& curr_scope) - { - for (auto& nm_sr : m_symbols) - nm_sr.second.bind_fwd_rep (curr_scope, - curr_scope->find_symbol (nm_sr.first)); - } - - void - symbol_scope_rep::unbind_script_symbols (void) - { - for (auto& nm_sr : m_symbols) - nm_sr.second.unbind_fwd_rep (); - } } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/symscope.h --- a/libinterp/corefcn/symscope.h Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/symscope.h Fri Jul 12 12:14:43 2019 -0400 @@ -64,10 +64,15 @@ subfunctions_iterator; symbol_scope_rep (const std::string& name = "") - : m_name (name), m_symbols (), m_subfunctions (), m_fcn (nullptr), - m_parent (), m_primary_parent (), m_children (), - m_nesting_depth (0), m_is_static (false), m_context (0) - { } + : m_name (name), m_symbols (), m_subfunctions (), + m_persistent_values (), m_fcn (nullptr), m_parent (), + m_primary_parent (), m_children (), m_nesting_depth (0), + m_is_static (false) + { + // All scopes have ans as the first symbol, initially undefined. + + insert_local ("ans"); + } // No copying! @@ -77,14 +82,13 @@ ~symbol_scope_rep (void) = default; - void insert_symbol_record (const symbol_record& sr) - { - m_symbols[sr.name ()] = sr; - } + size_t num_symbols (void) const { return m_symbols.size (); } + + // Simply inserts symbol. No non-local searching. - void install_auto_fcn_vars (void); + symbol_record insert_local (const std::string& name); - void install_auto_fcn_var (const std::string& name); + void insert_symbol_record (symbol_record& sr); bool is_nested (void) const { return m_nesting_depth > 0; } @@ -92,6 +96,8 @@ void set_nesting_depth (size_t depth) { m_nesting_depth = depth; } + bool is_parent (void) const { return ! m_children.empty (); } + bool is_static (void) const { return m_is_static; } void mark_static (void) { m_is_static = true; } @@ -112,22 +118,31 @@ = std::shared_ptr (new symbol_scope_rep (m_name)); for (const auto& nm_sr : m_symbols) - new_sid->insert_symbol_record (nm_sr.second.dup (new_sid)); + new_sid->m_symbols[nm_sr.first] = nm_sr.second.dup (); + new_sid->m_subfunctions = m_subfunctions; + new_sid->m_persistent_values = m_persistent_values; + new_sid->m_subfunction_names = m_subfunction_names; + new_sid->m_fcn = m_fcn; new_sid->m_parent = m_parent; new_sid->m_primary_parent = m_primary_parent; + new_sid->m_children = m_children; + new_sid->m_nesting_depth = m_nesting_depth; + new_sid->m_is_static = m_is_static; return new_sid; } - void set_context (symbol_record::context_id context) + octave_value& persistent_varref (size_t data_offset) { - m_context = context; + return m_persistent_values[data_offset]; } - symbol_record::context_id current_context (void) const + octave_value persistent_varval (size_t data_offset) const { - return m_context; + auto p = m_persistent_values.find (data_offset); + + return p == m_persistent_values.end () ? octave_value () : p->second; } symbol_record find_symbol (const std::string& name) @@ -140,51 +155,14 @@ return p->second; } - void inherit_internal - (const std::shared_ptr& donor_scope_rep) + symbol_record lookup_symbol (const std::string& name) const { - for (auto& nm_sr : m_symbols) - { - symbol_record& sr = nm_sr.second; - - if (! (sr.is_automatic () || sr.is_formal ())) - { - std::string nm = sr.name (); + auto p = m_symbols.find (name); - if (nm != "__retval__") - { - octave_value val = donor_scope_rep->varval (nm); - - if (val.is_defined ()) - { - sr.assign (val, m_context); - - sr.mark_inherited (); - } - } - } - } + return p == m_symbols.end () ? symbol_record () : p->second; } - void inherit (const std::shared_ptr& donor_scope_rep) - { - std::shared_ptr dsr = donor_scope_rep; - - while (dsr) - { - inherit_internal (dsr); - - if (dsr->is_nested ()) - dsr = parent_scope_rep (); - else - break; - } - } - - octave_value find (const std::string& name); - - symbol_record& - insert (const std::string& name, bool force_add = false); + symbol_record insert (const std::string& name); void rename (const std::string& old_name, const std::string& new_name) { @@ -202,289 +180,6 @@ } } - void assign (const std::string& name, const octave_value& value, - bool force_add) - { - auto p = m_symbols.find (name); - - if (p == m_symbols.end ()) - { - symbol_record& sr = insert (name, force_add); - - sr.assign (value, m_context); - } - else - p->second.assign (value, m_context); - } - - void assign (const std::string& name, - const octave_value& value = octave_value ()) - { - assign (name, value, false); - } - - void force_assign (const std::string& name, const octave_value& value) - { - auto p = m_symbols.find (name); - - if (p == m_symbols.end ()) - { - symbol_record& sr = insert (name, true); - - sr.assign (value, m_context); - } - else - p->second.assign (value, m_context); - } - - octave_value varval (const std::string& name) const - { - table_const_iterator p = m_symbols.find (name); - - return (p != m_symbols.end () - ? p->second.varval (m_context) : octave_value ()); - } - - bool is_variable (const std::string& name) const - { - bool retval = false; - - table_const_iterator p = m_symbols.find (name); - - if (p != m_symbols.end ()) - { - const symbol_record& sr = p->second; - - retval = sr.is_variable (m_context); - } - - return retval; - } - - void push_context (void) - { - for (auto& nm_sr : m_symbols) - nm_sr.second.push_context (); - } - - void pop_context (void) - { - auto tbl_it = m_symbols.begin (); - - while (tbl_it != m_symbols.end ()) - { - if (tbl_it->second.pop_context () == 0) - m_symbols.erase (tbl_it++); - else - tbl_it++; - } - } - - void refresh (void) - { - for (auto& nm_sr : m_symbols) - { - symbol_record& sr = nm_sr.second; - - if (sr.is_global ()) - sr.unbind_global_rep (); - else if (! (sr.is_persistent () || sr.is_forwarded ())) - sr.clear (m_context); - } - } - - void clear_variables (void) - { - for (auto& nm_sr : m_symbols) - nm_sr.second.clear (m_context); - } - - void clear_objects (void) - { - for (auto& nm_sr : m_symbols) - { - symbol_record& sr = nm_sr.second; - octave_value val = sr.varval (m_context); - if (val.isobject ()) - nm_sr.second.clear (m_context); - } - } - - void clear_variable (const std::string& name) - { - auto p = m_symbols.find (name); - - if (p != m_symbols.end ()) - p->second.clear (m_context); - else if (is_nested ()) - { - std::shared_ptr psr = parent_scope_rep (); - - if (psr) - psr->clear_variable (name); - } - } - - void clear_variable_pattern (const std::string& pat) - { - glob_match pattern (pat); - - for (auto& nm_sr : m_symbols) - { - symbol_record& sr = nm_sr.second; - - if (sr.is_defined (m_context) || sr.is_global ()) - { - if (pattern.match (sr.name ())) - sr.clear (m_context); - } - } - - if (is_nested ()) - { - std::shared_ptr psr = parent_scope_rep (); - - if (psr) - psr->clear_variable_pattern (pat); - } - } - - void clear_variable_regexp (const std::string& pat) - { - octave::regexp pattern (pat); - - for (auto& nm_sr : m_symbols) - { - symbol_record& sr = nm_sr.second; - - if (sr.is_defined (m_context) || sr.is_global ()) - { - if (pattern.is_match (sr.name ())) - sr.clear (m_context); - } - } - - if (is_nested ()) - { - std::shared_ptr psr = parent_scope_rep (); - - if (psr) - psr->clear_variable_regexp (pat); - } - } - - void mark_automatic (const std::string& name) - { - insert (name).mark_automatic (); - } - - void mark_hidden (const std::string& name) - { - insert (name).mark_hidden (); - } - - void mark_global (const std::string& name) - { - insert (name).mark_global (); - } - - std::list - all_variables (bool defined_only = true, - unsigned int exclude = symbol_record::hidden) const - { - std::list retval; - - for (const auto& nm_sr : m_symbols) - { - const symbol_record& sr = nm_sr.second; - - if ((defined_only && ! sr.is_defined (m_context)) - || (sr.storage_class () & exclude)) - continue; - - retval.push_back (sr); - } - - return retval; - } - - std::list - glob (const std::string& pattern, bool vars_only = false) const - { - std::list retval; - - glob_match pat (pattern); - - for (const auto& nm_sr : m_symbols) - { - if (pat.match (nm_sr.first)) - { - const symbol_record& sr = nm_sr.second; - - if (vars_only && ! sr.is_variable (m_context)) - continue; - - retval.push_back (sr); - } - } - - return retval; - } - - std::list - regexp (const std::string& pattern, bool vars_only = false) const - { - std::list retval; - - octave::regexp pat (pattern); - - for (const auto& nm_sr : m_symbols) - { - if (pat.is_match (nm_sr.first)) - { - const symbol_record& sr = nm_sr.second; - - if (vars_only && ! sr.is_variable (m_context)) - continue; - - retval.push_back (sr); - } - } - - return retval; - } - - std::list variable_names (void) - { - std::list retval; - - for (const auto& nm_sr : m_symbols) - { - if (nm_sr.second.is_variable (m_context)) - retval.push_back (nm_sr.first); - } - - retval.sort (); - - return retval; - } - - bool is_local_variable (const std::string& name) const - { - table_const_iterator p = m_symbols.find (name); - - return (p != m_symbols.end () - && ! p->second.is_global () - && p->second.is_defined (m_context)); - } - - bool is_global (const std::string& name) const - { - table_const_iterator p = m_symbols.find (name); - - return p != m_symbols.end () && p->second.is_global (); - } - void install_subfunction (const std::string& name, const octave_value& fval) { @@ -547,7 +242,7 @@ void cache_name (const std::string& name) { m_name = name; } - octave_user_function *function (void) { return m_fcn; } + octave_user_function *function (void) const { return m_fcn; } void set_function (octave_user_function *fcn) { m_fcn = fcn; } @@ -559,14 +254,23 @@ void update_nest (void); - bool look_nonlocal (const std::string& name, symbol_record& result); - - void bind_script_symbols (const std::shared_ptr& curr_scope); - - void unbind_script_symbols (void); + bool look_nonlocal (const std::string& name, size_t offset, + symbol_record& result); octave_value dump_symbols_map (void) const; + const std::map& symbols (void) const + { + return m_symbols; + } + + std::map& symbols (void) + { + return m_symbols; + } + + std::list symbol_list (void) const; + private: //! Name for this scope (usually the corresponding filename of the @@ -582,6 +286,9 @@ std::map m_subfunctions; + //! Map from data offset to persistent values in this scope. + std::map m_persistent_values; + //! The list of subfunctions (if any) in the order they appear in //! the function file. @@ -611,8 +318,6 @@ //! If true then no variables can be added. bool m_is_static; - - symbol_record::context_id m_context; }; class symbol_scope @@ -640,23 +345,32 @@ explicit operator bool () const { return bool (m_rep); } - void insert_symbol_record (const symbol_record& sr) + size_t num_symbols (void) const + { + return m_rep ? m_rep->num_symbols () : 0; + } + + symbol_record insert_local (const std::string& name) + { + return m_rep ? m_rep->insert_local (name) : symbol_record (); + } + + void insert_symbol_record (symbol_record& sr) { if (m_rep) m_rep->insert_symbol_record (sr); } - void install_auto_fcn_vars (void) - { - if (m_rep) - m_rep->install_auto_fcn_vars (); - } - bool is_nested (void) const { return m_rep ? m_rep->is_nested () : false; } + bool is_parent (void) const + { + return m_rep ? m_rep->is_parent () : false; + } + void set_nesting_depth (size_t depth) { if (m_rep) @@ -694,15 +408,16 @@ return symbol_scope (m_rep ? m_rep->dup () : nullptr); } - void set_context (symbol_record::context_id context) + octave_value& persistent_varref (size_t data_offset) { - if (m_rep) - m_rep->set_context (context); + static octave_value dummy_value; + + return m_rep ? m_rep->persistent_varref (data_offset) : dummy_value; } - symbol_record::context_id current_context (void) const + octave_value persistent_varval (size_t data_offset) const { - return m_rep ? m_rep->current_context () : 0; + return m_rep ? m_rep->persistent_varval (data_offset) : octave_value (); } symbol_record find_symbol (const std::string& name) @@ -710,22 +425,15 @@ return m_rep ? m_rep->find_symbol (name) : symbol_record (); } - void inherit (const symbol_scope& donor_scope) + // Like find_symbol, but does not insert. + symbol_record lookup_symbol (const std::string& name) const { - if (m_rep) - m_rep->inherit (donor_scope.get_rep ()); + return m_rep ? m_rep->lookup_symbol (name) : symbol_record (); } - octave_value find (const std::string& name) + symbol_record insert (const std::string& name) { - return m_rep ? m_rep->find (name) : octave_value (); - } - - symbol_record& - insert (const std::string& name, bool force_add = false) - { - static symbol_record dummy_symrec; - return m_rep ? m_rep->insert (name, force_add) : dummy_symrec; + return m_rep ? m_rep->insert (name) : symbol_record (); } void rename (const std::string& old_name, const std::string& new_name) @@ -734,146 +442,6 @@ m_rep->rename (old_name, new_name); } - void assign (const std::string& name, const octave_value& value, - bool force_add) - { - if (m_rep) - m_rep->assign (name, value, force_add); - } - - void assign (const std::string& name, - const octave_value& value = octave_value ()) - { - if (m_rep) - m_rep->assign (name, value); - } - - void force_assign (const std::string& name, const octave_value& value) - { - if (m_rep) - m_rep->force_assign (name, value); - } - - octave_value varval (const std::string& name) const - { - return m_rep ? m_rep->varval (name) : octave_value (); - } - - bool is_variable (const std::string& name) const - { - return m_rep ? m_rep->is_variable (name) : false; - } - - void push_context (void) - { - if (m_rep) - m_rep->push_context (); - } - - void pop_context (void) - { - if (m_rep) - m_rep->pop_context (); - } - - void refresh (void) - { - if (m_rep) - m_rep->refresh (); - } - - void clear_variables (void) - { - if (m_rep) - m_rep->clear_variables (); - } - - void clear_objects (void) - { - if (m_rep) - m_rep->clear_objects (); - } - - void clear_variable (const std::string& name) - { - if (m_rep) - m_rep->clear_variable (name); - } - - void clear_variable_pattern (const std::string& pat) - { - if (m_rep) - m_rep->clear_variable_pattern (pat); - } - - void clear_variable_regexp (const std::string& pat) - { - if (m_rep) - m_rep->clear_variable_regexp (pat); - } - - void mark_automatic (const std::string& name) - { - if (m_rep) - m_rep->mark_automatic (name); - } - - void mark_hidden (const std::string& name) - { - if (m_rep) - m_rep->mark_hidden (name); - } - - // This function should only be called for the global - // symbol_scope, and that should only happen when it is added to - // the global symbol_scope. - - void mark_global (const std::string& name) - { - if (m_rep) - m_rep->mark_global (name); - } - - std::list - all_variables (bool defined_only = true, - unsigned int exclude = symbol_record::hidden) const - { - return (m_rep - ? m_rep->all_variables (defined_only, exclude) - : std::list ()); - } - - std::list - glob (const std::string& pattern, bool vars_only = false) const - { - return (m_rep - ? m_rep->glob (pattern, vars_only) - : std::list ()); - } - - std::list - regexp (const std::string& pattern, bool vars_only = false) const - { - return (m_rep - ? m_rep->regexp (pattern, vars_only) - : std::list ()); - } - - std::list variable_names (void) - { - return m_rep ? m_rep->variable_names () : std::list (); - } - - bool is_local_variable (const std::string& name) const - { - return m_rep ? m_rep->is_local_variable (name) : false; - } - - bool is_global (const std::string& name) const - { - return m_rep ? m_rep->is_global (name) : false; - } - void install_subfunction (const std::string& name, const octave_value& fval) { @@ -957,7 +525,7 @@ m_rep->cache_name (name); } - octave_user_function * function (void) + octave_user_function * function (void) const { return m_rep ? m_rep->function () : nullptr; } @@ -991,21 +559,10 @@ m_rep->update_nest (); } - bool look_nonlocal (const std::string& name, symbol_record& result) - { - return m_rep ? m_rep->look_nonlocal (name, result) : false; - } - - void bind_script_symbols (const symbol_scope& curr_scope) + bool look_nonlocal (const std::string& name, size_t offset, + symbol_record& result) { - if (m_rep) - m_rep->bind_script_symbols (curr_scope.get_rep ()); - } - - void unbind_script_symbols (void) - { - if (m_rep) - m_rep->unbind_script_symbols (); + return m_rep ? m_rep->look_nonlocal (name, offset, result) : false; } std::shared_ptr get_rep (void) const @@ -1023,14 +580,30 @@ return a.m_rep != b.m_rep; } + const std::map& symbols (void) const + { + static const std::map empty_map; + + return m_rep ? m_rep->symbols () : empty_map; + } + + std::map& symbols (void) + { + static std::map empty_map; + + return m_rep ? m_rep->symbols () : empty_map; + } + + std::list symbol_list (void) const + { + static const std::list empty_list; + + return m_rep ? m_rep->symbol_list () : empty_list; + } + private: std::shared_ptr m_rep; - - octave_value dump_symbols_map (void) const - { - return m_rep ? m_rep->dump_symbols_map () : octave_value (); - } }; } diff -r 940c1b6e3453 -r 61701d1317a1 libinterp/corefcn/symtab.cc --- a/libinterp/corefcn/symtab.cc Wed Jul 10 20:02:44 2019 -0700 +++ b/libinterp/corefcn/symtab.cc Fri Jul 12 12:14:43 2019 -0400 @@ -25,16 +25,14 @@ # include "config.h" #endif +#include + #include -#include "file-ops.h" -#include "file-stat.h" -#include "oct-env.h" #include "oct-time.h" #include "bp-table.h" #include "defun.h" -#include "dirfns.h" #include "fcn-info.h" #include "interpreter-private.h" #include "interpreter.h" @@ -45,279 +43,378 @@ #include "pager.h" #include "parse.h" #include "pt-pr-code.h" -#include "symrec.h" #include "symscope.h" #include "symtab.h" -#include "unwind-prot.h" -#include "utils.h" - -// Should Octave always check to see if function files have changed -// since they were last compiled? -static int Vignore_function_time_stamp = 1; namespace octave { - static void - split_name_with_package (const std::string& name, std::string& fname, - std::string& pname) + symbol_table::symbol_table (interpreter& interp) + : m_interpreter (interp), m_fcn_table (), m_class_precedence_table (), + m_parent_map () + { + install_builtins (); + } + + symbol_scope symbol_table::current_scope (void) const + { + tree_evaluator& tw = m_interpreter.get_evaluator (); + + return tw.get_current_scope (); + } + + bool symbol_table::is_built_in_function_name (const std::string& name) { - size_t pos = name.rfind ('.'); + octave_value val = find_built_in_function (name); + + return val.is_defined (); + } + + // FIXME: this function only finds legacy class methods, not + // classdef methods. + + octave_value symbol_table::find_method (const std::string& name, + const std::string& dispatch_type) + { + if (name.empty ()) + return octave_value (); + + fcn_table_const_iterator p = m_fcn_table.find (name); - fname.clear (); - pname.clear (); - - if (pos != std::string::npos) + if (p != m_fcn_table.end ()) + return p->second.find_method (dispatch_type); + else { - fname = name.substr (pos + 1); - pname = name.substr (0, pos); + fcn_info finfo (name); + + octave_value fcn = finfo.find_method (dispatch_type); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; } - else - fname = name; + } + + octave_value symbol_table::find_built_in_function (const std::string& name) + { + fcn_table_const_iterator p = m_fcn_table.find (name); + + return (p != m_fcn_table.end () + ? p->second.find_built_in_function () : octave_value ()); + } + + octave_value symbol_table::find_autoload (const std::string& name) + { + if (name.empty ()) + return octave_value (); + + auto p = m_fcn_table.find (name); + + return (p != m_fcn_table.end () + ? p->second.find_autoload () : octave_value ()); } - // Check the load path to see if file that defined this is still - // visible. If the file is no longer visible, then erase the - // definition and move on. If the file is visible, then we also - // need to check to see whether the file has changed since the - // function was loaded/parsed. However, this check should only - // happen once per prompt (for files found from relative path - // elements, we also check if the working directory has changed - // since the last time the function was loaded/parsed). - // - // FIXME: perhaps this should be done for all loaded functions when - // the prompt is printed or the directory has changed, and then we - // would not check for it when finding symbol definitions. + octave_value + symbol_table::builtin_find (const std::string& name, + const symbol_scope& search_scope_arg) + { + if (name.empty ()) + return octave_value (); + + fcn_table_iterator p = m_fcn_table.find (name); + + symbol_scope search_scope + = (search_scope_arg ? search_scope_arg : current_scope ()); + + if (p != m_fcn_table.end ()) + return p->second.builtin_find (search_scope); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.builtin_find (search_scope); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } + + return octave_value (); + } + + octave_value + symbol_table::fcn_table_find (const std::string& name, + const octave_value_list& args, + const symbol_scope& search_scope_arg) + { + if (name.empty ()) + return octave_value (); + + fcn_table_iterator p = m_fcn_table.find (name); - static inline bool - load_out_of_date_fcn (const std::string& ff, const std::string& dir_name, - octave_value& function, - const std::string& dispatch_type = "", - const std::string& package_name = "") - { - bool retval = false; + symbol_scope search_scope + = (search_scope_arg ? search_scope_arg : current_scope ()); + + if (p != m_fcn_table.end ()) + return p->second.find (search_scope, args); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find (search_scope, args); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } - octave_value ov_fcn - = load_fcn_from_file (ff, dir_name, dispatch_type, - package_name); + return octave_value (); + } + + octave_value + symbol_table::find_function (const std::string& name, + const symbol_scope& search_scope_arg) + { + if (name.empty ()) + return octave_value (); - if (ov_fcn.is_defined ()) + if (name[0] == '@') { - retval = true; + size_t pos = name.find_first_of ('/'); + + if (pos == std::string::npos) + return octave_value (); - function = ov_fcn; + std::string method = name.substr (pos+1); + std::string dispatch_type = name.substr (1, pos-1); + + return find_method (method, dispatch_type); } else - function = octave_value (); + { + symbol_scope search_scope + = (search_scope_arg ? search_scope_arg : current_scope ()); + + return find_function (name, ovl (), search_scope); + } + } - return retval; + octave_value + symbol_table::find_function (const std::string& name, + const octave_value_list& args, + const symbol_scope& search_scope) + { + if (name.empty ()) + return octave_value (); + + return fcn_table_find (name, args, search_scope); + } + + octave_value + symbol_table::find_user_function (const std::string& name) + { + if (name.empty ()) + return octave_value (); + + auto p = m_fcn_table.find (name); + + return (p != m_fcn_table.end () + ? p->second.find_user_function () : octave_value ()); } - bool - out_of_date_check (octave_value& function, - const std::string& dispatch_type, - bool check_relative) + octave_value symbol_table::find_cmdline_function (const std::string& name) { - bool retval = false; + if (name.empty ()) + return octave_value (); - octave_function *fcn = function.function_value (true); - - if (fcn) - { - // FIXME: we need to handle subfunctions properly here. - - if (! fcn->is_subfunction ()) - { - std::string ff = fcn->fcn_file_name (); + auto p = m_fcn_table.find (name); - if (! ff.empty ()) - { - sys::time tc = fcn->time_checked (); - - bool relative = check_relative && fcn->is_relative (); + return (p != m_fcn_table.end () + ? p->second.find_cmdline_function () : octave_value ()); + } - if (tc <= Vlast_prompt_time - || (relative && tc < Vlast_chdir_time)) - { - bool clear_breakpoints = false; - std::string nm = fcn->name (); - std::string pack = fcn->package_name (); - std::string canonical_nm = fcn->canonical_name (); - - bool is_same_file = false; - - std::string file; - std::string dir_name; - - if (check_relative) - { - int nm_len = nm.length (); + void symbol_table::install_cmdline_function (const std::string& name, + const octave_value& fcn) + { + auto p = m_fcn_table.find (name); - if (sys::env::absolute_pathname (nm) - && ((nm_len > 4 - && (nm.substr (nm_len-4) == ".oct" - || nm.substr (nm_len-4) == ".mex")) - || (nm_len > 2 - && nm.substr (nm_len-2) == ".m"))) - file = nm; - else - { - // We don't want to make this an absolute name, - // because load_fcn_file looks at the name to - // decide whether it came from a relative lookup. - - if (! dispatch_type.empty ()) - { - load_path& lp - = __get_load_path__ ("out_of_date_check"); - - file = lp.find_method (dispatch_type, nm, - dir_name, pack); - - if (file.empty ()) - { - std::string s_name; - std::string s_pack; + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; - symbol_table& symtab - = __get_symbol_table__ ("out_of_date_check"); - - const std::list& plist - = symtab.parent_classes (dispatch_type); - - std::list::const_iterator it - = plist.begin (); + finfo.install_cmdline_function (fcn); + } + else + { + fcn_info finfo (name); - while (it != plist.end ()) - { - split_name_with_package (*it, s_name, - s_pack); + finfo.install_cmdline_function (fcn); - file = lp.find_method (*it, nm, dir_name, - s_pack); - if (! file.empty ()) - { - pack = s_pack; - break; - } + m_fcn_table[name] = finfo; + } + } - it++; - } - } - } + // Install local function FCN named NAME. FILE_NAME is the name of + // the file containing the local function. - // Maybe it's an autoload? - if (file.empty ()) - file = lookup_autoload (nm); - - if (file.empty ()) - { - load_path& lp - = __get_load_path__ ("out_of_date_check"); - file = lp.find_fcn (nm, dir_name, pack); - } - } - - if (! file.empty ()) - is_same_file = same_file (file, ff); - } - else - { - is_same_file = true; - file = ff; - } + void symbol_table::install_local_function (const std::string& name, + const octave_value& fcn, + const std::string& file_name) + { + auto p = m_fcn_table.find (name); - if (file.empty ()) - { - // Can't see this function from current - // directory, so we should clear it. - - function = octave_value (); + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; - clear_breakpoints = true; - } - else if (is_same_file) - { - // Same file. If it is out of date, then reload it. + finfo.install_local_function (fcn, file_name); + } + else + { + fcn_info finfo (name); - sys::time ottp = fcn->time_parsed (); - time_t tp = ottp.unix_time (); - - fcn->mark_fcn_file_up_to_date (sys::time ()); - - if (! (Vignore_function_time_stamp == 2 - || (Vignore_function_time_stamp - && fcn->is_system_fcn_file ()))) - { - sys::file_stat fs (ff); + finfo.install_local_function (fcn, file_name); - if (fs) - { - if (fs.is_newer (tp)) - { - retval = load_out_of_date_fcn (ff, dir_name, - function, - dispatch_type, - pack); + m_fcn_table[name] = finfo; + } + } - clear_breakpoints = true; - } - } - else - { - function = octave_value (); + void symbol_table::install_user_function (const std::string& name, + const octave_value& fcn) + { + auto p = m_fcn_table.find (name); - clear_breakpoints = true; - } - } - } - else - { - // Not the same file, so load the new file in - // place of the old. - - retval = load_out_of_date_fcn (file, dir_name, function, - dispatch_type, pack); - - clear_breakpoints = true; - } + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; - // If the function has been replaced then clear any - // breakpoints associated with it - if (clear_breakpoints) - { - bp_table& bptab - = __get_bp_table__ ("out_of_date_check"); + finfo.install_user_function (fcn); + } + else + { + fcn_info finfo (name); - bptab.remove_all_breakpoints_in_file (canonical_nm, - true); - } - } - } - } + finfo.install_user_function (fcn); + + m_fcn_table[name] = finfo; } - - return retval; } - void - symbol_table::clear_global (const std::string& name) + // FIXME: should we ensure that FCN really is a built-in function + // object? + void symbol_table::install_built_in_function (const std::string& name, + const octave_value& fcn) { - m_global_scope.clear_variable (name); + auto p = m_fcn_table.find (name); + + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; + + finfo.install_built_in_function (fcn); + } + else + { + fcn_info finfo (name); + + finfo.install_built_in_function (fcn); + + m_fcn_table[name] = finfo; + } + } + + // This is written as two separate functions instead of a single + // function with default values so that it will work properly with + // unwind_protect. + + void symbol_table::clear_functions (bool force) + { + auto p = m_fcn_table.begin (); + + while (p != m_fcn_table.end ()) + (p++)->second.clear (force); + } + + void symbol_table::clear_function (const std::string& name) + { + clear_user_function (name); } - void - symbol_table::clear_global_pattern (const std::string& pattern) + void symbol_table::clear_function_pattern (const std::string& pat) + { + glob_match pattern (pat); + + auto p = m_fcn_table.begin (); + + while (p != m_fcn_table.end ()) + { + if (pattern.match (p->first)) + (p++)->second.clear_user_function (); + else + p++; + } + } + + void symbol_table::clear_function_regexp (const std::string& pat) + { + regexp pattern (pat); + + auto p = m_fcn_table.begin (); + + while (p != m_fcn_table.end ()) + { + if (pattern.is_match (p->first)) + (p++)->second.clear_user_function (); + else + p++; + } + } + + void symbol_table::clear_user_function (const std::string& name) { - m_global_scope.clear_variable_pattern (pattern); + auto p = m_fcn_table.find (name); + + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; + + finfo.clear_user_function (); + } + // FIXME: is this necessary, or even useful? + // else + // error ("clear: no such function '%s'", name.c_str ()); + } + + // This clears oct and mex files, including autoloads. + void symbol_table::clear_dld_function (const std::string& name) + { + auto p = m_fcn_table.find (name); + + if (p != m_fcn_table.end ()) + { + fcn_info& finfo = p->second; + + finfo.clear_autoload_function (); + finfo.clear_user_function (); + } + } + + void symbol_table::clear_mex_functions (void) + { + auto p = m_fcn_table.begin (); + + while (p != m_fcn_table.end ()) + (p++)->second.clear_mex_function (); } // Insert INF_CLASS in the set of class names that are considered // inferior to SUP_CLASS. Return FALSE if INF_CLASS is currently // marked as superior to SUP_CLASS. - bool - symbol_table::set_class_relationship (const std::string& sup_class, - const std::string& inf_class) + bool symbol_table::set_class_relationship (const std::string& sup_class, + const std::string& inf_class) { if (is_superiorto (inf_class, sup_class)) return false; @@ -343,8 +440,7 @@ // else // // No relation. - bool - symbol_table::is_superiorto (const std::string& a, const std::string& b) + bool symbol_table::is_superiorto (const std::string& a, const std::string& b) { class_precedence_table_const_iterator p = m_class_precedence_table.find (a); // If a has no entry in the precedence table, return false @@ -356,111 +452,88 @@ return (q != inferior_classes.end ()); } - octave_value - symbol_table::builtin_find (const std::string& name) + void symbol_table::alias_built_in_function (const std::string& alias, + const std::string& name) { - fcn_table_iterator p = m_fcn_table.find (name); + octave_value fcn = find_built_in_function (name); - if (p != m_fcn_table.end ()) - return p->second.builtin_find (); - else + if (fcn.is_defined ()) { - fcn_info finfo (name); + fcn_info finfo (alias); - octave_value fcn = finfo.builtin_find (); + finfo.install_built_in_function (fcn); - if (fcn.is_defined ()) - m_fcn_table[name] = finfo; - - return fcn; + m_fcn_table[alias] = finfo; } - - return octave_value (); + else + panic ("alias: '%s' is undefined", name.c_str ()); } - octave_value - symbol_table::fcn_table_find (const std::string& name, - const octave_value_list& args) + void symbol_table::install_built_in_dispatch (const std::string& name, + const std::string& klass) { - fcn_table_iterator p = m_fcn_table.find (name); + auto p = m_fcn_table.find (name); if (p != m_fcn_table.end ()) - return p->second.find (args); - else { - fcn_info finfo (name); - - octave_value fcn = finfo.find (args); + fcn_info& finfo = p->second; - if (fcn.is_defined ()) - m_fcn_table[name] = finfo; - - return fcn; + finfo.install_built_in_dispatch (klass); } - - return octave_value (); + else + error ("install_built_in_dispatch: '%s' is undefined", name.c_str ()); } - octave_value symbol_table::find_function (const std::string& name) + std::list symbol_table::user_function_names (void) { - if (name.empty ()) - return octave_value (); - - if (name[0] == '@') - { - size_t pos = name.find_first_of ('/'); + std::list retval; - if (pos == std::string::npos) - return octave_value (); + for (const auto& nm_finfo : m_fcn_table) + { + if (nm_finfo.second.is_user_function_defined ()) + retval.push_back (nm_finfo.first); + } - std::string method = name.substr (pos+1); - std::string dispatch_type = name.substr (1, pos-1); + if (! retval.empty ()) + retval.sort (); - return find_method (method, dispatch_type); - } - else - return find_function (name, ovl ()); + return retval; } - octave_value - symbol_table::find_function (const std::string& name, - const octave_value_list& args) + std::list symbol_table::built_in_function_names (void) { - octave_value fcn; + std::list retval; - if (m_current_scope) + for (const auto& nm_finfo : m_fcn_table) { - fcn = m_current_scope.find_subfunction (name); + octave_value fcn = nm_finfo.second.find_built_in_function (); if (fcn.is_defined ()) - return fcn; + retval.push_back (nm_finfo.first); } - return fcn_table_find (name, args); + if (! retval.empty ()) + retval.sort (); + + return retval; } - // FIXME: this function only finds legacy class methods, not - // classdef methods. - - octave_value - symbol_table::find_method (const std::string& name, - const std::string& dispatch_type) + std::list symbol_table::cmdline_function_names (void) { - fcn_table_const_iterator p = m_fcn_table.find (name); + std::list retval; - if (p != m_fcn_table.end ()) - return p->second.find_method (dispatch_type); - else + for (const auto& nm_finfo : m_fcn_table) { - fcn_info finfo (name); - - octave_value fcn = finfo.find_method (dispatch_type); + octave_value fcn = nm_finfo.second.find_cmdline_function (); if (fcn.is_defined ()) - m_fcn_table[name] = finfo; + retval.push_back (nm_finfo.first); + } - return fcn; - } + if (! retval.empty ()) + retval.sort (); + + return retval; } template