pax_global_header00006660000000000000000000000064132604624350014517gustar00rootroot0000000000000052 comment=195c4c19fcadeb1910495a8d44fc949f3cb824ec sc-7.16_1.1.2/000077500000000000000000000000001326046243500126005ustar00rootroot00000000000000sc-7.16_1.1.2/CHANGES000066400000000000000000003164021326046243500136010ustar00rootroot00000000000000CHANGES BETWEEN 7.15 and 7.16: Chuck Martin -The marks for the last nine edited cells are now updated properly even if the data entered or edited is a string constant. -When assigning a numeric value to a cell with = (or "let"), the numeric part of the cell will be cleared if no expression or value is specified (e.g. "let A0 ="). -Fixed a bug that caused references to cells in rows 0-3 to some- times be incorrect when copying. -The vv command now increments modflg properly so sc knows that the file has been modified. -Added vi-like abbreviations, defined with the abbrev command and undefined with the unabbrev command (shortcut key is ~). See the man page for usage details. -When in navigate mode, ^V now acts like the ESC key, which allows you to use ^V^V to disable expansion of abbreviations, as in vi. You can still use 'v' to insert the numeric value from the current cell into the command line. -Added support for POSIX regex functions (regcomp() and regexec()), using extended regular expression syntax, and made it the default in the Makefile. Also combined all regex variables into a single variable in the Makefile. -Rewrote the code that copies a marked cell. This fixes several problems: the IS_CLEARED and MAY_SYNC flags are now correctly set (thanks to Michael Karcher for bringing that bug to my attention), sc will no longer copy into a locked cell, and copying a blank cell into a nonblank cell now clears the nonblank cell (it previously had no effect). -When in quick numeric entry mode and a command is started using a digit or the +, -, or . keys, any cursor movement while the cursor is at the end of the line and the line ends with a + or - will automatically switch to navigate mode. -The "+" key now works like "=" if the numeric part of the current cell is empty, except that the entry will begin in navigate mode. If quick numeric entry mode is enabled, after switching to insert mode the first time, the cursor keys will behave as if the entry was started with a digit or decimal. -When defining a range in navigate mode and the character immedi- ately preceding the cursor is either a + or -, or the cursor is at the beginning of an empty "let" expression, pressing + or - will insert "@sum(" into the line, followed by the range, followed by ")" and then the character pressed (either + or -), and will remain in navigate mode. Pressing TAB, :, or . in such a case will do the same thing, but without the final + or -, and without remaining in navigate mode. -Pressing the space bar in navigate mode while highlighting a range will enter the highlighted range in the command line, followed by a space, but will remain in navigate mode. Otherwise, it will move forward a column, as before. This is handy for entering copy, move, or frame commands, for example, which accept more than one range in the command. -The openrow command now works like the 'o' command instead of the 'i' command, as it should. -When adding or deleting a note, modflg and FullUpdate are now always incremented so that the the screen is updated properly and the change is saved if necessary. -When deleting rows in a framed range, the frame is now adjusted properly all the time, regardless of how many rows are deleted. Also, if all of the inner range rows are deleted, the frame is removed. -Removed clear() from the goraw() function to avoid screen flicker at the end of a macro, and added a clear() after each goraw() function call that still needed it. -Removed #includes for getopt.h and added one for unistd.h in psc.c because getopt.h is only necessary for GNU long options, which sc and psc don't use. -Removed the "#" prefix from backup file names, leaving only the trailing "~", which is more standard practice. This was in response to a suggestion forwarded to me from the Debian Bug Tracking System (although it technically wasn't a bug). -The umask is set to 0 before creating backups, and then reset to its original value afterwards, so that the backup file will have the same identical permissions as the original file. -Backup files now retain the modification time of the original file before it was modified, and also the user and group ids, if possible. -The -P command line option can now accept "%" as its range, which is interpreted to mean the whole spreadsheet. -There is now a -W command line option that works similar to the -P option, except it uses the write command instead of the put command, and it takes a single range as an argument, which may be "%" to designate the whole spreadsheet. -User-defined formats may now be edited instead of having to re-enter them from scratch. -Rewrote much of the history search code so that it works much better than before and allows both forward and reverse searching. Also added regular expression handling to the history search functions. -When searching or navigating the command line history, a message is shown on the error line (2nd line) telling which line of the history you're currently looking at. -If a cell uses a format that hasn't been defined yet, and isn't one of the predefined formats, it will now cause the cell to be blank instead of containing random characters. Thanks to Hartmut Henkel for reporting this bug. -All user-defined formats are now cleared in erasedb(), such as when loading a new file. -When copying, if the destination range has fewer rows or columns than the source range, the area to be cleared before copying will be expanded to the same number of rows or columns as the source range to prevent there being unexpected data in the "holes". -In the copy() function, added a call to sync_refs() after erasing the destination range, but before doing the actual copy, to prevent messing up color ranges, frames, ranges in expressions, or named ranges. -Range commands are now initiated with "r" instead of "/". The "/" key will be mapped to more advanced search (and possibly replace) capabilities later. -Strings with embedded, backslash-escaped quotes are now displayed properly, with the backslashes removed. -Shell escapes (using "!") now also work while in edit mode. -Added @filename(e) function which returns the current default filename. Expression e determines whether the full path should be returned, or just the base name, depending on whether it is zero or not. -If craction is set to move the cursor either down or to the right, autoinsert is not set, and the cell cursor is in the scrolling region of a framed range, the cell cursor will never leave that region, but will always go to the next row/column after entering data in the last column/row of the range. After reaching the bottom right corner, craction will be ignored. -If quick numeric mode and craction are both set, pressing will move in the direction craction is set for instead of switching to input mode with an empty line. -Changed test in get_motion() from checking for 0 to checking for '0' (digit instead of NULL byte), so that commands like d0, c0, and y0 (in edit mode) work correctly. -If the dollar prescale option is set, all numbers entered with a decimal point will be scaled, even if all of the digits after the decimal point are zeros (or missing). -Fixed a bug that prevented sc from remembering the filename when starting with a non-existent filename on the command line (for creating a new file). Also, added a message stating that a new file is being created if sc is started with a single filename on the command line and the file doesn't exist. -All of the commands that take you to the limits of the spreadsheet (0, $, ^, #, and the HOME key) now work differently when invoked inside a framed range. Depending on where you are, they will take you first to the limit of the inner range, then the outer range, then the whole spreadsheet. Also moved the code for all of these commands to their own functions in vi.c, since these portions of code were all being duplicated in the navigate mode section of the write_line() function. -The last goto can now be repeated (find the next match) by pressing 'n'. All note commands must now be accessed by pressing '*' instead of 'n' to allow this. To actually go to a note, press '*' twice. -Added a new pull command, pullfmt (pf), that works like merge except that only cell formatting is merged, leaving the actual data un- touched. -Changed starting value of findfunc in vi.c from '0' to '\0' to fix a bug that caused lockups when using the ; and , commands before a prior search with f, F, t, or T. -Swapped the meaning of the ` and ' commands because the ' key is less of a reach on most keyboards (and more standard), and restoring the target cell to its original position on the screen seems to be more useful and more often used than not restoring the position. -In closecol(), doformat(), and formatcol() functions, rowsinrange and colsinrange are now reset to prevent occasional lockups when deleting or resizing columns. -Moved color initialization earlier in the start-up so that it won't override colors in a file (including the .scrc file). -Trying to define color number 0 now correctly results in an error. -Fixed support for entering numbers in exponential notation (thanks to Michael Van Biesbrouck for bringing that bug to my attention). Also, disallowed range names that are all numeric except for a single 'e' or 'E' anywhere in the name, because they are ambiguous. -Removed the strtof() function, since it partially duplicated the functionality of the standard ANSI C strtod() function (but not as well), and replaced all strtof() calls with calls to strtod() (with a slightly different syntax). -Moved the resetting of skipautorun inside the if(eraseflag) block in the readfile() function to prevent it getting reset prematurely (during the reading of the .scrc files) and resulting in the autorun macro being run anyway. -If deleting a range of rows, columns, or cells causes the range pointed to by lastfr to no longer exist, lastfr is reset to NULL to prevent a segmentation fault during the next screen update. -The copy command now uses the delete buffer as temporary storage for the source range being copied so that if the source and desti- nation ranges overlap, the copy still works. -The sync_refs() function now also syncs references in all delete buffers so they will still be correct when pulled (even though the references are adjusted when pulling, they were still occasionally wrong if the cells originally referenced were moved in the meantime). -A new copy command, "c.", can be used to copy the current cell into a range which includes the current cell, and which is highlighted by moving the cell cursor. This facilitates the rapid entry of the same value, string, or expression into a range of cells. See the man page for details. -You can now left justify, right justify, or center all the strings in a range with the "r{", "r}", and "r|" commands, respectively. These operations are also available in macros with "leftjustify", "rightjustify", and "center". -When changing column formats, if ESC, ^G, or q is pressed, the original format will be restored. -The fixed operator was not working. It now works, but, like all other keywords, must be preceded by "@". -Autoinsert mode now works properly even if the bottom of the frame is of width 0. -A new Set option, autowrap, allows data to be entered in a framed range row by row or column by column if craction is not 0, auto- matically wrapping to the next row or column when the end of the scrolling region is reached. If both autowrap and autoinsert are enabled, autoinsertion only takes place if autowrap would take the cell cursor outside of the scrolling region of the framed range. -The "set" command now increments modflg so that sc knows the file has been changed and asks whether to save when quitting. -Added vi-like W and B commands to edit mode which only consider a string of one or more spaces to be a word boundary. -The @date function can now take an optional second parameter, which is a strftime(3) compatible format string, which will override the default format. -All functions which take a single argument now store it as the left parameter instead of the right (e.o.left instead of e.o.right), so that adding an optional second parameter, such as in the @date function in the previous item, keeps the parameters in the correct order when decompiling for display on the top line or saving in a file. -When numeric constants are entered which are too large to be con- tained in a double, they are replaced with @err. When the result of a calculation is too large to be contained in a double, it results in an error. These fix a dual bug which caused "inf" or "INF" to be displayed (bug 1) and written to a file (bug 2) when saving. When written to a file, it resulted in an error when the file was read back in the next time. -All rows, columns, and ranges that are yanked or deleted now have their respective row/column hidden flags and column width, precision, and format saved so they can be restored appropriately when pulling rows or columns (these were all lost, previously, and the default values were restored, instead). -Added a pullcopy command and its interactive shortcut, pC, which acts like pp, except that references are adjusted as in the other copy operations. Also added a "p." command, which works like pC, except that it allows a destination range to be defined as in the "c." command. -When highlighting a range in navigate mode, pressing "o" will move you to the opposite corner of the highlighted range, allowing you to adjust all four sides of the range. When used with the "c." command, the cell to be copied will always be the cell the command was started in. -The deleterow, deletecol, yankrow, and yankcol commands now work properly with no arguments instead of assuming there is a range highlighted. -The Z command (for hiding rows or columns) now allows a range of rows or columns to be highlighted using any cursor movement keys, like the d (delete) and y (yank) commands have for quite awhile. -The erase_area() function now has a new argument to ignore locked cells which is used when sorting so that locked cells can still be sorted. Also added an unlock argument to free_ent() and caused the IS_LOCKED flag to be copied in the copyent() function so that sorting won't unlock cells. -The @sum, @prod, @avg, @count, @stddev, @max, and @min functions may now take an optional second argument which is an expression that is evaluated for each cell in the range, and only those cells for which the expression is non-zero are included in the calculation of the function. See the man page for details. -If rows are inserted into a framed range, note addresses are now updated properly. -Logical NOT (~ or !) now always decompiles to "!" instead of "~" for display or writing to a file. -In the O_VAR case in the seval() function, check to make sure that ep is not NULL before checking if it has a label to prevent possible segfaults with the seval command, and also changed its name to vp for consistency with the O_VAR case handling in eval(). -In doseval(), check the return value of seval() for NULL before using it to prevent segfaults with the seval command if the ex- pression given to seval isn't a valid string expression. -Fixed a bug that prevented a string expression that currently evaluated to a null string from being written to a file with the put command by testing for either a non-null string value or a string expression in the cell instead of just the former. -Moved "FullUpdate++" outside of "if (eraseflg)" block at the end of the readfile() function in cmds.c so that the screen is updated properly after a macro. -Added a getkey command for use in macros to get a single key from the user instead of a string. Handy for "hotkeys" or "Press any key..." type things. -Changed two instances of update(0) to refresh() in formatcol(), and added another refresh() after update(1) later in the same function because the screen wasn't being refreshed properly in FreeBSD. -Added a ^L option to redraw the screen while formatting a column. -Added -ffloat-store to the compiler command line for interp.c to fix intermittent error with @round function as well as some other problems caused by the FPU registers in x86 machines, and possibly others, having different precision than doubles in memory, and removed some previous workarounds for some of these problems. -Braille mode now positions the cursor at the far left of the current cell and also moves the column names to the left edge of the column, which works better with screen readers such as SpeakUp while still retaining all of its advantages for braille readers. Michael Lapsley -Added @lastrow and @lastcol functions, useful for macros designed to default to the whole spreadsheet. -Added getrange command for use by macros to check if a given named range exists without causing an error, and return the actual range if so. -Added a history file (default: ~/.sc_history) to carry the command history from one session to another. -Added ability to interface with plugins (external programs for adding commands to sc and converting files with user-specified extensions automatically when reading or writing). Wilhelm B. Kloke -Pointed out problems with arrow keys in nmgetch() when BSD43 is defined due to changed ^B and ^F behavior, causing arrow keys to work incorrectly on FreeBSD. Also, similar changes were necessary for VMS when SIMPLE is defined. -Add "optreset = 1" to -P option handling when BSD43 is defined so the -P command line option will work properly on FreeBSD. Hartmut Henkel -Added locale support. -Rewrote dodate() function to use strftime() because ctime() doesn't honor locale. -Changed PI macro to M_PI, which is standard in math.h. -Changed sprintf() to snprintf() in dofmt() function to prevent potential segmentation faults. -Added isascii() check in addition to existing isalpha() check to prevent invalid address problems in some locales. TJ Dvorachek -Time types in interp.c have been changed from long to time_t and the time() prototype in dotime() is redundant and has been removed. -Provided information for creation of a DJGPP section in the Makefile. -In the signals() function in sc.c, the SIGBUS signal handler is not set when compiling with DJGPP because it lacks SIGBUS. CHANGES BETWEEN 7.14 and 7.15: Chuck Martin -Fixed "``" and "''" bug, introduced in 7.14 (they weren't being accepted). I guess 7.14 was released too soon after last minute changes, without sufficient testing. -Fixed format bug that caused segfaults when there was no decimal point in the format string, also introduced in 7.14 last minute changes. -When entering insert mode with '+' or '-', and the cell already contains a numeric value or expression, the edit is now saved in the dot buffer, as it should be. -Don't save the current position (for the "`" and "'" commands) during a goto in a macro. Also, don't save the current position during any movement command where the current cell doesn't change (leave the saved position pointing to the last position before an actual move). CHANGES BETWEEN 7.13 and 7.14: Chuck Martin -Added a braille option ("set braille" or ^Tb) with two different modes, which makes use of braille displays with sc much easier by judicious placement of the hardware cursor. -The "cellcur" option (current cell highlighting) is no longer saved with a file, since it's more likely a user preference and/or terminal dependent, and is more appropriate for use in a user's .scrc. -Split the last fprintf() in list_frames() so that the outside and inside ranges will be different (as they should be), even if neither is a named range, since r_name() uses a single static buffer. -The range copy command (/c) with only one argument will now use the highlighted range as the source if one is highlighted. Otherwise, it will copy the last copied range, as before. -The range copy command (/c) with no arguments will now use the highlighted range as the destination, if one is highlighted, and the last copied range as the source. -The addnote command with only one argument will now use the highlighted range if there is one, and the current cell otherwise, as the range to link to the cell specified by the single argument. -The frame command with a single argument will use the highlighted range, if any, as the inside range, and the single argument as the outside range. If no range is highlighted, the single argument will be used as the inner range, and the existing outer range containing the current cell will be used as the outer range, which which was the previous behavior whenever a single range was speci- fied. If the current cell is not in an existing framed range, an error message will be issued. -The frame command may now be used without any arguments, provided a range is highlighted (which will be used as the inner range) and the current cell is in a framed range whose outer range is to be used as the default outer range. -Fixed a bug that prevented defining a new range with a name that was the same as the beginning of a previously defined longer range name. -A new command line option, -a, will skip the autorun macro if one is defined. -Bug fix: if a cell contains a string expression, but no numeric value, pressing "e" to edit the non-existent numeric value will no longer bring up the string expression for editing (it wouldn't work, anyway, because string expressions don't work with the "let" command). -You can now use the cursor keys with ^Tr to set craction. -In numeric mode, entering insert mode by pressing a digit, '+', or '-' will cause the cursor keys to terminate an entry and move the cell cursor in the desired direction. Switching to edit mode will change the cursor keys back to their normal function (moving the cursor in the command line or history), even after switching back to insert mode. -Fixed @index function so that indexing by both row and column will still work even if there is only one row or one column (so that, for example, a macro could generically access the cell in the first row and first column of a range without regard for the actual dimensions of the range). -All flags are turned off in the clearent() function except IS_CHANGED and IS_CLEARED, which are turned on. Also, the IS_CLEARED flag is turned off in the fill() function after the cell is cleared and then assigned a new value. This prevents errors from appearing in expressions which reference cells in the filled range whenever the sync_refs() function is called, which happens any time any cell or range of cells is yanked, deleted, pulled, copied, or moved. -Each instance of @ext now saves its own previous value, which eliminates the longstanding bug that caused the last returned value to be used for all instances if external functions were enabled and later disabled. -Added a % command in edit mode that finds the matching parenthesis of a pair, as in vi (only works for parentheses, not for braces or brackets). -When starting a new command, the dot buffer will not be altered unless the command starts out in edit mode (like the "e" and "E" commands). All other commands will only alter the dot buffer after entering edit mode for the first time. This allows things like editing data, using "goto" to jump to a new cell, and then "." to make the same change to the new cell (previously, the "goto" command caused the previous edit to be lost from the dot buffer). -Numeric arguments are now saved for use with the dot buffer when editing a line, so the same argument will be used again when repeating the action with the "." command. -If a command line is identical to the last command in the history, it won't be stored in the history again. -The copy and move commands (/c and /m) no longer have an effect on the delete buffer. This allows you to do things like delete a cell or range of cells, move things around in the spreadsheet, and then pull the deleted cell(s) back in. -Implemented named buffers, "a" through "z" and "0" through "9", selected with the " key. Buffers "1" through "9" hold the last nine deletions, as in vi, and buffer "0" holds the last cell or range of cells yanked, as in vim. See the man page for details. (A "select" command was also added to allow the use of named buffers in macros.) -The format command now rejects a width of zero. -Column headings over columns of width 1, 2, or 3 are now displayed properly. If the width is 1 and the column name is two characters, only the second character will be displayed in the heading. -In navigate mode, v and ^V now place the numeric value from the current cell into the command line, freeing up the ^A command to be used as a synonym for the HOME key, as it is when not editing a line. -KEY_RESIZE is now handled properly so an error message is not issued whenever an xterm window is resized. If SIGWINCH is undefined, KEY_RESIZE resizes the spreadsheet to fit the window. otherwise, it is ignored, since the SIGWINCH signal handles the resizing. -Pulling the same range several times now works correctly. Pre- viously, some cell references were being altered after the first pull under certain circumstances. -Internal references are now adjusted properly when pulling a range with "pt" (pulltp). -The -R and -C command line options now work properly again. -The pull merge command (pm) now actually does a merge, instead of overwriting the destination cells, and a new pull command (paste, or "pp") pulls a cell or range after first erasing the destination cells. For macros, pull does the paste, while pullmerge does the merge. -The f (format) command now allows you to interactively change the column width, precision, and format number as well as enter these values on the command line as before. See the man page for details. -The "&" in the fractional part of a custom format type now works properly even if the precision is one. -When using "&" in a custom format type, and there are literal characters after the fractional portion, they will now be included properly even when the precision is zero. -Removed the "fill x y" (where x and y are numbers and the range is supposed to be highlighted) form of the fill command, since its usefulness and convenience leaves much to be desired the was sc now handles the /f command. It's much easier to use the full form now. -Added marks '1' through '9' to remember the locations of the last nine edits, and '0' for the current (or last, if not editing) edit. Hartmut Henkel -When saving a range with "Put", the "format" and "hide" commands are now written for the last column of the range, if necessary (needed to change a "c < cn" to "c <= cn" in the write_fd() function in each case). CHANGES BETWEEN 7.12 and 7.13: Chuck Martin -New column formats may now be defined with "format # = string", where # is a number from 0-9, and string is a format string like that used for formatting individual cells. Numbers 0-4 will replace the default formats (causing the precision value to be ignored), and numbers 5-9 will add new ones. -Format strings now interpret "&" in the fractional part to mean that the column precision should be used. This allows user specified formats to have their precision vary from column to column without having to create multiple formats (see the previous item). -The "vr" command now limits itself to the current framed range, if any, like "dr" and "yr". If not contained in a framed range, it works as before. -The "yy" command now yanks only the current cell instead of the current row, as this seems more useful. Likewise, "vv" converts the current cell from an expression to a constant value, and for consistency, "dd" erases the current cell ("dd" is a synonym for "x" now, instead of "dr"). -You can now unset mdir and autorun by setting them to "". -Both goraw() and deraw() do nothing if stdout is not a terminal. This prevents segmentation faults when using sc non-interactively with redirection or pipes. -The redraw command also does nothing if stdout is not a terminal for the same reason as the goraw() and deraw() functions (see the previous item). -doquit() and dump_me() no longer try to prompt the user about saving a file under emergency situations such as a broken pipe or kill signal if stdout is not connected to a terminal, which prevents even more possible segmentation faults. Also, SIGINT is no longer ignored, but calls doquit() instead. -Added a "usecurses" variable, and replaced most instances of "isatty(STDOUT_FILENO)" with "usecurses". The -q option also resets usecurses to 0, so echoing commands to "sc -q" now works from the command line. -Added another "usecurses" before the confirmation message in the writefile() function. -The error() macro now checks stdout instead of stdin before dis- playing its message. -find_char() now checks to see if the last character in the dot buffer is 'f', 'F', 't', or 'T', and if so, stores the next character after it. This causes these commands to be stored in the dot buffer if they are part of another command (such as 'df+'), but not otherwise. -Fixed a bug where numeric arguments could not be used in edit and navigate modes if quick numeric mode was set. -Added a new command, seval, that evaluates an expression like eval, except it evaluates string expressions instead of numeric expressions. -Added KEY_BACKSPACE and KEY_END to the control key handling part of the main loop, and removed KEY_END from nmgetch(). -Eliminated the hitwinch variable and moved all SIGWINCH stuff into the SIGWINCH handler (winchg()). Also, changed update() to use getmaxyx() instead of LINES and COLS. These changes make sc react immediately to resizing xterms (for some reason, this doesn't work with old versions of ncurses--at least not with version 1.9.9g, which seems to require restarting sc to make size changes effective). -A new Y command in edit mode that yanks to the end of the line, like y$ (I know this isn't the way Y works in vi, but it's the way it *should* work for consistency [cf. the D and C commands], and the way Y works in vi would be pointless in sc). -The "cellassign = 1" in slet() is now only done if the cell being assigned to is the current cell, like it was supposed to (I must have deleted that part by accident somewhere along the line). -The /S command now lists named ranges, framed ranges, and color ranges, one after another, along with definitions of any colors that have been changed from the default (no second letter required). -Colors may now be unset (reverted back to their default start-up values) by leaving out the expression (e.g. "color 3 =" will set color 3 back to it's original foreground/background pair). Unlike setting it explicitly to its original value, this will not cause the expression to be written to the file when saved. -Changed test in pullcells() function for checking if there is data to pull so that instead of testing the actual pointer to see if it's NULL, the index will be tested to see if it's negative. The old method was causing segfaults if the delete buffer was empty. -^A now goes to the beginning of the line in edit and insert modes. It retains its original behavior in navigate mode (the man page already stated that inserting the numeric value of the current cell into the command only works in navigate mode; now the behavior matches the manual). -Range names may now begin with a digit as long as there is at least one non-numeric character (alpha or '_'). -Added range name completion. -Fixed a bug that caused range names to match when they shouldn't when parsing a command (a range name would be matched by any string of characters that matched the beginning of the name, so that if "abc" and "abcd" are both defined, an expression using "abc" would end up accessing the wrong range, since the list is searched in reverse alphabetical order). -Cells with attached notes are now identified with an asterisk to the left of the numeric portion (using color 4, default black on yellow, if color is enabled). -The J, K, PageUp, and PageDown commands now use the pagesize setting even in navigate mode. -^B and ^F now do the same as PageUp and PageDown instead of moving left or right one cell. This is to be more compatible with vi, resulting in less confusion for those who use both. -Centered strings are now entered by pressing '\' instead of '"', so that '"' can be used for multiple delete buffers as in vi in the future. -If an autorun macro is already defined, pressing 'A' will include it for editing in the command line it brings up, as it should (it wasn't working properly due to a missing else). -In openfile(), check if rfd is NULL (signifies opening pipe to process for both reading and writing) before closing unused end of (non-existent) second pipe. This was causing intermittent lock-ups when only writing to a pipe because stdin was being closed. -The screen is no longer cleared or redrawn unless absolutely necessary after a macro is run, since most macros won't need it, and it causes screen flicker. Macros which need it (those that write directly to the screen, bypassing sc) will need to do an explicit redraw before ending. -SIGPIPE now causes a flag to be set (brokenpipe), which is checked a number of places so that nothing more is written to the pipe and an error message is displayed, instead of trying to save the file and quit (an annoyance when all you did was do a /S and then quit before reading the whole output). -If the last character in the string in a run command is '&' (signifying the command will be run in the background), the "Press any key to continue" prompt will not be displayed, and sc will continue with no hesitation. I use this in Linux to load another sc file in another virtual console with "openvt -sw" (assign it to a function key for ease of use). -Added @rows and @cols functions that take a range as argument and return the number of rows or columns, respectively, in that range. -Added a new error command for displaying error messages from macros (syntax: `error "message to display"'). -The duprow() and dupcol() functions now put the cursor in the original column or row, respectively, which, among other things, prevents sc from hanging when multiple rows or columns are appended. -A `put ""' command (with the empty string) will save the default name even in a pipeline, unless the default name is also the empty string, in which case it will be written to stdout. -Removing a function key definition by defining it to be "" (the empty string) now changes the pointer to NULL in addition to freeing the string, as it should. -When using goto (g) to jump to a specific cell address (as opposed to doing a search), only save the current address for the `` and '' commands if the destination address is different. -When inserting or deleting rows or columns, update the addresses associated with the last explicit goto. -Added definitions for color_set() and attr_get() to sc.h for older versions of (n)curses that have missing or outdated versions of these macros (not conforming to X/Open Curses). -Colors are now reinitialized in startdisp() (using init_pair()) so that they continue to work properly after resizing an xterm. -The -P command line option can now be used without specifying the source range, but the target address must be specified in such a case, preceded by a '/' (e.g. '-P/f23'). In this case, sc will be started interactively in navigate mode so you can highlight the source range you want to import. -The -P command line option may now be used more than once, and the specified ranges will be output one after another, however, the -v option must precede each instance of -P whose output is to be converted to values. This allows multiple ranges to be copied from one file to another without having to load and calculate the entire source file for each of them. -The destination given with the -P option may now be specified as either a cell address or a range. If a range is specified, the upper left corner of that range will be used. This allows named ranges to be used. -The initial allocation of memory for color pairs is now done in a loop instead of eight explicit statements. -Undoing changes made to a command now works after using replace mode. Also, backspacing in replace mode restores the original character instead of just deleting the new one and closing the hole. CHANGES BETWEEN 7.11 and 7.12: Chuck Martin -The yankrow and deleterow commands can now take a numeric argument or two numeric arguments separated by a colon to specify a row or range of rows to yank/delete. Likewise, the yankcol and deletecol commands can now take a column name or two column names separated by a colon to specify a column or range of columns to yank/delete. -Pressing y or d followed by any cursor movement, including PageUp, PageDown, H, J, K, or L, will begin highlighting full rows/columns to be yanked or deleted. Pressing TAB, '.', or ':' will terminate the highlighting and insert the range of rows/columns in the command line. Pressing the RETURN key, instead, will yank/delete the default range of rows/columns displayed. -Added a pagesize option that can be changed with the set (S) command. If nonzero, it will determine the number of rows to move up or down when using the J, K, PageUp, or PageDown keys. -The PageUp, PageDown, J, and K commands will now be multiplied by a preceding numeric argument like most other commands. -An improper test was causing the syncref() function to corrupt expressions which referenced empty cells every time anything was deleted or moved. This has been fixed (the struct ent now has an additional "IS_CLEARED" flag). -When adding a note with "na", automatically start out in navigate mode. CHANGES BETWEEN 7.10 and 7.11: Chuck Martin -You can now use ~ in edit mode to change the case of a character, just like in vi. -In quick numeric mode, the + and - keys now switch to insert mode and append a + or - to the existing numeric entry, respectively. This is so that you can easily add to or subtract from a cell that already contains numeric data. -When attempting to edit the numeric value in a cell with e, +, or -, and no value or numeric expression has previously been entered in that cell, you will no longer be presented with a 0 to edit. -In navigate mode, the + and - keys now insert the current cell address, followed by a + or -, respectively, and remain in navigate mode, so that other cells may be easily added to or subtracted from the equation. -^E can now be used in insert mode to jump to the end of the line. -Check to see if $HOME exists before copying it to curfile to prevent segfaults if it's unset. -The deraw() function now sets the background to the default colors (white on black) before clearing the last line. -If piping from a command (as opposed to *to* a command), as in advanced macros, the cell cursor won't move to the last line and the last line won't be cleared, which avoids unnecessary screen updates. -Function key definitions are now saved with a spreadsheet file. It makes more sense to include them with the file than to consider them user preferences for inclusion in a dotfile. -Fixed a bug that prevented locked cells from being recalculated (this actually looked deliberate, but I don't understand why, so I "fixed" it anyway). -Locked cells now can't be changed even if the command is entered from scratch at the command line (previously, you were only pre- vented from using =, <, >, or " to enter insert mode while in a locked cell). -There are now openrow and opencol commands ("o" followed by "r" or "c") that work like insertrow and insertcol ("ir" and "ic"), except that the new rows/columns are added after the current row/column instead of before it. -When adding new rows/columns with a, i, or o, the new cells will always be included in the same ranges (named, framed, or color, as well as those used in an expression) as their counterparts in the current row/column. This is handy when adding rows/columns at the edge of a range, by moving to the appropriate side of the boundary and using the appropriate command (i or o). This is actually multiple changes, since each type of range had to be dealt with individually. -Added new command line options for use in piping data to another program or redirecting to a file. These are: "-P range" or "-P range/var" for writing a range in sc format to stdout (the "var" is a cell address used to adjust addresses for inclusion in another file starting at cell address "var"), and -v for causing values to be output instead of expressions when the -P option is used. This only outputs cell data and formatting, without all of the colors, range definitions, column formatting, etc., that are included with the normal put command or when piping the output without the -P. Also, piping in general has been much improved. -When using get to load a new file, all options are reset to their initial defaults, marked cells are unset, etc., and the user's .scrc file is reread. -Fixed a bug which allowed strings that are too long for the cell width to slop over into the next cell when they shouldn't (there is data in the next cell, for example). This bug only occurred in framed ranges. -The pipe symbol (|) is no longer required at the beginning of the "pipe" commands. The way the proper file descriptor is chosen has changed so it's no longer necessary. This makes it easier to use shell scripts for macros because there's no pipe symbol to quote (it should have been done this way in the first place--I don't know why it didn't occur to me before). -Added a "status" command for use in advanced macros that will return information about the current state, such as whether the file has been modified and whether stdin or stdout is connected to a terminal. -Added an "eval" command for use in macros to evaluate an expression without storing it in a cell first. This is a pipe command so the result will be piped to the macro. -Added a -q command line option to force sc to exit immediately after reading all files, including stdin, if that is being read as a file. This is useful for getting information from a file without entering it interactively (e.g. echoing the eval command to sc from a shell script, effectively using sc as a command line calculator). -Defining a function key as "" (the empty string) will effectively undefine it, so it won't be written to the file when saving. When used with F1, this restores the default behavior of reading the man page. -After doing a goto, update() is called, so that following a goto by a whereami in a macro will return the correct second address (the upper left corner of the current screen). -^E and ^Y both work in navigate mode now. ^E works both for scrolling and going to the end of a blank/non-blank region. The END key also works in navigate mode. -Fixed problem in the range commands (those initiated with a "/") where the cursor wasn't being positioned properly at the end of the command line due the the recent change in how insert mode is entered (for making the dot buffer work properly). -Simplified logic for doing autorun macros. -When starting to define a range from navigate mode with the TAB, ".", or ":" keys, a space is no longer inserted into the command line (there was an "ins_in_line(' ')" in the wrong place). -Added a new function, @err, that forces an error. If rows or columns are deleted and not pulled back in, all references to cells in those rows/columns will be replaced with @ERR until they are pulled back in (@ERR is equivalent to @err, but the caps show that it is due to a deleted cell being referenced). If the spreadsheet is saved to a file before pulling, the @ERR will be saved as a part of the expression, and will show as @err (lower case) after being read in again, since the deleted cell can no longer be restored. This is so that it can be fixed in a later session. Previously, the whole expression was lost if there was an error when the file was saved and reread. Also, if another deletion is performed before pulling the last one in, all instances of @ERR will change to @err, showing that these references may no longer be restored. -Fixed a bug when inserting columns at the end of the scrolling portion of a framed range, which caused formulas referencing the last cell in the scrolling portion to reference the new last cell (in the last newly added column) instead of the old one. -Added a command to sort the rows in a range according to either numeric or string data (or both) in one or more columns. See man page for details. -Added a "cslop" option (short for "color slop"), disabled by default, which, when disabled, prevents long strings from slopping over into a cell in a different color range, even if there is no data in it. When enabled with "set cslop" or ^Ts (to toggle it), this slopover will occur regardless of whether there is a change in color range or not. The default is very handy in framed ranges to get more data on-screen by including only the beginnings of row identifiers in the frame while maintaining a cleaner look, while enabling cslop still allows you to see the full string. -All options which can be disabled by using ! with the set command can now use ~ instead (handy for shell scripts because it doesn't need to be quoted). Some options worked this way before, but it wasn't consistent. -The move command will now accept the currently highlighted range as its second argument even if you don't press TAB, ".", or ":" to enter it into the line. CHANGES BETWEEN 7.9 and 7.10: Chuck Martin -Implemented "dd" command as a synonym for "dr" and "yy" as a synonym for "yr" (similar to the way these commands work in vi). -Added a vi-like y (yank) command to edit mode which copies from the command line to the text delete buffer without deleting. -Added two more goto commands: `goto #"regex"' to do a regular expression search through formatted numbers, and `goto %"regex"' to do a regular expression search through expressions. Both of these may take an optional range argument to limit the search to a specified range. -Added a |getframe command for use in macros to return the outer and inner ranges, respectively, of a frame, separated by a space. -You can now scroll up and down without moving the cell cursor using ^E and ^Y, as in vi. Since ^E also has another function, it only scrolls when immediately following a ^Y or another ^E. -Corrected man page which still erroneously stated that C centers the current cell (it is now used to define a color). -ZZ now only writes a file if it has been modified. It also gives an error message if there is no default file name. -If cell highlighting is turned off while color is on, the cell pointer (<) no longer leaves a trail through colored ranges (I don't know why anyone would use this combination of options, but it's fixed, anyway). -When writing a range with the put (P) command, format commands for columns outside the specified range are no longer written. -Removed "#include " from sc.c, vi.c, and xmalloc.c, since this is redundant when curses.h is included. -Added an autorun command (shortcut "A") to specify a macro to be run automatically as soon as the file finishes loading. Autorun macros will not be executed from a file which is merged (such as another macro). -When uninstalling, $(LIBDIR) (default: /usr/lib/sc) is removed completely, instead of just its contents. -When used in a pipeline rather than a terminal, the format command doesn't try to resize the column because the format is "too large" for the screen, which resulted not only in negatively sized columns, but also segmentation faults when switching to interactive mode. -Initialized variable "pid" to 0 in readfile() function in cmds.c to eliminate potential hangs when closing a file after reading. -A message was added to let you know when a file is being read so it doesn't look like the program hung when reading a large file. -When switching from reading from a pipe to interactive mode, there is now a stopdisp() before the freopen() and a startdisp() after to make sure the curses initialization is done properly. -Now checks to see if the stdin is a tty before trying to check if the terminal has colors (curses has_colors()) to prevent segfaults when run in a pipeline. -Modified both the sc and psc man pages for a more consistent look. -You can now use numeric arguments in the middle of a command in edit mode, as well as the beginning. For example, d3w and 3dw both do the same thing. If you do both, they will be multiplied. -sc now uses getopt() to parse its options. -If stdin is not a terminal (as in a pipeline) and a filename of "-" is not given, stdin will be merged in after all files on the command line have been processed. -Added a modflg++ to the frame-handling part of the deleterow() function in cmds.c so that deleting a row in a frame will cause you to be prompted to save the file if you quit. -Added an autoinsert option and toggle (^Ti) to automatically insert a row/column each time the last row/column is filled in the scrolling portion of a framed range if craction is set to move the cell cursor so that it moves outside this range. -Marked cells are now updated properly when inserting or deleting rows inside a framed range. -In both edit mode and navigate mode, "v" is now a synonym for ^V. -Moved savedot() out of vigetch() in vi.c so that the f, t, F, and T commands wouldn't be saved for repeating with the dot command. Added a ";" and "," command to repeat these commands instead, as in vi (the latter reverses the direction of the search). -Replaced all instances of "insert_mode()" in the main loop in sc.c with "edit_mode; write_line('A')" so that they are written into the dot buffer properly. -Pressing RETURN in insert mode enters an ESC into the dot buffer instead of a ^M so that the dot command only repeats the last change, without automatically ending the input. -Removed a sync_refs() from the frame-handling section of insertrow() in cmds.c so that deleting and then pulling a row in a framed range doesn't mess up expressions that referred to cells in that row. -When not editing a line, ^A goes to A0 like the HOME key. -Navigating with the HOME key, ^A, 0, ^, $, or # now saves the current position for returning with `` or ''. CHANGES BETWEEN 7.8 and 7.9: Chuck Martin -Fixed a bug where cells in the delete buffer were having their row and column numbers changed each time they were pulled to match the last pull address instead of keeping their original values. -Added two more options for the 'p' (pull) command, in addition to 'pr' (pull rows), 'pc' (pull columns), and 'pm' (pull merge). These are 'px' (pull exchange), which works like 'pm', but instead of leaving the contents of the delete buffer unchanged, exchanges the cells in the delete buffer with those being overwritten; and 'pt' (pull transpose), which works like 'pm', but transposes the rows and columns while pulling them into the spreadsheet. Also added equivalent commands for use in macros: pull (pm), pullrows (pr), pullcols (pc), pullxchg (px), and pulltp (pt). -Fixed bug when inserting rows at the end of the scrolling portion of a framed range, which caused formulas referencing the last cell in the scrolling portion to reference the new last cell (in the last newly added row) instead of the old one. -Added a 'yankrow' and 'yankcol' command for macros, and 'yr' and 'yc' equivalents for the user, which work like /y, but work on whole rows or columns instead of a range. 'yr' is limited to the current framed range, if any, just like the other row commands. -When deleting, yanking, etc., the upper left and lower right cells of the range are allocated if they don't exist, so that pulling them back in will always work correctly, even if all the cells in one or more edges of the range being erased are empty. Pre- viously, they might be offset due to the pullcells() function using the minimum and maximum rows/columns of any cell in the delete buffer to determine the range being pulled. -The closecol() function now only takes one argument, since the first argument was always curcol whenever the function was called. -If the color option is the only one that has been changed, it will still be saved in the file (the test was reversed). -Cells may now be marked while in navigate mode. -Can now 'goto' a cell, range, etc., while in navigate mode, without losing the command line being entered. -Added a |query command that allows a macro to display a question or message and obtain information from the user. -The 'run' command now frees up the space allocated to the command string being run after it's through. -Removed a '+1' from the coltoa() function because it was causing the column returned to be off by one. -Moved the gmyrow and gmycol variable assignments to the beginning of the RealEvalOne() function so that @myrow and @mycol work not only in numeric expressions, but also in string expressions. -Two variables in screen.c seemed to serve the same purpose. These were lastcol and lastcurcol. All occurrences of lastcurcol were changed to lastcol to eliminate the redundancy, and lastcurrow was changed to lastrow for consistency. -Numeric arguments are now accepted in edit mode and navigate mode. For now, the numeric argument must precede the whole command, rather than come in the middle. For example, "3dw" works; "d3w" does not. -The f, F, t, and T commands in edit mode now work properly even if the cursor is currently on a character matching the one being searched for. -More bugs fixed in screen.c dealing with framed ranges, one which was causing the row labels at the left of the screen to disagree with the actual cells being shown under certain circumstances. -Many more minor changes and bug fixes I've forgotten, and don't have the time to figure out from the diffs. CHANGES BETWEEN 7.7 and 7.8: Chuck Martin -Separated most of the code for framed ranges into its own file. -Inserting, deleting, or appending rows while in a framed range now only effects the framed range you're in. The old behavior of inserting, deleting, or appending the row or rows all across the whole spreadsheet is still the default behavior when done outside of all framed ranges. -Inserted columns are never hidden when created. Previously, the hidden flag was copied from elsewhere, resulting in new columns potentially being hidden for no apparent reason. -Columns may now be inserted at the end of the scrolling region of a framed range. Previously, they were added to the bottom of the frame instead. -Changed the behavior of the delete row/column (dr & dc), the delete range (/x), and the delete cell (x) commands so that subsequently pulling the deleted row/column/range into another part of the spreadsheet will cause references to external cells to continue to point to their original locations. References to deleted cells show as "ERROR", but pulling the cells back in restores the references. -Implemented a "move range" (/m) command, which, unlike deleting and pulling, will cause all cell references to move with the range (both internal references to external cells and external references to internal cells; see the man page for more informa- tion). -Implemented a "yank range" (/y) command, which copies a range into the delete buffer without actually deleting it. -The range-copy command (/c) may now be used without arguments, or with only the destination specified. See the man page for details. -Implemented colors and color ranges, with the ability to base foreground and background colors on a calculation or test, and to set colors differently for negative numbers or cells with errors. See man page for more information. -Writing a file now gives a message telling what it's doing, instead of only after it's done (useful for large files, so you know it probably didn't lock up). -The `, ', and * commands now work in navigate mode. -The yn_ask() function in cmds.c now ignores anything except y, Y, n, N, ESC, and ^G, instead of giving an error message and returning. -@pi now acts as a function instead of a constant, so it won't be optimized away unless the optimize option is turned on. -You can now "define" a cell (give it a name) without pressing TAB or equivalent to start highlighting a range. -The redraw command no longer sets FullUpdate (it may not be necessary). -Negative numbers are now treated as constants instead of expressions (required adding unary "-" operator to list of tests in constant() function in interp.c). -Changed isfunc from type bool to type int, since this is more in keeping with the way it's now used. -Moved test for KEY_END back into nmgetch(), since, not being a control character, it wasn't working properly in the main loop. -The |getnum and |fgetnum commands can now return ERROR and INVALID if the cell has a first or second generation error, respectively. -Removed "#include " from range.c, since this is redundant when curses.h is included. -Cursor keys now work properly with the END key (or ^E). -The goto command now works with all labels, including those which are the same as keywords. -ESC and ^G can now be used to cancel defining/undefining a framed range, and an error message is issued for invalid frame commands. -Strings can now be rejustified by pressing {, }, or | while the cell cursor is in the appropriate cell. -Documented the changes made to @index and @stindex in version 7.7 in the man page. -Fixed numerous bugs in the screen-handling code in update() in screen.c (mostly in handling frames). Hiding columns no longer causes sc to lock up, and column headings are centered better. This is terribly inefficient code, and a nightmare to debug, and really needs to be rewritten from scratch. -You can now use ":" in navigate mode as a synonym for "." or TAB. -Removed redundantant TAB description in list of keybindings for edit mode in the man page. -When defining a range with the cursor, "," will now insert the range into the command line followed by a literal ",", similar to the way ")" works now. Also, if the range is ended with a TAB, ".", or ":", a space will be inserted after the range in the command line. -In navigate mode, "c" will insert the color range, "f" will insert the outer frame range, and "r" will insert the inner frame range which includes the current cell into the command line. -Going through the command history now positions the cursor at the end of the line instead of the beginning. -The command for hiding rows/columns has been changed from "z" to "Z", so that "z" could be used as in vi to move the current cell to the top ("z") or middle ("z.") of the screen, and also to center the current column ("z|") or the current cell, both vertically and horizontally ("zc"), which aren't in vi, but are patterned after vi. Other "z" commands may be added later. Also implemented vi-like "ZZ" shortcut to save and quit in one step. -Added "tags" to the list of files to be removed during a make clean or make distclean. -Removed the "-lfl" from the LIBS line in the Linux section, since flex isn't being used (Was it ever? Should this also be done in the system V.3 section?). -Lots more minor bug fixes and things I've forgotten. CHANGES BETWEEN 7.6 and 7.7: Chuck Martin -The openrow() function has been eliminated, and insertrow() has been rewritten to eliminate looping. The closerow() function has also been rewritten to eliminate looping (it takes a second argument specifying the number of rows to delete). Also, the opencol() function has been eliminated, and its functionality incorporated into insertcol(). -New commands: insertrow, insertcol, deleterow, and deletecol for use in advanced macros. -Added rowsinrange and colsinrange variable assignments in doformat() function to eliminate potential lock-ups. -Changed many shorts to ints because they were actually increasing the code size instead of decreasing it, and slowing things down. -Added a cbreak() to the closefile() function so you can now "press any key" instead of "press RETURN" to continue. Also changed the main loop under "case '!'" in the same way. -Removed redundant v_name() declaration from write_fd(). -New command: run, for running arbitrary programs (useful in function key definitions). -The getnum, fgetnum, getstring, getexp, and getfmt commands may now be used with a range argument to get data from more than one cell at a time. -The @index and @stindex functions may now take a third argument to index into a two-dimensional array as well as the old one- dimensional version. Also, the @lookup, @hlookup, @vlookup, @index, and @stindex commands will now accept their range argument as the first argument instead of the second (the old order is still accepted), which makes more sense with the new two-dimensional @index and @stindex. -The goto command no longer duplicates the previous goto command if there is an error in the command (accepting the WORD token as an argument to mean "do nothing" fixed this). -"Showing" (listing) named ranges no longer pipes through sort, since that also sorted the headings and the following blank line with the data. Instead, named ranges are inserted into the list in sorted order as they're created. -Deleting a named range now updates modflg like it should. -The F1 key now defaults to running "man sc" if not redefined with the fkey command, and the default also works in edit mode, unlike the user-defined function keys. -When starting a range command with '/', if you change your mind and press ESC or ^G, the error line is now cleared. -When using 'ns' to show which cells have attached notes, a message is now presented informing the user what the highlighted cells mean (I know it should be obvious, but a message reminds you in case you get distracted, and forget your current cell isn't highlighted). -startshow() and showdr() were moved from sc.c to vi.c, where they can be used more easily. -RESCOL has been changed from a macro to an int, and renamed to rescol (lowercase), so it can be changed when the spreadsheet has too many rows for the row numbers to fit properly, causing problems with the first displayed column. -The default value of mode_ind has been changed from '.' to 'i'. -Renamed navigate_mode() to toggle_navigate_mode() to better fit its function. -Fixed history functions so that some lines aren't skipped when moving forward. -Fixed TAB behavior in navigate mode so it doesn't exit navigate mode, requiring you to press ^V to continue defining the range. -Implemented vi-like G, P, and p commands in edit mode, moved ^I (TAB) funcionality from main loop to write_line(), and added '.' as a synonym for TAB, and changed behavior of RETURN/ENTER key in navigate mode to end input as it does in insert or edit mode if a range is highlighted instead of entering the range into the line like TAB or '.' does. -The H, J, K, L, HOME, and page up/down keys now work in navigate mode. -The savedot() function now checks to see that there are at least two chars left in the dot buffer before saving if the last key pressed requires two chars to store (cursor keys, HOME, etc.). -Pressing ESC or ^G now cancels the r (replace a single character) command instead of being entered into the command line. -Implemented vi-like c0, d0, c$, and d$ commands, and fixed the dw command again (was deleting an extra char). -Replaced loop in del_chars() with a memmove(). -Implemented framed ranges, whereby rows or columns at the top, bottom, left, and/or right of the range will remain onscreen at all times whenever the cell cursor is in that range. -The set and goto commands now work properly again (set wasn't allowing multiple arguments and goto didn't accept error or invalid as an argument). -Defined ranges with names equivalent to function names (without the "@") or other keywords no longer cause errors when subsequently used. CHANGES BETWEEN 7.5 and 7.6: Chuck Martin -Fixed error() macro so messages are displayed like they should be. This should probably be rewritten as a regular function. -Added programmable function keys. -Added a navigate mode that can be switched to while editing a line, which allows movement around the spreadsheet using any and all cursor movement keys. In addition to the control characters and cursor control keys, this now also including 0, $, ^, #, h, j, k, l, H, J, K, L, b, and w, which used to be unavailable while editing. The arg variable was made global so that cursor movement keys can be given a numeric argument even when in navigate mode. Since ^V is the command to enter navigate mode, it must be pressed twice to get its former action: once to enter navigate mode, and a second time (after moving to the cell you want) to enter the cell address in the command line (the RETURN/ENTER key may be substituted for the second ^V). -The ins_in_line() function is now global, so it can be used from the main loop in sc.c. -The /d command (for defining a range) doesn't automatically start highlighting a range, since that would mean switching to navigate mode, and a range name needs to be entered first, but the rest of the range commands enter navigate mode and start a range immediately. -Special keys, such as cursor control keys, the END, INSERT, and DELETE keys, etc., are no longer converted to straight ASCII equivalents in nmgetch(), but are passed as-is to the functions where they're used. Also, the conversions made in nmgetch() for the wyse wy75 have been disabled because they interfere with the programmable function keys. -Using the TAB key to highlight a range and enter it into a command will now insert it at the cursor position instead of appending it at the end of the line. -When editing, the current command line is temporarily added to the end of the history so you won't lose your work if you go back through the history, although it doesn't become a permanent part of the history until you press the RETURN/ENTER key. -The savedot() and dotcmd() functions have been modified to save and retrieve special keys like the INSERT and DELETE keys as two consecutive bytes. -The default prefix in the Makefile has been changed back to /usr. CHANGES BETWEEN 7.4 and 7.5: Chuck Martin -Rewrote error() macro definition to fix segfaults in version 7.4. -Fixed division operator to eliminate segfaults in version 7.4. -Do NOT use version 7.4. The above bugs make it unusable. My sincere apologies for not testing thoroughly after some last minute changes. CHANGES BETWEEN 7.3 and 7.4: Chuck Martin -Added a BUGS file that lists known but not-yet-fixed bugs. -Lots of changes to allow -Wall to be used with gcc without too many warnings, including additional #includes in some files, parentheses around assignments used as booleans, braces in the sed files, and the removal of many register declarations. -Wall is not used by default in the Makefile because a few innocuous warnings remain that may worry some people if they don't under- stand them. Also changed -O to -O2 in Linux section CFLAGS. -Added an uninstall target to Makefile. -Before jumping to a new cell (any movement except simple cursor movements or half-screen movements), the location is remembered, and may be returned to by using either `` (to restore the cell cursor to its original location on the screen) or '' (to do a simple jump to the cell without regard to where it exists on the screen). This is similar to the way vi works. -The Write command now puts date formatted values in the correct columns instead of at the beginning of the line ("pline" was changed to "pline+plinelim" in printfile() function). -Added a check for rfd != NULL in openfile() function so /s command and writing to a pipe will work again without segfaults. -Notes can be attached to cells, which amounts to providing a link to a range that can be jumped to quickly. See man page for details. -Backslash-escaped double quotes now work properly in strings, such as filenames. -If stdin is not a tty, no attempt is made to write the data to the screen. Also, startdisp() and stopdisp() become no-ops, and the first startdisp() has been moved to just before the main loop. This allows sc to work better in a pipeline for non-interactive use. -Implemented vi-like F and T commands for moving backward in a command line in edit mode. -Deleting to a character in edit mode with dt, df, dT, or dF now works properly even if the character is not found. -If a cell changes from ERROR or INVALID to a value of 0, the cell will now be updated properly on the screen without having to force an update by moving it offscreen and back on or doing a ^L. -If the denominator of a division operation references a cell with an ERROR, the result will correctly show INVALID instead of ERROR. -More "style" changes to code for more consistency, and other minor changes of little real effect or consequence. CHANGES BETWEEN 7.2 and 7.3: Chuck Martin -Fixed doend() function to prevent occasional lock-ups when using ^E or the END key. -When outputting formatted data with Write or Tbl, dates formatted with Fmt are now converted and output correctly. -Makefile has been rewritten, with many unused targets removed, "clobber" target renamed to "distclean", ${prefix} variable defined to allow installation directory ${prefix} to be specified (/usr/local is now the default), among other things. -Added start-up files $HOME/.scrc and ./.scrc for configuration commands. New commands added for specifying default filename extensions are: scext, ascext, tbl0ext, tblext, latexext, slatexext, and texext. Also added `set scrc' (see man page). -If the filename ends in either .sc or the user defined extension for regular sc files (scext - see the man page), this extension will be stripped first before a new extension is appended by the Put, Write, or Tbl commands. If scext is defined, it will also be appended when saving a file with the Put command if it isn't already present. -Created temporary string variable for manipulation of filename when adding/deleting extensions before saving a file, since there wasn't always room in the existing space. This also fixed a bug when expanding `~/' or `~user/' into a user's home directory. -Expanding `~/' or `~user/' into a user's home directory now also works with pipes. -Much longer file names are now allowed when adding extensions on filesystems that allow them. The actual length allowed is filesystem dependent. -A filename of `-' can now be used to tell sc to read spreadsheet data from the standard input. Also, multiple files can now be specified on the command line, and they will be merged, with the default filename being taken from the first one if possible. -Added an "advanced" macro capability, where commands and data are passed through pipes. New commands added are: up, down, left right, endup, enddown, endleft, endright, |whereami, |getnum, |fgetnum, |getstring, |getexp, |getformat, |getfmt, recalc, redraw, and quit. Documentation is still sparse. Only available on systems which support pipes. -The mdir command no longer automatically appends a `\' to the path you enter, so that an actual filename can now be used (this is handy for advanced macros, which might contain more than one macro in a file, so you can add command line switches and arguments to the defined string instead of a filename). -Removed modflag++ from readfile() so sc doesn't assume the file was changed just because a macro was run. If a macro changes a file, modflag will be updated when the change occurs. -Significant digits in the fractional portion of a number were being lost if preceded by a 0 when the format specification used #'s. An "else break;" if a nonzero digit was found when looking for trailing 0's fixed this. -Decimal points are now suppressed if there are no significant digits after the decimal point in a formatted number. -The g (goto) command now has an optional second argument when used for searching that can be used to specify a range to search instead of always searching the whole spreadsheet. -Range names can now be less than three letters without being mistaken for column names, and may also contain any mix of letters, numbers, and underscores, as long as it isn't a valid cell address and the first character isn't numeric. -Rewrote the evaluation of the `-' and `=' operators and added an fflush(stdout) to each to force optimizing compilers like gcc to pop the FPU stack so both sides of the calculation have the same precision. Otherwise, comparisons would sometimes fail when they shouldn't, and subtractions that should be zero sometimes wouldn't be. This (differing precision due to the FPU) also turns out to be the cause of the spurious "Still changing after x iterations" message, which was fixed previously. -Removed toascii() in several places in nmgetch() to make sc 8 bit clean and suitable for international use (international characters can now be entered as data in cells). Also removed -DINTERNATIONAL from Makefile and elsewhere, since it should no longer be necessary. -Removed unused ClearScreen variable. -If the -e command line switch is used, rndtoeven is set both before and after loading the files, so that it will override the contents of any files loaded. -When editing a cell's existing format string, the cursor now starts out at the last character instead of after the last character. -No longer tries to center a range that's wider than the screen, which was causing lock-ups. -If current cell highlighting is on, turn the cell pointer (<) off (don't need both) and hide the hardware cursor at the lower right-hand corner of the screen unless editing a command on the top line. -Row indicators are now right- instead of left-justified. -Turn off highlighting on row and column indicators for the current cell so they stand out. -Implemented vi-like e command to go to next end-of-word in edit mode. Also, cw now works just like ce, as it does in vi and its variants. -If deleting the last character in a line, x now backs up as in vi et al. -Many more minor changes that will probably not even be noticed. Michael L. Hall -Don't quote (via a backslash) the "[" and "]" characters in LaTeX table output. CHANGES BETWEEN 7.1 and 7.2: Chuck Martin -A dummy fflush(stdout) was added to the RealEvalOne() function to work around a strange bug which causes spurious "Still changing after x iterations" errors when automatic optimization of expressions is off (the default). This still needs a proper fix. See the BUGS file for more information. -The goto (g) command now accepts an optional second parameter which specifies which cell is to be located in the upper lefthand corner of the screen, if possible. When saving the file, this parameter is included in the goto command that brings you back to where you left off. -The mark (m) command remembers not only which cell the cursor is in, but also which cell is in the upper lefthand corner of the screen. The ` command uses this information when returning to the cell, but the ' command does not, so you can decide whether to center the marked cell when returning (') (assuming the cell is not currently visible) or whether to try to restore it to its original position on the screen (`). -If the destination of a goto command is a range instead of a single cell, the whole range is centered on the screen, if possible, instead of the upper lefthand cell of the range. Named ranges in the tutorial have been adjusted to use this fact. -If quick numeric entry mode was enabled and you started to enter a number, then changed your mind and escaped out of it, any subsequent cursor movement (except h, j, k, or l; i.e. only control key combinations and cursor keys) would put you back into insert mode. Setting numeric_field back to 0 in the stop_edit() function in vi.c fixed this. -Cursor control keys now work properly while in quick numeric entry mode if craction is set to other than no action (it used to follow both desired cursor movement and craction direction). -Implemented vi-like C and s commands in edit mode. See the man page for details. -All instances of rndinfinity have been changed to rndtoeven and all tests have been reversed to make the old rndinfinity behavior the default rounding method and round-to-even (banker's rounding) an option. A new command line switch, -e, allows you to set rndtoeven when sc is started. Round-to-even may be superior for some applications, but it isn't the way most people round or expect rounding to be done. Spreadsheets which explicitly set rndinfinity will display an error when loading, but will behave as intended and will save without it. Those which don't set rndinfinity but depend on the former default behavior will have to either be started with the -e option or have rndtoeven set manually the first time they are loaded. The advantages of setting rndtoeven for some applications have been added to the man page. -The craction toggle has been redone (again) to prompt for the direction you want to move after entering data in a cell. See the man page for more details. Also, craction now only has an effect if data is being assigned to the current cell (i.e. the command just entered starts with either "let", "label", "leftstring", or "rightstring"). -Placed parentheses around all "OP_BASE + ..." macros in sc.h to stave off potential problems and increase robustness. -Many more additions, deletions, and changes (too numerous to mention) to remove unused and unnecessary stuff, fix minor bugs, and make for more uniformity of style. CHANGES BETWEEN 6.21 and 7.1: Chuck Martin -Converted function declarations and definitions from K&R style to ANSI style. -Removed unnecessary function declarations for standard library functions. -Multiple marks (up to 26) may now be set with m and copied with c. Marked cells may also be jumped to by using either ` or ' as in vi. For now, ` and ' work exactly alike, but this will change in a later release. -Expressions which resolve to a constant are no longer automatically optimized by default, but optimization may be turned on if desired with ^To or the -o commandline option. This allows you to edit expressions instead of having to reenter them from scratch. -Rewrote the @dts function from scratch. It is now Y2K compliant and allows parameters to be entered in y,m,d order as well as the old m,d,y order (the proper format is detected automatically). -Removed support for the undocumented feature added previously by David Fox (sorry, David!) which allowed dates to be entered as dd_mm_yy and replaced it with a similar feature which allows dates to be entered as y.m.d or m.d.y. This is actually just a shorthand way of entering the @dts function using only the numeric keypad, providing the year, month and day are all purely numeric (no formulas), and the year must include the century. -Rewrote the date formatting routine to use strftime() and added a second date format (format 4) identical to the regular one (format 3), but with a four digit year. -Cells may now contain date formatting strings, entered with F, which work similar to the standard numeric formatting strings, but allow all the same conversion specifiers as strftime() to format a date. A ^D as the first character in the string is used to distinguish a date format string from a standard numeric format string. -^L now redraws the screen as it was instead of attempting to center the current row. Added a C command for centering the current row. -Changed main() in help.c to type int (was void). -Cells in columns pi, ln, fv, pv, and if may now be referred to in expressions (they previously conflicted with two letter function names), but the number pi now requires the @ in front of it to work (@pi). -The Insert and End keys now work. Insert works exactly like i and End works like ^E. -Moving up and down half a screen at a time now works much better. -Pressing Escape to exit insert mode now backspaces to be more consistent with the behavior of vi and its variants. -Moved switch (craction) from ^M/^J part of main input loop to if clause in cr_line() so that pressing RETURN to enter input mode won't move you to another cell if newline action is set to something other than the default no action. -Added a test in deleterow() to prevent segfaults when deleting too many rows. -^Tr now enters a special toggle mode instead of just toggling the newline action, since there are three options instead of two. That way, if you need to toggle twice in a row to get the action you want you can use ^Trr instead of ^Tr^Tr, which I think is easier (the second r can be any key except ). -Removed a line from the tutorial because the first page was one line too long for a 24 line screen. This really needs to be completely rewritten, IMHO. -There are probably many more bug fixes and other changes including style changes to make the code look more consistent that I've forgotten or that I was just too lazy to figure out or document properly, but they're mostly minor. CHANGES BETWEEN 6.21 and 6.19 Mark R. Rubin -noted a problem using bison 1.16 (use any version but 1.16) Marco S Hyman/Ian */and others -Crypt/CRYPT_PATH define problem Paul Eggert -sc.doc $Revision: 6.21 $ 'buglet' Ulf Noren/Dave Lewis -AIX3.1/Microport System V/AT don't have notimeout() changed NONOTIMEOUT to NO_NOTIMEOUT, define if not present Niels Baggesen -function keys may not return ascii codes that isascii() understands -added an A command for vi mode (add at end of row). -Special key support: DC='x' (del char in vi mode), FIND='g' (goto), HELP='?', SELECT='m' Dave Davey -noted Ultrix 4.2 curses doesn't have idlok() [I added NO_IDLOK in Makefile] Kim DeVaughn -added ${RIGHTBUG} is now passed to sc.o && screen.o -suggested a better fix on SunOS 4.x dont use the Sys V package (CC = /usr/5bin/cc, etc), but use the BSD 4.3 defines David Bonnell -scqref [will produce] TROFF output instead of plain text, [when] you define QREF_FMT=TROFF in the Makefile. The resulting quick reference guide uses the MS macro set and you build with something like: scqref > quickref troff -ms quickref > quickref.ps Kurt Cockrum - sc.h: If not (defined(BSD42) || defined(BSD43) && !defined(ultrix)), include for the benefit of some USG systems... - screen.c: Repaired cpp logic: don't mention IDLOKBAD or idlok() unless SYSV3 defined; idlok() does not exist for USG pre-SYSV systems (may exist for SYSV{2,3,4}). - tutorial.sc: Repaired a number of off-by-1 errors. Mats Wichmann -cleaned up Robert E. Cousins MIF format support code which is compatible with FrameMaker. Neil Skilling -added @numiter which returns the number of iterations performed. It allows you to solve equations by direct substitution. Taking a guess from another cell if the first iteration otherwise taking the last best iterate. Other uses may be found. Martin MacLaren -MS-DOS cleanup of Makefile Art Mulder -^T toggle: don't list crypt if not available John Amanatides -pointed out a possible NULL ref in interp.c Phil Johnson -sc now appends: "asc", "cln", "tbl", etc. to output files -made the engineering format match that used by an engineer -deleted an unused engmult[] reference -added a fix to struct enode for the HP compiler Kevin Cosgrove -noted sc should use any predefined PI Jeff Buhrt -'make clean' now leaves the binaries and man pages [Jean-Pierre Radley] -'make clobber' cleans all built files (like clean used to) [""] -'-D' vs '-S' was needed on a XENIX2_3 line [""] -'quit()' -> 'doquit()', function conflict [""] -change xmalloc,xrealloc,xfree -> scxmalloc,scxrealloc,scxfree (xmalloc is a standard malloc) CHANGES BETWEEN 6.19 and 6.18 Tom Tkacik -sc.doc and CHANGES changes Edgard -moving right off the screen now redraws vs optimize Sisira Jayasinghe - added build.com (VMS) and VMS fixes Jonathan I. Kamen && Charlie Shub -noted fmod doesn't exist on BSD4.3 and Mt Xinu Ben Priest -vi compatability: ' ' moves right as well as 'l' while line editing Jeff Buhrt -one more possible NULL pointer fixed -added NONOTIMEOUT for those that don't have notimeout() in curses -undef CRYPT=-DCRYPT_PATH... if crypt isn't available -merged simple fmod into interp.c if fmod() is not present CHANGES BETWEEN 6.18 and 6.17 James Dugal - NULL pointer fix for IS_LOCKED Kevin Pye - add a new mode suitable for entry of large amounts of data. moves to next cell on return, maxrow/col when to start entering in the next row/col. (see help screens B&C) COMMANDS ADDED: ^Tz, ^Tr, Srowlimit=?, Scollimit=? David Fox - added a date format so that columns whose values are the number of seconds since 1/1/70 will be displayed as dates in the format dd-mmm-yy, and a modification to the grammar so data entered in the format dd_mm_yy will be converted into the number of seconds since 1/1/70. COMMANDS ADDED: f # # 3 Teus Hagen - labels are centered strings - constant strings with '\' preceeding character will be wheeled over the column width - a restart of sc on an sc file will go to last used cel - added toupper, tolower and do proper word capitalization COMMANDS ADDED: @toupper(), @tolower(), @capital(), @pi, "\[String] Jeff Buhrt - external functions null/previous message was backwards - cleaned up help.c by inserting a new screen - found a possible NULL pointer in screen.c CHANGES BETWEEN 6.17 and 6.16 Ulf Noren - added cell locking, disallowing input, to ranges of cells Herr Soeryantono - I added ifdef's around curses KEY_* functions his curses didn't have (Sun/OS 4 on a SPARC) Jay Lepreau - changes to tutorial.sc: how to get out, should be used w/ 24 lines - IDLOKBAD was not passed to screen.c - suggested error messages if the execl of crypt fails - pointed out BSD's crypt is in /usr/bin/crypt Henk P. Penning - suggested Makefile list the mode of the man page & tutorial.sc - make install will now install the psc man - yylval was not known in lex.c for HP-UX 7.05 Edgard - hitwinch fixes - KEY_HOME now takes you to 0,0 CHECK KEY_NPAGE/PPAGE Stephen (Steve) M. Brooks - suggested the man pages should include Sc's revision Dan Banay - code to set LINES and COLS after a window size change Bart Schaefer - @myrow/@mycol fix Bruce Jerrick - noted ln may not always work for the temporary source files Gene H. Olson - fixes for SIGWINCH for Sun OS 4.1.1 Teus Hagen - added three functions: 1) allow @PI as well 2) @upper/@lower for casing characters in a string 3) @capital for upper case first char of words in a string. Martin Maclaren - added MS-DOS support COMPILER USED: Microsoft C, 5.1 TOOLS USED : NDMAKE GNUBISON GNUSED PCCURSES Cuong Bui - has a working Vietnamese version of sc, noted a A_CHARTEXT mask problem Jeff Buhrt - when numeric prescale is on: 300 -> 3.0, now 300. -> 300. not 3.0 (numbers with a decimal aren't scaled) CHANGES BETWEEN 6.16 and 6.15 Tom Tkacik -fixed a bug in ^W Jonathan I. Kamens - added Makefile rules so scqref and psc don't clobber .o's Larry Philps - fixed a SCO XENIX vs M_XENIX problem - fixed a problem where dosval() might not xmalloc enough memory Dave Close - fix for Xenix 2.3 to reset terminal modes CHANGES BETWEEN 6.15 and 6.14 Lowell Skoog - fixed a bug in 'F'ormat Henk Hesselink - format.c double neg. sign - interp.c minr/minc bug, plus modflg wasn't set - fixed a hardcoded path in sc.doc - improvement: -show current cell format in top line -[buhrt: go into edit mode on the old format if it existed otherwise insert mode] Jonathan Crompron - made sure doformat() does a checkbounds() Stephen (Steve) M. Brooks - pointed out -s in psc was broke Michael Richardson - fixed negative numbers in exponential format CHANGES BETWEEN 6.14 and 6.13 Mats Wichmann - Sys V R4 patches, fixed 'Press RETURN ...' on a shell command Tim Theisen - changed #define for memcpy/memset under ultrix Rick Walker - Added @myrow and @mycol to give the row/col of the current cell 'The two functions are @myrow and @mycol, which return the numerical row and column of the calling cell. The cell directly above a cell in the D column could then be accessed by @nval("d",@myrow-1).' NOTE: @myrow and @mycol can't be used in specifying ranges. CHANGES BETWEEN 6.13 and 6.12 Rick Walker - pointed out a move(x,y)-> (y,x) in sc.c Glenn Barry - Further SunOS 4.X cleanups Tom Tkacik - made sure 'J' moves downward 1/2 a screen even at the bottom David I. Dalva - pointed out crypt may not be in /bin/crypt Gregory Bond - allows the vi-mode editing of very long expressions (> 1 screen width) to work on 2nd + subsequent lines Tom Anderson - "let A1 = aaa" (where aaa is defined as A0:A0) is now valid - added autolabeling 'When there is an empty cell to the left of a cell that has just been defined (with /d), a label is created in the blank cell. The label holds the definition that has just been created. This labeling is only done for definitions of single cells (and not for ranges).' 'The feature can be turned on and off with a toggle (^T) command' Petri Wessman - Added support for SLaTeX, 'a Scandinavian version of LaTeX, in intensive use ... in Finland and in Sweden' Jeff Buhrt - vmtbl.c explictly set arrays of pointers to NULL, vs memset() - psc [-P] plain numbers only:a number only when there is no [-+eE] [-S] all numbers are strings - psc: a number must end in [0-9.eE] anything else makes it a string (4, 4., 4.5, and 4e are numbers; 4-, 4+, etc are not). - psc: made sure we grow enough when we call growtbl() - cleaned up the Makefile w/ a few suggestions - SIGWINCH is delt with next time the screen would update (testing) - added IDLOKBAD to get around a SysV curses bug (see Makefile) - moved screen functions into screen.c (except for one indirect 'repaint()' call in sc.c, and help.c) CHANGES BETWEEN 6.12 and 6.11 James Dugal - added format.c to SRCS in Makefile - noted RETURN didn't enter insert mode Peter King - pointed out iscntrl is broken on some other systems as well - sent some lint cleanups Michael Richardson - patch to stop format looping when scientific notation was selected Glenn T. Barry - code to turn on hardware scrolling and added 'slow speed display' speedups, default for SYSV3 or see -DSUNOS41SYSV in Makefile. Tom Tkacik - fixes to make sure J and K move same amount, and re-added H code Jeff Buhrt - fixed a possible xfree(NULL) in getent() (found when adding K_VAL) - merged compiler cleanups * - added $(name)qref to print a Quick Reference card - got rid of INVALIDS that may have been left around * - pressing return on a empty line puts you into insert mode (like in <=Sc6.1). When entering you can also press ESC to go into the editor (no change); this is also documented now so it might stay around this time. CHANGES BETWEEN 6.11 and 6.10 Jonathan I. Kamens - sc.doc now mentions the tutorial file in the FILES section Andy Fyfe - pointed out 3 locations where a NULL should have been '\0' Robert Bond - pointed out the ERROR could hide a cellerror Piercarlo Grandi - H,J,I,K now move 1/2 screen Ulf Noren - changes for AIX V3.1 - defined CHTYPE and NLS for the preprocessor. CHTYPE is the type of every character in a curses window. - Added KEY_BACKSPACE to nmgetch - strtof ifdef - Iteration change: when Sc says: "Still changing after 9 iterations" Sc at that point will have eval'd 9 times Chris Metcalf - pointed out I broke setlist when adding 'goto {error,invalid}' James P. Dugal - iscntrl() wasn't handling TABS though CRs under Pyramid OSx4.1 Peter King - BROKENCURSES patch for nl()/nonl() bug on some BSD systems - backups, tutorial file, and man references now depend on $name - DFLTPAGER to DFLT_PAGER fix CHANGES BETWEEN 6.10 and 6.9 Tom Tkacik - when moving off the current table (resizing) now move the cursor on 'l' or 'k'. - patches to sc.doc to correctly format the vi-mode notes Jim Clausing - made sure / doesn't try to divide by zero. Tom Kloos - correction to substr() example in help.c Piercarlo "Peter" Grandi - Disable non-constant expressions while loading - Added extra code in dealing w/ floating point exceptions - #ifdef'd SAVENAME (vs hardcoded SC.SAVE) to allowing changing the emergency save name. Casey Leedom - Makefile changes: man extension, RINT note, make values should never be left undefined and then referenced, don't leave around *.old's Tom Anderson - patches to add type of column format (note format now has 3 args) Jeff Buhrt - xmalloc/xfree fatal() will now call diesave() (MAKE SURE the saved file is ok if this were to happen) - history[] is now a circular queue, this will cut down on the number of data moves and also xmalloc/xfree calls (idea from Keith Bostic) - cells with an error (ex: divide by 0) will show 'ERROR' - you can 'goto error' (or 'goto') to find an ERROR (for next ERROR) Robert Bond - When in numeric mode the ^B, ^F, ^N, ^P key will end a numeric entry. CHANGES BETWEEN 6.9 and 6.8 Jim Richardson - pointed out vi mode was not documented in sc.doc - found a nasty buffer limit bug in savedot() - a side effect was ^D could cause a core dump (-Jeff) Tim Wilson - Hints on compiling on Ultrix Eric Putz -patch for printfile() (sc died on huge # of columns in a W) Jeffrey C Honig -patch for lex.c which bombed on SunOS 4.1 if $TERM was not set Tom Kloos -psc now calls [+-.] strings vs numbers. -also pointed out a format reversal problem Jack Goral -changes to Makefile to compile under SCO Unix V rel 3.2.0 Mark Nagel -changes to allow arbitrarily complex formatting of cells Kim Sanders -^W generated an incorrect equation (line was not started at beginning) Mike Schwartz -a put command will use the same encryption key as when the file was read. >I have a suggestion for making the encyrption option of "sc" more >usable: Right now, if you use the -x option when you start up sc, it >prompts you for the key (just like "vi -x" does). But when you try to >write the file out using the Put command, it asks for the key again >each time. Why not make it use the same key you used before (as "vi >-x" does)? That would really help, because as it is, each time you try >to save the file you run the risk of mistyping the key. > >You might think this causes a security problem, since the key is then >an argument to crypt, and hence is visible from ps. But when crypt >runs, the first thing it does is to copy the key to an internal buffer >and then zero out the argv copy, so the window of vulnerability is >vanishingly small. Adri Verhoef - pointed out a ^D caused a core dump (fixed) Gene H. Olson - format now grows the spreadsheet before setting the column format. - removed an extra ';' that caused a possible column number trashing Paul Eggert -sc now also has round-to-even, also known as ``banker's rounding''. >With round-to-even, a number exactly halfway between two values is >rounded to whichever is even; e.g. rnd(0.5)=0, rnd(1.5)=2, >rnd(2.5)=2, rnd(3.5)=4. This is the default rounding mode for >IEEE floating point, for good reason: it has better numeric >properties. For example, if X+Y is an integer, >then X+Y = rnd(X)+rnd(Y) with round-to-even, >but not always with sc's rounding (which is >round-to-positive-infinity). I ran into this problem when trying to >split interest in an account to two people fairly. -While we're on the subject, @round(X,Y) should also work when Y >is negative. For example, @round(123,-2) should yield 100. CHANGES BETWEEN 6.8 and 6.7 Jeff Buhrt (with help from some beta testers-Thank you) 1) added a per row memory allocation -runs in about 1/2 run time and 1/3 the space of 6.6vm.1 -insert/delete row now just moves pointers (# == maxrow+1-currow) and blanks one row (of columns (maxcol)) -as the number of cells grows the size is more linear (no more ##Meg images except for 100,000's of rows....) -row to column pointer translation is done by a macro (ATBL) that returns a pointer to the cell pointer. *ATBL would be a pointer to a *ent (cell). -the maximum # of columns is limited by ABSMAXCOLS or sizeof(struct ent *)*maxcols (whichever is smaller) (702 * 4 = 2808 is no real limit even for 286 large model) -the maximum # of rows is limited by the virtual memory limit or sizeof(struct ent **)*maxrows (whichever is smaller) (4*X=64k, X=16384 rows (excluding malloc overhead) on a '286 large model. Even w/ 3.25Meg and 10Mhz) (plus of course any memory used for cells) 2) dolookup (int vs double) 3) dolookup calling eval w/ ent * not enode * (dolookup called w/ ent * not enode *) 4) cleaned up a lot of .... *x = 0 to (.... *)0 (cmds, interp) 5) psc: fwidth/precision were reversed on the output 6) Backup copy (on save) using same mode to [path/]#file~ (will prompt if a backup fails) 7) put y/n prompt function into yn_ask(mesg) 8) found a move(x,y) in sc -> move(y,x) and only move when needed 9) we use FullUpdate || changed (to see if ANY cells changed) before trying to redraw the screen in update (now we don't try to redraw every time a key is hit) -if we are stand[ing]out we do not create a cell just to force a standout inside the repaint section of update() -only draw blank cells if we cleared it or it is standing out reason: the less work (what to update) curses has to do, the faster a screen update will be (less cpu required) 14) {insert, delete}col replaced w/ {open,close}col(currow, numcol_to_insert) (limits looping) 6.7.1.1 15) goto nonexistant cell may loop 16) make sure that startup size will at least fill the screen w/ cells. 17) added version.c 6.7.1.2 18) When we would normally die w/o saving (SIGQUIT, etc), we now ask if people would like to save the current spreadsheet. If 'y', saves to the current file name, otherwise ~/SC.SAVE, then /tmp/SC.SAVE if all else fails. 6.7.1.3 19) don't use malloc.c for production code 20) progname is now truncated to just the basename (systems w/ long paths caused problems) CHANGES BETWEEN 6.1 and 6.7 Dave Lewis - Found and fixed a null pointer derefrece in the 'R' command. Rob McMahon - Changed the ctl() macro to work with ANSI style compilers. Cleaned up some non-readonly text problems. Rick Linck - Fixed a bug in lex.c - Ann Arbor Ambassadors have long ks and ke termcap entries. Sam Drake - A fix for undefined C_* symbols in AIX. Peter Brower - Cleaned up the INTERNATIONAL ifdefs with more portable code. Glen Ditchfield Cleaned up a problem in crypt.c when the encrypted file shrank. Bob Bond - Vi style editing for the command line. A bug in range name aliases. Jeff Buhrt - -Added "~" filename expansion. -702 columns (A-ZZ) and unlimited rows/cells based on max. memory -fixed a few bugs -slightly decreased CPU usage -MAKES backup copies of files -understands ~$HOME stuff CHANGES BETWEEN 5.1 and 6.1: Andy Valencia - xmalloc aligns data to a double boundary. Lawrence Cipriani - Fixed a bug in the "do you want to save this" sequence. Soren Lundsgaard - A null pointer derefrence. Rick Perry - Cleaned up a problem with modchk() in sc.c. Gregory Bond - Added code for multi argument versions of @min and @max. Tad Mannes - Added code to save/restore hidden rows and columns when the data base is saved or restored. Marius Olafsson - INTERNATIONAL changes. Allows full 8 bit characters (if curses supports them.) Kurt Horton - Added support for @pv, @fv and @pmt financial functins. Tested lots of different systems, linting. John Campbell - Support for VMS. See VMS_NOTES. Peter King - User selection of row or column order for recalculation. Also affects order of traversing regions in /f and /r User setting of automatic or manual recalculation. User setting of number of times to try recalculation. + and - commands when in non-numeric mode to do increment and decrement operations. @index, @stindex, @atan2, @lookup functions. Save/restore options. Support for TeX, LaTeX, and better support for tbl in "T" cmd. Provision of a copyent function to copy entries (same code repeated in several locations) Forwrow, backrow, forwcol, backcol functions to replace repeated code Correct interpretation of ESCAPE or ^G as an abort when in a two character command such as 'ar' or 'ac' Cleanup in eval() - catches non-trap function errors. Bob Bond - Added search options to "g". Added supression of hidden columns to "W" Added the mod operator "%" New help functions. Constant prescale "$" Added string matching to @lookup. Some more bug fixes. Testing, integration, documentation. Alan Silverstein- Greatly revised the manual entry. Added menus for ^E command and row/column commands, which involved a bunch of code cleanup. Changed top row display to clearly indicate string labels versus number parts, and to distinguish string functions from constant labels. When the character cursor is on a cell (not topline), ^H (backspace) is like ^B (move back one cell), rather than being ignored. When the character cursor is on a cell (not topline), ^I (tab) is like ^F (move forward one cell), rather than being ignored. ^R is no longer identical with ^L. Now ^R highlights all cells which should be entered by a user because they contain constant numeric values (not the result of a numeric expression). Added a ^X command, similar to ^R, which highlights cells which have expressions. It also displays the expressions in the highlighted cells as left-justified strings, instead of the label and/or value of the cell. Added indirection functions (@nval() and @sval()) for simple table lookups. Given a column name and row number, they return the numeric or string value of the selected cell. Added external functions (@ext()) for non-trivial computations. Given a command name and argument, it calls the command and reads back one output line. Added a ^T,e command to toggle enabling of external functions. Changed ^T,t to only control the top line display, and added ^T,c to control current cell highlighting. (Separated the functions.) "!" (shell escape) gives a vi-style warning if there were any changes since the last write. (No change to manual entry.) Fixed some startup, error, and prompt messages to be cleaner and/or more consistent. (No changes to manual entry.) Fixed a bug: If @substr() upper bound (third parameter) is past the end of the string operand, return the substring through the end of the string, rather than returning a null string. Fixed a bug: Reset SIGINT to default after forking before calling shell escape program and before starting pipeline (for commands which support this). Didn't reset SIGINT before calling crypt and external functions because in both cases it should be irrelevant. (No change to manual entry.) CHANGES BETWEEN 6.1 and 6.2: Chris Cole- Compatibility with Lotus 1-2-3 a) @hlookup(expr,range,expr) b) @vlookup(expr,range,expr) c) @round(expr,expr) d) @if(expr,expr,expr) e) @abs(expr) sc-7.16_1.1.2/CHANGES-git000066400000000000000000000043471326046243500143640ustar00rootroot000000000000002017-09-08 * Changes to make sc build on NetBSD 1.5 (Issue #3 reported by DNied@GitHub) 2016-04-14 * Option -M (process mouse events) 2016-04-13 * When rows or columns are deleted (e.g. with "dr" or "dc"), enclosed color ranges had not been deleted. An attempt had been made to fix this. * Bugfix: Automatic color range removal in fix_crange() led to a invalid pointer access crash. * Bugfix: "set cslop/!cslop" had no visual effect an did set the internal cslope value to the wrong (inverted) logical level. * Left mouse button support for edit/input line * Bugfix: vi commands f,t,F,T: Buffer out of bounds access fixed 2016-04-06 * Selecting cells with the left mouse button added. * Bugfix: Scrolling upwards with ^Y did not check if top row is already zero and decremented further. This led to unpredictable effects. 2016-04-05 * On some terminals ^Y did suspend sc instead of scrolling the screen down. This "delayed suspend" is now disabled (like in vi) to make ^Y's scroll down function work on any terminal. * Vertical scrolling using the mouse wheel possible after .scrc command "set mouse" 2016-04-01 If the history had been saved and which filename was used for this had been configured in the Makefile. It is now possible to use the command "histfile" in .scrc to set this filename or to disable saving the history. 2016-03-24 * Saving an existing database into a backup file before overwriting had been controlled by the Makefile definition DOBACKUPS. This is removed now and replaced by the options backup/!backup which can be set in .scrc * By definition command 'W' writes the database into a file as it appears on the screen. This did not work for double quotes (") or escaped double quotes (\"). Here the required escape characters itself (\) had been displayed. But sc does remove 1 or 2 '\' before a '"' when drawing on the screen. sc-7.16_1.1.2/Makefile.in000066400000000000000000000353721326046243500146570ustar00rootroot00000000000000# Makefile $Revision: 7.16 $ # # 1) Select the proper EXDIR (path), MANDIR, MANEXT, LIBDIR, SIGVOID, # REGEX, DFLT_PAGER, and FMOD. Most of the other things aren't # normally changed (see the comments with each) # 2) Select the proper machine/compiler/OS section of code # for MS-DOS look for the pattern 'MS-DOS' # 3) make install # 4) If you have the command 'file' that uses /etc/magic add the line: # 38 string Spreadsheet sc file # Specify the name of the program. # All documentation and installation keys on this value. # name=sc NAME=SC # The base directory where everything should be installed. If you're # packaging this with an O/S, for example, you'll probably want to change # this to /usr. Otherwise, /usr/local is probably more appropriate, unless # you're replacing the vendor-supplied version. prefix=/usr/local # This is where the install step puts it. EXDIR=${prefix}/bin # This is where the man page goes. MANDIR=${prefix}/share/man/man1 MANEXT=1 MANMODE=644 # This is where the library file (tutorial) goes. #LIBDIR=/usr/local/share/$(name) # reno LIBDIR=${prefix}/share/$(name) LIBRARY=-DLIBDIR=\"${LIBDIR}\" # Set SIMPLE for lex.c if you don't want arrow keys or lex.c blows up #SIMPLE=-DSIMPLE SIMPLE= # Set BROKENCURSES if your curses has the nl/nonl bug # if it does and you don't set BROKENCURSES, the display will # be staggered across the screen. Also try IDLOKBAD below. #BROKENCURSES=-DBROKENCURSES BROKENCURSES= # Set USELOCALE to enable country-dependent display of decimal points, # local character recognition in words, and local @date() format. #USELOCALE= USELOCALE=-DUSELOCALE # Set SIGVOID if signal routines are type void. # use: SIGVOID=-DSIGVOID for: # System 5.3, SunOS 4.X, VMS, BSD4.4 (reno), and ANSI C Compliant systems # use: SIGVOID= for: # BSD systems (excluding reno, BSD4.4), and the UNIXPC 'cc' #SIGVOID= SIGVOID=-DSIGVOID # Set IEEE_MATH if you need setsticky() calls in your signal handlers # #IEEE_MATH=-DIEEE_MATH IEEE_MATH= # The -ffloat-store compiler option is necessary for compiling interp.c to # prevent spurious "Still changing after x iterations" errors, intermittent # problems with the @round function, comparisons failing when they shouldn't, # and potentially other similar problems due to FPU registers having greater # precision than doubles in memory. This is known to be necessary for GCC # on x86 processors/FPUs, and probably others. #FLOAT_STORE=-ffloat-store # Set RINT=-DRINT if you do not have rint() in math.h # Set RINT= on/with (they have rint): # SunOS 4.0.3c compiler # BSD4.4 (reno) #RINT=-DRINT RINT= # If your system supports POSIX.2 regular expressions, REGEX should be # set to -DREGCOMP. Otherwise, set REGEX to -DREGCMP if you have the # regcmp/regex regular expression routines (most System V based systems # do) or to -DRE_COMP if you have the re_comp/re_exec regular expression # routines (most BSD based systems do). If your system has no support for # regular expressions, leave REGEX unset. #REGEX= #REGEX=-DREGCMP #REGEX=-DRE_COMP REGEX=-DREGCOMP # This is the name of a pager like "more". # "pg" may be appropriate for SYSV. #DFLT_PAGER=-DDFLT_PAGER=\"more\" # generic && reno DFLT_PAGER=-DDFLT_PAGER=\"less\" # this is the name to save back ups in SAVE=-DSAVENAME=\"$(NAME).SAVE\" # path to crypt, do not define if you don't have crypt # most systems #CRYPT=-DCRYPT_PATH=\"/bin/crypt\" # BSD # CRYPT=-DCRYPT_PATH=\"/usr/bin/crypt\" # other people? #CRYPT=-DCRYPT_PATH=\"/usr/local/bin/crypt\" # If you get errors about fmod being undefined when you try to # compile, then define NO_FMOD (most likely BSD4.3 and Mt Xinu). #FMOD=-DNO_FMOD FMOD= # If your system doesn't have notimeout() in curses define NONOTIMEOUT NO_NOTIMEOUT=-DNONOTIMEOUT #NO_NOTIMEOUT= # flags for lint LINTFLAGS=-abchxv # Format of quick reference guide generated by $(name)qref # Leave undefined for normal text output. #QREF_FMT= QREF_FMT=-DTROFF # *** SPECIAL NOTES *** # For ULTRIX: define the BSD4.2 section and SIGVOID above # tdw@cl.cam.ac.uk tested on Ultrix 3.1C-0 # HP-UX 7.0: Do NOT use -O # (known broken, try sc's boolean operators if you wish) # # **** SYSV curses bugs... **** # Try setting IDLOKBAD to fix (with an empty spreadsheet): # a) Redrawing the bottom half of the screen when you # move between row 9 <-> 10 # b) the highlighted row labels being trash when you # move between row 9 <-> 10 # c) On an xterm on Esix Rev. D+ from eating lines # -goto (or move) a few lines (or more) past the bottom # of the screen, goto (or move) to the top line on the # screen, move upward and the current line is deleted, the # others move up even when they should not, check by # noticing the rows become 2, 3, 40, 41, 42... (etc). # Known systems/terminfos w/ curses problems: # {Esix Rev. D+, AT&T SysV3.2.1}:at386-m,xterm, HP-UX7.0:(not sure) #IDLOKISBAD=-DIDLOKBAD IDLOKISBAD= # If you don't have idlok() in your curses define NOIDLOK #NO_IDLOK=-DNOIDLOK NO_IDLOK= # If moving right off the screen causes the screen to not redraw # properly, define RIGHT_CBUG to get around a curses problem on some # boxes, this forces screen redraws when going right off the screen #RIGHTBUG=-DRIGHT_CBUG RIGHTBUG= # IF you have problems w/ your yacc try bison, Berkeley yacc, or # some other yacc. Some systems don't allow you to # increase the number of terminals (mostly AT&T), SCO's does though. # YACC=yacc # NOTE: Do not use with bison 1.16! Get a new version.... #YACC=bison -y # MS-DOS needs y_tab instead of the normal y.tab #YTAB=y_tab YTAB=y.tab # Command to use to make temporary copies of some source files. #LN=ln -s #LN=cp LN=ln #### SYSTEM DEFINES #### ######################################### # Use this for system AIX V3.1 #CFLAGS= -O -DSYSV2 -DCHTYPE=int -DNLS #LDFLAGS= #LDADD=-lm -lPW -lcurses ######################################### # Use this for system V.2 (includes: HP-UX 7.05, UNIXPC) #CFLAGS= -O -DSYSV2 #LDFLAGS= #LDADD=-lm -lPW -lcurses # with gcc on a Sequent also use: #CC=att gcc #CFLAGS= -DSYSV2 -g -pipe -traditional ######################################### # Use this for system V.3 #CFLAGS= -DSYSV3 -O #LDFLAGS= -s #CFLAGS= -DSYSV3 -g #LDFLAGS= -g #LDADD=-lm -lncurses -lfl # with gcc also use: #CC=gcc #CFLAGS= -DSYSV3 -O -pipe # debugging bison (bison 1.16 is broken) #CFLAGS= -DSYSV3 -g -pipe -traditional #YACC=bison -y -v -t -l ######################################### # Use this for system V.4 #CFLAGS= -DSYSV4 -DSYSV3 -O #LDFLAGS= -s #LDADD=-lm -lcurses -lgen # with gcc also use: #CC=gcc #CFLAGS= -DSYSV3 -O -pipe ######################################### # Microport #CFLAGS= -DSYSV2 -O -DUPORT -Ml #LDFLAGS=-Ml #LDADD=-lm -lcurses -lPW ######################################### # Use this for BSD 4.2 #CFLAGS= -O -DBSD42 #LDFLAGS= #LDADD=-lm -lcurses -ltermcap # with gcc also use: #CC=gcc ######################################### # Use this for Sequent boxes #CC=atscc #CFLAGS=-O -DBSD42 #LDFLAGS= #LDADD=-lm -lcurses -ltermcap #PSCLIB=-lseq # with gcc also use: #CC=gcc #CFLAGS= -O -DBSD42 -pipe ######################################### # Use this for BSD 4.3 #CFLAGS= -O -DBSD43 #-O or -g #LDFLAGS= # -lg might help if -g used in CFLAGS #LDADD=-lm -lcurses -ltermcap ######################################### # Use this for SunOS 4.X if you have the System V package installed. # This will link with the System V curses which is preferable to the # BSD curses (especially helps scrolling on slow (9600bps or less) # serial lines). # # Be sure to define SIGVOID and REGEX (to -DRE_COMP) above. # #CC=/usr/5bin/cc #CFLAGS= -O -DSYSV3 #LDFLAGS= #LDADD=-lm -lcurses ######################################### # Use this for system III (XENIX) #CFLAGS= -O -DSYSIII #LDFLAGS= -i #LDADD=-lm -lcurses -ltermcap ######################################### # Use this for XENIX Version 2.3 #CFLAGS= -O -DSYSIII -DXENIX2_3 #LDFLAGS= -i #LDADD=-lm -lcurses -ltermcap ######################################### # Use this for VENIX #CFLAGS= -DVENIX -DBSD42 -DV7 #LDFLAGS= -z -i #LDADD=-lm -lcurses -ltermcap ######################################### # For SCO Unix V rel. 3.2.0 # -compile using rcc, cc does not cope with gram.c # -edit /usr/include/curses.h, rcc does not understand #error # -link: make CC=cc, rcc's loader gets unresolved __cclass, __range # (rather strange,?) #CC=rcc #CC=cc #CC=gcc -fstrength-reduce #SIGVOID=-DSIGVOID #CFLAGS= -O -DSYSV3 #LDFLAGS= #LDADD=-lm -lcurses -ltinfo -lPW #YACC=yacc -Sm10000 ######################################### # Use this for SCO Unix 3.2.2 and ODT 1.1 #CC=cc #CFLAGS= -O -DSYSV3 #LDFLAGS= #LDADD=-lm -lcurses -lPW -lmalloc -lc_s #YACC=yacc -Sm10000 ######################################### # Use this for MS-DOS, Microsoft C 5.1 and NDMAKE #CC=cl #CFLAGS= -AL -O -Fo$*.o #LDFLAGS=/noi /st:0x4000 #LDADD=lcurses #YACC=bison -y # #.SUFFIXES : .o .c #.c.o: # $(CC) $(CFLAGS) -c $*.c ######################################### # Use this for MS-DOS with DJGPP # REGEX should also be undefined (see above) unless a separate REGEX library # is installed (gdb includes one, but may result in file conflicts with # existing DJGPP files). #CC=gcc # Only use -Wall for testing, since it produces warnings that are of no # real effect on the reliability of the program, but may concern some # people who don't understand them. #CFLAGS=-DSYSV3 -O2 -Wall -UMSDOS #CFLAGS=-DSYSV3 -O2 -UMSDOS #LDADD=-lm -lpdcurses ######################################### _CFLAGS= $(CFLAGS) $(INCDIR_CURSES) $(DEFINES) $(__CDBG) $(__CLDBG) \ #-DTRACE='"/tmp/trace.txt"' _LDFLAGS=$(LDFLAGS) $(__CLDBG) -s LDADD=-lm $(LIBDIR_CURSES) $(LIB_CURSES) # All of the source files SRC=Makefile abbrev.c cmds.c color.c crypt.c eres.sed frame.c format.c gram.y \ help.c interp.c lex.c pipe.c psc.c range.c sc.c sc.h screen.c sort.c \ sres.sed version.c vi.c vmtbl.c xmalloc.c # The objects OBJS=abbrev.o cmds.o color.o crypt.o format.o frame.o gram.o help.o interp.o \ lex.o pipe.o range.o sc.o screen.o sort.o version.o vi.o vmtbl.o \ xmalloc.o compat.o # The documents in the Archive DOCS=CHANGES README sc.doc psc.doc tutorial.sc VMS_NOTES torev build.com all: $(name) p$(name) $(name)qref $(name).1 p$(name).1 $(name)qref.1 \ x$(name) x$(name).1 $(name):$(PAR) $(OBJS) $(CC) $(_CFLAGS) ${_LDFLAGS} ${OBJS} $(RPATH_CURSES) -o $(name) \ ${LDADD} # Alternative link for MS-DOS #$(name): $(OBJS) # link ${_LDFLAGS} ${OBJS},$(name),,${LDADD}; gram.c: gram.y $(YACC) -d gram.y mv $(YTAB).c gram.c $(YTAB).h: gram.y p$(name): psc.c pvmtbl.o pxmalloc.o $(CC) $(_CFLAGS) ${_LDFLAGS} -o p$(name) psc.c \ pvmtbl.o pxmalloc.o ${PSCLIB} # Alternative link for MS-DOS (NB: MSC 5.1 has no getopt.c) #p$(name): psc.o pvmtbl.o pxmalloc.o getopt.o # link ${_LDFLAGS} psc.o pvmtbl.o pxmalloc.o getopt.o,p$(name); qhelp.c: help.c -rm -f qhelp.c ${LN} help.c qhelp.c $(name)qref: qhelp.c sc.h $(CC) $(_CFLAGS) $(_LDFLAGS) -DQREF $(QREF_FMT) \ -DSCNAME=\"$(NAME)\" -o $(name)qref qhelp.c # Alternative link for MS-DOS #$(name)qref: qhelp.c sc.h # $(CC) -AL -O -Foqhelp.o -c -DQREF -DSCNAME=\"$(name)\" qhelp.c # link ${_LDFLAGS} qhelp.o,$(name)qref; pvmtbl.c: vmtbl.c -rm -f pvmtbl.c ${LN} vmtbl.c pvmtbl.c pvmtbl.o: sc.h pvmtbl.c $(CC) $(_CFLAGS) -c -DPSC pvmtbl.c pxmalloc.c: xmalloc.c -rm -f pxmalloc.c ${LN} xmalloc.c pxmalloc.c # Objects abbrev.o: abbrev.c sc.h $(CC) $(_CFLAGS) ${DFLT_PAGER} -c abbrev.c cmds.o: cmds.c sc.h $(CC) $(_CFLAGS) ${CRYPT} -c cmds.c color.o: color.c sc.h crypt.o: crypt.c sc.h $(CC) $(_CFLAGS) ${CRYPT} -c crypt.c format.o: format.c frame.o: frame.c sc.h gram.o: sc.h $(YTAB).h gram.c $(CC) $(_CFLAGS) ${USELOCALE} -c gram.c sed < gram.y > experres.h -f eres.sed sed < gram.y > statres.h -f sres.sed help.o: help.c sc.h $(CC) $(_CFLAGS) ${CRYPT} -c help.c interp.o: interp.c sc.h $(CC) $(_CFLAGS) ${FLOAT_STORE} ${IEEE_MATH} \ ${SIGVOID} ${RINT} ${REGEX} ${FMOD} -c interp.c lex.o: sc.h $(YTAB).h gram.o lex.c $(CC) $(_CFLAGS) ${SIMPLE} ${IEEE_MATH} \ ${LIBRARY} ${SIGVOID} ${NO_NOTIMEOUT} -c lex.c pipe.o: pipe.c sc.h pxmalloc.o: sc.h pxmalloc.c $(CC) $(_CFLAGS) -c -DPSC pxmalloc.c qhelp.o: qhelp.c sc.h $(CC) $(_CFLAGS) ${CRYPT} -c qhelp.c range.o: range.c sc.h sc.o: sc.h sc.c $(CC) $(_CFLAGS) ${DFLT_PAGER} ${SIGVOID} \ ${SAVE} -c sc.c screen.o: sc.h screen.c $(CC) $(_CFLAGS) ${BROKENCURSES} ${IDLOKISBAD} \ ${RIGHTBUG} ${SIGVOID} ${NO_IDLOK} -c screen.c sort.o: sort.c sc.h vi.o: vi.c sc.h $(CC) $(_CFLAGS) ${REGEX} ${HISTORY_FILE} -c vi.c # other stuff clean: rm -f *.o *res.h $(YTAB).h debug core gram.c y.output pxmalloc.c \ pvmtbl.c qhelp.c tags distclean: clean rm -f $(name) p$(name) $(name)qref x$(name) Makefile config.log \ $(name).1 p$(name).1 $(name)qref.1 x$(name).1 compat.h shar: ${SRC} ${DOCS} shar -c -m 64000 -f shar ${DOCS} ${SRC} sshar: ${SRC} shar -c -m 1000000 -f shar ${SRC} lint: sc.h sc.c lex.c gram.c interp.c cmds.c color.c crypt.c frame.c pipe.c \ range.c help.c vi.c version.c xmalloc.c format.c vmtbl.c lint ${LINTFLAGS} $(_CFLAGS) ${SIMPLE} sc.c \ lex.c gram.c interp.c cmds.c color.c crypt.c frame.c pipe.c \ range.c help.c vi.c version.c xmalloc.c format.c vmtbl.c \ $(LDADD) make lintqref lintqref: help.c lint ${LINTFLAGS} $(_CFLAGS) ${SIMPLE} -DQREF \ help.c lintpsc: psc.c vmtbl.c lint ${LINTFLAGS} $(_CFLAGS) ${SIMPLE} -DPSC \ psc.c vmtbl.c .SUFFIXES: .doc .1 .c.o: $(CC) $(_CFLAGS) -c $< .doc.1: name=$(name) NAME=$(NAME) LIBDIR=$(LIBDIR) sh torev $< > $@ install: $(EXDIR)/$(name) $(EXDIR)/$(name)qref $(EXDIR)/p$(name) \ $(LIBDIR)/tutorial.$(name) $(MANDIR)/$(name).$(MANEXT) \ $(MANDIR)/p$(name).$(MANEXT) $(MANDIR)/$(name)qref.$(MANEXT) \ $(EXDIR)/x$(name) $(MANDIR)/x$(name).$(MANEXT) $(EXDIR)/$(name): $(name) $(EXDIR) install $(name) $(EXDIR)/ $(EXDIR)/$(name)qref: $(name)qref $(EXDIR) install $(name)qref $(EXDIR)/ $(EXDIR)/p$(name): p$(name) $(EXDIR) install p$(name) $(EXDIR)/ $(EXDIR)/x$(name): p$(name) $(EXDIR) install x$(name) $(EXDIR)/ x$(name): xsc.sh name=$(name) sh torev < xsc.sh > x$(name) $(LIBDIR)/tutorial.$(name): tutorial.sc $(LIBDIR) $(LIBDIR)/plugins install -m $(MANMODE) tutorial.sc $(LIBDIR)/tutorial.$(name) $(EXDIR): mkdir -p $(EXDIR) $(LIBDIR): mkdir -p $(LIBDIR) $(LIBDIR)/plugins: mkdir -p $(LIBDIR)/plugins $(MANDIR): mkdir -p $(MANDIR) $(MANDIR)/$(name).$(MANEXT): $(MANDIR) $(name).1 install -m $(MANMODE) $(name).1 $@ $(MANDIR)/p$(name).$(MANEXT): $(MANDIR) p$(name).1 install -m $(MANMODE) p$(name).1 $@ $(MANDIR)/x$(name).$(MANEXT): $(MANDIR) x$(name).1 install -m $(MANMODE) x$(name).1 $@ $(MANDIR)/$(name)qref.$(MANEXT): $(MANDIR) $(name)qref.1 install -m $(MANMODE) $(name)qref.1 $@ uninstall: rm -f $(EXDIR)/$(name) rm -f $(EXDIR)/$(name)qref rm -f $(EXDIR)/p$(name) rm -f $(EXDIR)/x$(name) rm -rf $(LIBDIR) rm -f $(MANDIR)/$(name).$(MANEXT) rm -f $(MANDIR)/p$(name).$(MANEXT) rm -f $(MANDIR)/$(name)qref.$(MANEXT) files: @find $(DOCS) $(SRC) -print sc-7.16_1.1.2/README000066400000000000000000000227101326046243500134620ustar00rootroot00000000000000This is a much modified version of the public domain spread sheet sc, originally by James Gosling, and posted a number of years ago by Mark Weiser as vc. The current maintainer is Chuck Martin (nrocinu@myrealbox.com). I originally got involved with sc because I wanted a good text-based spreadsheet program, and nothing I could find seemed to suit my needs. After looking at several different programs, I settled on sc as the program with the most promise, especially since source code was available, which meant I could modify it to add the features I needed. Since it appeared that no one was maintaining it anymore, I decided to take up the cause myself, although I don't want to claim ownership of the program. I would like to thank those who have made their own contributions to making sc a better program. I apologize for taking so long to release this version. I've been wanting to do it for quite some time, but have been holding off until I could find the time to update the documentation properly, and other responsibilities have prevented me from doing that until recently. On the bright side, this version has been in use more extensively, and I believe it to be more stable and bug-free than any previous version. The CHANGES file lists the changes from 6.1 to 7.16. Although I've already added all of the "must have" features I needed, and then some, I'm willing to continue maintaining sc for as long as I'm able, and I'll certainly share any new features and bugfixes I come up with. If anyone else finds any bugs or would like to contribute patches, please send them to me. If they're useful and they work, I'll probably add them. I prefer to keep it simple, though, since that's what drew me to sc instead of some other "full-featured" spreadsheet in the first place. I like the Unix philosophy of making each program do one thing and do it well, and would rather leave all the fancy graphics and other features to other programs that are better suited for them. Many of the items in the TODO list were there before I took over maintaining sc, and although I won't be adding those features myself, I left them in the list in case someone else still wants to add them. I've added undo to the TODO list because I feel it is a much needed feature, but I won't make any promises as to when (or if) I will get to it. If anyone else would like to tackle it, feel free to do so. I've tried to avoid changing key bindings any more than necessary because it could be confusing to people who have been using sc for years if the keybindings suddenly change, but in version 7.13, I finally decided to change a few things for more compatibility with vi, so that it will be easier to move back and forth between the two programs. For example, the ^F and ^B keys now work like J and K (or PageDown/PageUp). Also, the " key is no longer used for entering centered labels, but for named delete buffers instead, as it does in vi. The \ key will take its place for entering centered labels. This will also make it easier to enter a so-called "wheel" for filling a cell with a character or string, since you'll begin such a string by pressing \ twice. Also, most keyboards have the "\" and "|" on the same key, which should make it easy to remember because | is already used for centering an existing string. I'm sure someone will let me know if this causes any problems. :) In version 7.16, I've added a few more changes in sc's key bindings. For one, the range commands now all begin with "r" instead of "/" because I intend to add a new search feature that will be more powerful than the goto command, and I want to use "/" for that feature. Also, "n" is now used to repeat the last search (the last goto, for now, but that will change when the new search feature is implemented). Since that conflicts with the former usage of "n", which was for the note commands, these commands now all begin with "*", and following a link to a note now requires that "*" be pressed twice in succession. I've started an announcement-only mailing list for those who would like to find out when new versions of sc are available. If anyone is interested in being added to this list, please let me know. This will not be an automated list. Everything will be done manually, and all names will be put in a "Bcc:" header, so there will be no danger of being added to someone's spam list. Problems with color: I'm no longer running ncurses 1.9.9g on any of my machines or machines I work with, since it seems to have bugs in the handling of color that I've been unable to find a work-around for, and upgrading to the latest version seems to eliminate them. If anyone else finds a way to work around these bugs, patches are welcome. If you have a problem getting highlighting to work when color is enabled, you may want to check the value of ncv in your terminfo file (this can be checked with the "tput ncv" command at the shell prompt). This value should be an even number in order for standout mode to work. If ncv is odd, try subtracting one and recompiling. See the man pages for tic and infocmp for how to do this. The linux terminfo file in some older versions of ncurses is known to have this problem, for example. I've also found the problem in some versions of the terminfo files for xterm and screen. Compiling the program: Before you compile, make sure to check the Makefile and uncomment the lines that pertain to your system and comment out the lines for Linux. If you run Linux, this step won't be necessary, since that's the default (since that's what I run). I haven't tried compiling or running sc on anything else, but I've used it on both Slackware 4.0 (with ncurses upgraded to version 5.2) and Slackware 7.1, so I hope I haven't broken anything for anyone else. If you get it to compile and run on something else, please let me know. If it doesn't work and you can fix it, please send me a patch. A couple of notes from the previous maintainer: 1) If you have problems with lex.c, and don't care about arrow keys, define SIMPLE (-DSIMPLE in the makefile). SIMPLE causes the arrow keys to not be used. 2) If you have problems with your yacc saying: too many terminals ...127... find a different yacc: bison, Berkeley yacc, Pd yacc, etc. AT&T's Sys V yacc has small, fixed sized tables. SCO will allow dynamic yacc tables when given the correct flags. After you get it built, if you aren't familiar with sc, you might want to try "sc tutorial.sc" for a simple introduction to the basic commands. Most of the key bindings are patterned after the vi text editor, so if you're familiar with that program or its variants, you shouldn't have any problems learning sc. The tutorial is a bit out-of-date, and doesn't include any of the new features that have been added since I began maintaining sc. If anyone would like to rectify this situation, feel free. The same goes for the built-in help system (available by pressing `?'), although the feature list has grown quite a bit, and that may not be easy to do without some major changes to the way the help system works. If you've used sc before, you may want to check out the CHANGES file to see what's changed, and read the man page for more details. To print a quick reference card, type the command: scqref | [your_printer_commmand] If you have the command 'file' that uses /etc/magic, and it isn't there already, you may want to add the following lines: # sc: file(1) magic for "sc" spreadsheet # 38 string Spreadsheet sc spreadsheet file Psc formats ascii files for use in the spreadsheet. If you don't have getopts, there is a public domain version by Henry Spencer hidden away in the VMS_NOTES file. If you'd like to rename the program to something different, just change "name=sc" and "NAME=SC" to "name=myfavoritename" and "NAME=MYFAVORITENAME" and try "make myfavoritename". (Does anyone need or use this? If so, please let me know. Otherwise, I may remove that capability in a future version, since it seems to me that it reduces confusion if everyone refers to the program by the same name. (I've been told that there is a program called Sunshine Commander that used the same name, but I may still drop this, since the sc Spreadsheet Calculator is much more well known)). Similarly, you can make the documentation with "make myfavoritename.man". "make install" will make and install the code in EXDIR. The installation steps and documentation all key off of the name. The makefile even changes the name in the nroffable man page. If you don't have nroff, you will have to change sc.man yourself. Uninstalling the program is now very easy, if you should want to do this. Just do "make uninstall". Finding the latest version: You should be able to ftp the lastest version of sc from ibiblio.org The directory where you should be able to find the latest version is /pub/Linux/apps/financial/spreadsheet. If you can't find it, e-mail me. I know this is a Linux specific source, but it's the only place I know of. If anyone knows of a more O/S-neutral location, please let me know. Guarantee: Since some people are wary of using a program that has no guarantee, I've decided to provide the following guarantee: It is a well-known fact that any non-trivial program has bugs. If you haven't found them, you just haven't stumbled upon the proper combinations of actions that will cause the bugs to manifest them- selves. Since sc stands for "Spreadsheet Calculator", and since a spreadsheet calculator is by definition a non-trivial program, sc is guaranteed to have bugs. Chuck Martin nrocinu@myrealbox.com sc-7.16_1.1.2/README.md000066400000000000000000000067741326046243500140750ustar00rootroot00000000000000### sc Spreadsheet Calculator `sc` is a free curses-based spreadsheet program that uses key bindings similar to vi and less. For more information on `sc` please see the [`README`](https://github.com/n-t-roff/sc/blob/master/README) file. This is a fork of the latest source code version 7.16 from September 2002 which can be found at [http://www.ibiblio.org/pub/Linux/apps/financial/spreadsheet/sc-7.16.tar.gz](http://www.ibiblio.org/pub/Linux/apps/financial/spreadsheet/sc-7.16.tar.gz). The following [changes](https://github.com/n-t-roff/sc/blob/master/CHANGES-git) had been made: * Many bugs had been found, all of them are fixed now. (For reporting bugs please use the [issue list](https://github.com/n-t-roff/sc/issues).) * All `gcc` and most `clang` `-Wall` and `-Werror` compiler warnings fixed. * Made `UBSAN` and `ASAN` clean. * All `sprintf`, `strcpy`, and `strcat` replaced with `snprintf`, `strlcpy`, and `strlcat`. * `./configure` script added and tested on BSD, Linux, and Solaris. Functional extensions: * By default `sc` made a backup of the database file before overwriting it. This could only be changed at compile time. There are now options `backup` and `!backup` to control this. * Configured at compile time, `sc` always did update the history file `~/.sc_history`. It is now possible to change this filename with the `.scrc` command `histfile` or save no history at all with an empty command argument. * Command `set mouse` (e.g. in `.scrc`) enables * Selecting cells with the left mouse button * Vertical scrolling with the mouse wheel * Selecting the edit line position with the left mouse button (In this case the shift key needs to be pressed for ‘cut and paste’. All `sc` functions work well with NetBSD and Solaris curses but using the mouse requires ncurses. Using the mouse _wheel_ additionally requires ncurses version ≥ 6.) #### Documentation Documentation is provided in short form integrated in `sc` accessible with the `?` key. A complete documentation is available as a [manual page](https://github.com/n-t-roff/sc/blob/master/sc.doc). For better navigation [HTML](http://n-t-roff.github.io/sc.1.html) and [PDF](http://n-t-roff.github.io/sc.1.pdf) versions with table of contents and hyperlinks had been created (using the [-man](http://n-t-roff.github.io/heirloom/doctools/man.7.html) macros of [Heirloom troff](http://n-t-roff.github.io/heirloom/doctools.html)). #### Getting the source The source can be cloned with ```sh git clone https://github.com/n-t-roff/sc.git ``` and updated later with ```sh git pull ``` #### Compiling and Installation Defaults for compiling and installation are set in the [`Makefile.in`](https://github.com/n-t-roff/sc/blob/master/Makefile.in). A system dependent makefile is generated with ```sh ./configure ``` The source is compiled with ```sh make ``` (the `curses` or `ncurses` header files need to be installed, else `make` failes). If `make` failes it may be possible that `./configure` did not work correctly with the installed `/bin/sh`. If `ksh` is installed in such cases using ```sh make distclean ksh configure make ``` can solve the problem. (Alternatively `sh` in `configure`s first line can be changed to `ksh`.) The result is installed with ```sh make install ``` to the base directory `/usr/local`. This path can be changed in the [`Makefile.in`](https://github.com/n-t-roff/sc/blob/master/Makefile.in). All generated files are removed with ```sh make distclean ``` The package can be uninstalled with ```sh make uninstall ``` sc-7.16_1.1.2/SC.MACROS000066400000000000000000000447171326046243500140300ustar00rootroot00000000000000One of the coolest features new to sc 7.3 is the ability to write advanced macros in your favorite language. I haven't had time to document this feature properly (I'm hoping to do a separate man page), but until I can, I decided to write a basic overview of how this feature works, and let people experiment with it. Any comments, questions, or suggestions are welcome. Basically, the way the new macros appear to sc is no different than the old style macros, apart from the additional commands. On the other side, however, they are as different as night and day. The old style macros are basically just text files containing sc commands in the same form that they would be stored in a plain spreadsheet file. There is no difference between reading a macro file and reading a spreadsheet file, and they can both contain the same commands. This is very limiting because there is no way to create a loop or branch to another part of the macro or execute certain commands only under specific conditions. Advanced macros allow you to do all this and more. An advanced macro is nothing more than an executable program that outputs sc commands on stdout and reads information back in on stdin. All decision-making, looping, etc. is done within the macro program, rather than within sc. This means that you have all the power of whatever language you prefer when programming macros. A few additional commands have been added to request specific data from sc. I call these "pipe commands", since they cause sc to pipe data back to the macro program. The pipe symbol (|) that used to precede these commands is no longer necessary, starting with version 7.13. The command is echoed or printed to stdout, and the result is read from stdin in the form of a newline-terminated string. If the result contains multiple values, they will be separated by spaces. Most of these commands take an optional argument specifying a cell or column you are requesting information from. If this argument is missing, the current cell or column will be used. A few more commands have been added to do things that are easily done from the keyboard, but which previously had no specific commands that could be used to do them from a macro file. These include such things as moving around the spreadsheet using h, j, k, l, or the cursor control keys, or forcing recalculation of the spreadsheet using @. Here is a list of the new commands, along with a description of each one: Pipe Commands: -------------- whereami This command tells sc that you would like to know where you currently are in the spreadsheet. The response will be the address of the cur- rent cell followed by the address of the cell in the upper left-hand corner of the screen, separated by a space. If this line is appended to the string "goto ", it can be used to restore the cell cursor to its original position. It can also be parsed to find out which row and column you're in, which can be used for whatever purpose you want. getnum This command tells sc that you would like the number contained in a given cell. Each cell in sc can contain both a numeric portion and a string portion, and one of those, but not both, can be in the form of an expression. This command requests the numeric portion. If the numeric portion is an expression, this will return the calculated value, rather than the expression. The result will be returned in the form of a newline terminated string formated with "%.15g", which is the same format that is used to store a numeric value in a spreadsheet file, unless it's in the form of an expression (remember, sc files are text files). When followed by a cell address (e.g., `getnum b23'), it will return the number from that cell. Specifying a range instead will return the number from each cell in the range, one row per line, with the values separated by tabs (an empty cell will be represented by two consecutive tabs with nothing between). With no arguments, the number from the current cell will be returned. If a cell contains an error, the string "ERROR" or "INVALID" will be returned. fgetnum This command works exactly like getnum, except that the format used to return the value is the same as that used to format it for display on the screen. In other words, if the cell uses a date format, a date will be returned. If the cell is formatted for scientific notation, that's what you'll get back. The only exception is that if the for- matted result happens to be wider than the cell, you won't get a string of *'s back. Instead, you'll get a result that is wider than the cell it's contained in. Like getnum, you can use this by itself to get the number from the current cell, or with a cell address or range as an argument. If a cell contains an error, the string "ERROR" or "INVALID" will be returned. getstring Like getnum and fgetnum above, this command requests data from sc. However, what is requested (and returned) is the string portion of the cell. If the string portion of the cell is an expression, it will be evaluated, and the result of that evaluation will be returned. getstring may be used to get the data from the current cell or from a specified cell or range, just like getnum and fgetnum above. getexp If either the numeric portion or the string portion of a cell is in the form of an expression, this will return that expression. getformat This command will return a string containing the three numeric values that specify the format of the given column, or the current column if none is given. This is the format specified by the f (format) command. getfmt This command will return the format string for the specified cell (or cells, if a range is specified), or for the current cell if no arguments are given. This is the format specified by the F (fmt) command. It may be either a standard numeric format or a date format. getframe This command will return the outer and inner ranges, respectively, of the framed range containing either the specified cell, if given, or the current cell, otherwise, separated by a space. If the cell is not inside a framed range, an empty string will be returned (in other words, just a lone newline). getrange This command takes a string argument and checks to see if a named range exists with that name. If it does, the actual range is returned. Otherwise, an empty string is returned. status This command will return a set of flags to show information about the current state of the program or the file. Currently, there are three flags implemented: m If the string returned contains an `m', the file currently in memory has been modified. If no `m' is present, the file has not been modified. i If the string returned contains an `i', stdin is currently connected to a terminal. Otherwise, stdin has been redirected to a file or pipe. o If the string returned contains an `o', stdout is currently connected to a terminal. Otherwise, stdout has been re- directed to a file or pipe. query This command can be used to obtain information from the user. An optional string argument will be displayed on the second line of the display to ask the user a question or present an informational message. For example, 'query "Please enter today's sales."' will display the message on the second line, and wait for the user to enter the appropriate information on the top line. If a second string argument is present, it will be used as a default response which the user can accept as is, or edit. The user may switch from insert mode to edit mode, navigate mode, etc., and may use any of the vi-style editing commands or operations that are available during input of regular sc commands, including the use of the command line history. getkey This command can be used to get a single key from the user. For special keys, such as Insert, Delete, function keys, cursor keys, etc., a NULL character will be returned, followed by a newline- terminated string naming the key that was pressed. This name is the same as that found in curses.h with the "KEY_" prefix and any embedded parentheses removed. For example, the cursor right key is "RIGHT", the Insert key is "IC", and the F4 function key is "F4". error Displays a specified string on the second line of the display. The string argument is required, but may be empty (""). This command is intended for displaying error messages from a macro, but may be used to display other informative messages as well. eval This can be used to send an expression directly to sc for evaluation without entering it into a cell. An optional second parameter can be used to specify formatting information. For example, the following line could be used in a shell script: echo eval a49-c53 \"0.00\" You can then read the result in from standard input. If you want to know the number of the current column without having to convert the column name yourself, you can let sc do it for you by sending the command "eval @mycol" and reading the answer back from sc. You can also use this in non-macro shell scripts. For example, the following line could be used to calculate the area of a circle to four decimal places: AREA=`echo eval @pi*$RADIUS^2 \"0.0000\" | sc -q` seval This works like eval, except that it evaluates string expressions instead of numeric expressions. For example, the following line could be used in a shell script to convert a column number to its name (e.g., 5 would be converted to F): echo "seval @coltoa($COLNUM)" The quotes are necessary in this case to prevent the shell from using the parentheses for its own purposes. Other Commands: --------------- up down left right These do just what you would expect. They move the cell cursor in the specified direction. You can also use an optional numeric argu- ment to move the specified number of cells in the given direction. For example, `down 7' will take you to the cell seven rows below the current cell. endup enddown endleft endright These also do what you would expect. For example, if you're in the middle of a long column of data, and you would like to jump to the bottom of the column, but you don't know where the column ends, using the enddown command will take you there. This works exactly like the END key (or ^E) followed by a cursor movement key. insertrow insertcol openrow opencol deleterow deletecol yankrow yankcol These commands insert, delete, or yank rows or columns, just as if the user had pressed `i', `o', `d', or `y'. If you want to insert, delete, or yank more than one row or column, follow the command with `*' and a number; e.g. `insertrow * 5' will insert five rows before the current row. pull pullmerge pullrows pullcols pullxchg pulltp pullfmt pullcopy These commands pull cells (or parts of cells in the case of the pullfmt command) from the delete buffer into the current location in the spreadsheet. They perform the same actions as the `pp' (pull), `pm' (pullmerge), `pr' (pullrows), `pc' (pullcols), `px' (pullxchg), `pt' (pulltp), `pf' (pullfmt), and `pC' (pullcopy) user commands. leftjustify rightjustify center These commands are used to justify or center the string(s) in a cell or range of cells. With no argument, all strings in the currently highlighted range, if one is highlighted, or the current cell, if not, will be justified/centered. Otherwise, a cell or range may be given as a single argument, which will define which strings are to be justified or centered. select This command takes a single string argument, whose first character is used to select a named buffer to be used for the next insertion, deletion, yank, or pull. It is the same as the interactive `"' command. recalc This works like the @ command. It forces recalculation of the spreadsheet. Note that automatic recalculation is turned off tem- porarily while executing a macro (for speed), so you will need to use this command if you want to present current data to the user before the macro is complete. You will also need to use the redraw command to write the recalculated data to the screen. Since recal- culation is turned back on after the macro is complete, this command will not be necessary at the end of a macro. redraw This command works like ^L, and redraws the screen. You may have to use the recalc command first if you want the data to be current. Note that screen updates are not performed during macros (for speed), so you'll have to use this command if the data have changed in any way, and you want the user to see those changes. If your macro writes directly to the screen at any time to display messages or otherwise interact with the user, you will need to use this command to restore the spreadsheet to the screen when the macro ends. quit This command causes sc to immediately exit. It does not prompt the user for confirmation or ask if the data should be saved if it has been modified. You will need to do that from the macro, if necessary. Use this command with caution. Now that you understand the new commands (and hopefully the old ones, too, although you'll have to figure those out on your own; hint: watch the top of the screen as you enter data and execute other commands, and look at the contents of a few spreadsheet files), a few hints are in order. First, it is perfectly okay to write directly to the screen, although, since stdin and stdout have been redirected, you'll have to use another method of doing this. From a shell script, for example, you can redirect stdin and/or stdout to /dev/tty on a command by command basis. Remember that even a command like clear sends special codes to do its job, so you'll need to use redirection to make it work. For example, to clear the screen, use `clear >/dev/tty'. If you have the dialog or cdialog program, you can use these to interact with the user. Just redirect stdin and stdout with `<>/dev/tty' for each dialog command (or what might be better is to open a file descriptor once with something like `exec 3<> /dev/tty' and use that for communicating with the user). You can also use the tput, echo, and read commands with redirection to read and write the top two lines of the screen for inter- action with the user. For example, `(tput cup 0 0; tput el) >/dev/tty' will position the cursor on the top line and clear the line. You can then use the echo and read commands to ask the user a question and read the answer. Just remember to redraw the screen after using any commands that write directly to the screen (now that the query command has been implemented, you probably won't be doing it this way, but the capability is there, anyway). Although the examples above are for shell scripts, similar methods can be used from any language. I'll leave the exact implementation for various other languages as an exercise for the reader. Testing macros outside of sc in most cases is very easy, since they read and write stdin and stdout. All you have to do is run the program and feed it information from the keyboard that it would otherwise get from sc. Once you've written and tested your macro, you can try it from within sc. Make sure the file is executable, and then use the R command to run it. Make sure you precede the name with a `|'. You can use D to define the macro directory, although this no longer needs to actually be a directory. It can be the name of your macro file, including the path, preceded by a `|' so that it will be executed as a program. If you include a trailing space, you can then add command line arguments or options when running it with R. This allows you to include several macros in the same file, and use the command line to determine which macro to run. Alternatively, you could use the whereami command to determine where the user is at in the spreadsheet, and run a different macro depending on which region of the spreadsheet the macro is being run from. Use your imagination. Since the macro is a program, you can import data from any source you want. You could get information from the Internet, read it from custom hardware connected to the serial or parallel port (or your own custom interface), or pull it from a file created by another program. You could cause your macro to react to the phase of the moon or whether the date is even or odd, if you want (although your users might not like that very well). Basically, you can do anything you want. I'd be interested in hearing of some creative uses of macros. Here's some additional information about the pipe commands. Although it is no longer necessary to include the pipe symbol at the beginning of the "pipe" commands, you may still append it to any of these commands, follosed by file descriptor as before (e.g. `whereami | fd', where fd is a file descriptor). I've used this for testing by using a file descriptor of 1, which will write the information to the screen, or a file descriptor of 2, which will write the information to stderr, which I then redirect to another virtual terminal or a file. This may also be used in a pipe- line to pass data from sc on to the next command (or to a file, through redirection). For example, if you create an sc spreadsheet on the fly and pipe it to sc, you could add the following lines to the end of the spreadsheet: getfnum D49:G73 | 1 quit This will cause the formatted numeric data from the range D49:G73 to be piped to the next command, tab-delimited, one row per line. Also, if you're using simple macros (the old-style "list of sc commands in a text file"), no pipes are created, and the default file descriptor for the pipe commands is 1 (or stdout). Make sure you include the quit command, or sc will become interactive after receiving all of the data from the pipeline. If anyone finds another use for this way of using the pipe commands, please let me know, and I'll add it to the documentation in the next version. I'm hoping to eventually include better documentation for macros, preferably in a man page. In the meantime, enjoy the new capability, and send me any comments or suggestions for the next release. Chuck nrocinu@myrealbox.com sc-7.16_1.1.2/TODO000066400000000000000000000024031326046243500132670ustar00rootroot00000000000000 todo: 1) autobackup of things typed in. idea: each cell change output to a stdio open file in the save format, fflush() every so often... (diffs w/r to the original file) 2) make sure ISVALID should be used in place of checkbounds in interp.c 3) hide range 4) block moving into range 5) chain cells w/ equations into a linked list or dependency tree -have a top level eval, eval and UPDATE all lower nodes 6) an option to go into a ^R like ++data entry fields (highlight entry cells).... ++only allow entry in these cells.... 7) don't redraw the whole screen all the time (only cells that change, (in addition to what is in 'fixed #9')) (Note: there was no #9 when I took this over, and I've renumbered things since then, so I'm not sure what this is referring to - CRM) 8) add uemacs keybinding stuff 9) add uemacs macro language 10) add uemacs command completion 11) add undo 12) rewrite update() function in screen.c from scratch in a more sane manner (the current one is buggy and a headache to debug) 13) add a command line option (possibly -g) to use sc as a sort of grep for formatted or unformatted data in a file or list of files which will output the whole row in which a given number or string is found, formatted as with the write command. sc-7.16_1.1.2/VMS_NOTES000066400000000000000000000103651326046243500141450ustar00rootroot00000000000000From: ihnp4!gargoyle!oddjob!noao!arizona!naucse!jdc (John Campbell) To: arizona!noao!oddjob!gargoyle!ihnp4!nsc!nscpdc!rgb Subject: VMS SC VMS USERS: Bob Bond has been generous enough to give me free rein in adding what I think is needed to make SC run on VMS. Any problems with VMS should be directed to me--they are not Bob's fault. The VMS SC is "SIMPLE" for the most part, except that the arrow keys (instead of hjkl) will move you around the cells. The VMS version of SC will not interact with the Bourne shell (obviously), which means that CRYPT and EXTERNAL FUNCTIONS will not be available. If you have a 'C' compiler and GNU Bison then you should be able to get SC running on VMS by following the instructions below. Step 1: Get all the files I've heard of a few sites that can unpack unix shar files directly on VMS. Most people, however, will need access to a unix machine to get the original distribution unpacked. At this time you should also build experres.h and statres.h and perhaps run the man pages off if you need to port the documentation. To build the two "missing" hearder files: sed experres.h -f eres.sed sed statres.h -f sres.sed Step 2: Cut out BUILD.COM and GETOPT.C At the end of this file are two other pieces: BUILD.COM and GETOPT.C. After you've moved everything to VMS, cut BUILD.COM and GETOPT.C out of here and put them in the same directory as the rest of the SC distribution. Step 3: Build it Theoretically all you now need to do is @BUILD and SC (as well as PSC) will be running on VMS. If you have problems feel free to contact me at ...!arizona!naucse!jdc (or even call at 602-523-6259). ---------------------cut here for BUILD.COM-------------------------- $! VMS command file to build SC and PSC (requires bison) $! SC: $ bison -d gram.y $ ren gram_tab.c gram.c $ cc /define=("SIMPLE","SIGVOID") sc.c $ cc /define=("SIMPLE","SIGVOID") gram.c $ cc /define=("SIMPLE","SIGVOID") lex.c $ cc /define=("SIMPLE","SIGVOID") interp $ cc /define=("SIMPLE","SIGVOID") cmds $ cc /define=("SIMPLE","SIGVOID") xmalloc $ cc /define=("SIMPLE","SIGVOID") range $ cc /define=("SIMPLE","SIGVOID") help $ link sc.obj,lex.obj,gram.obj,interp.obj,cmds.obj,xmalloc.obj,- range.obj,help.obj,sys$library:vaxcrtl.olb/lib $ ! $ ! Create VMS foreign command symbol to test SC $ ! $ sc == "$" + f$logical("SYS$DISK") + f$directory() + "SC.EXE" $! $! Now PSC $! $ cc psc.c $ cc getopt.c $ link psc,getopt,sys$library:vaxcrtl.olb/lib $ ! $ ! Create VMS foreign command symbol to test PSC (Note that $ ! PSC reads SYS$INPUT and writes to SYS$OUTPUT, so use $ ! DEFINE/USER to redirect.) $ ! $ psc == "$" + f$logical("SYS$DISK") + f$directory() + "PSC.EXE" ---------------------cut here for GETOPT.C------------------------ /* * getopt - get option letter from argv * This software is in the public domain * Originally written by Henry Spencer at the U. of Toronto */ #include char *optarg; /* Global argument pointer. */ int optind = 0; /* Global argv index. */ static char *scan = NULL; /* Private scan pointer. */ /* extern char *index(); obsolete, used strchr (JDC). */ int getopt(argc, argv, optstring) int argc; char *argv[]; char *optstring; { register char c; register char *place; optarg = NULL; if (scan == NULL || *scan == '\0') { if (optind == 0) optind++; if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); if (strcmp(argv[optind], "--")==0) { optind++; return(EOF); } scan = argv[optind]+1; optind++; } c = *scan++; place = strchr(optstring, c); if (place == NULL || c == ':') { fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); return('?'); } place++; if (*place == ':') { if (*scan != '\0') { optarg = scan; scan = NULL; } else { optarg = argv[optind]; optind++; } } return(c); } sc-7.16_1.1.2/abbrev.c000066400000000000000000000072021326046243500142060ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Abbreviations * * Chuck Martin * Originally created: November, 2001 * * $Revision: 7.16 $ */ #include #include #include #include #include #include #include #include "compat.h" #include "sc.h" static int are_abbrevs(void); static struct abbrev *abbr_base; void add_abbr(char *string) { struct abbrev *a; register char *p; struct abbrev *prev = NULL; char *expansion; if (!string || *string == '\0') { if (!are_abbrevs()) { error("No abbreviations defined"); return; } else { FILE *f; int pid; char px[MAXCMD]; char *pager; struct abbrev *a; struct abbrev *nexta; strlcpy(px, "| ", sizeof px); if (!(pager = getenv("PAGER"))) pager = DFLT_PAGER; strlcat(px, pager, sizeof px); f = openfile(px, sizeof px, &pid, NULL); if (!f) { error("Can't open pipe to %s", pager); return; } (void) fprintf(f, "\n%-15s %s\n","Abbreviation","Expanded"); if (!brokenpipe) (void) fprintf(f, "%-15s %s\n", "------------", "--------"); for (a = nexta = abbr_base; nexta; a = nexta, nexta = a->a_next) ; while (a) { (void) fprintf(f, "%-15s %s\n", a->abbr, a->exp); if (brokenpipe) return; a = a->a_prev; } closefile(f, pid, 0); return; } } if ((expansion = strchr(string, ' '))) *expansion++ = '\0'; if (isalpha((int)*string) || isdigit((int)*string) || *string == '_') { for (p = string; *p; p++) if (!(isalpha((int)*p) || isdigit((int)*p) || *p == '_')) { error("Invalid abbreviation: %s", string); scxfree(string); return; } } else { for (p = string; *p; p++) if ((isalpha((int)*p) || isdigit((int)*p) || *p == '_') && *(p+1)) { error("Invalid abbreviation: %s", string); scxfree(string); return; } } if (expansion == NULL) { if ((a = find_abbr(string, strlen(string), &prev))) { error("abbrev \"%s %s\"", a->abbr, a->exp); return; } else { error("abreviation \"%s\" doesn't exist", string); return; } } if (find_abbr(string, strlen(string), &prev)) del_abbr(string); a = scxmalloc(sizeof(struct abbrev)); a->abbr = string; a->exp = expansion; if (prev) { a->a_next = prev->a_next; a->a_prev = prev; prev->a_next = a; if (a->a_next) a->a_next->a_prev = a; } else { a->a_next = abbr_base; a->a_prev = NULL; if (abbr_base) abbr_base->a_prev = a; abbr_base = a; } } void del_abbr(char *abbrev) { struct abbrev *a; struct abbrev *prev; if (!(a = find_abbr(abbrev, strlen(abbrev), &prev))) return; if (a->a_next) a->a_next->a_prev = a->a_prev; if (a->a_prev) a->a_prev->a_next = a->a_next; else abbr_base = a->a_next; scxfree((char *)(a->abbr)); scxfree((char *)a); } struct abbrev * find_abbr(char *abbrev, int len, struct abbrev **prev) { struct abbrev *a; int cmp; int exact = TRUE; if (len < 0) { exact = FALSE; len = -len; } for (a = abbr_base; a; a = a->a_next) { if ((cmp = strncmp(abbrev, a->abbr, len)) > 0) return (NULL); *prev = a; if (cmp == 0) if (!exact || strlen(a->abbr) == (unsigned int)len) return (a); } return NULL; } void write_abbrevs(FILE *f) { register struct abbrev *a; register struct abbrev *nexta; for (a = nexta = abbr_base; nexta; a = nexta, nexta = a->a_next) /* */ ; while (a) { (void) fprintf(f, "abbrev \"%s\" \"%s\"\n", a->abbr, a->exp); a = a->a_prev; } } static int are_abbrevs(void) { return (abbr_base != 0); } sc-7.16_1.1.2/build.com000066400000000000000000000026351326046243500144050ustar00rootroot00000000000000$! VMS command file to build SC and PSC (requires bison) on VMS $! SC: $! $Revision: 7.16 $ $! bison -d gram.y $! ren gram_tab.c gram.c $ cc'p1' /define=("SIMPLE","SIGVOID") sc.c $ cc'p1' /define=("SIMPLE","SIGVOID") gram.c $ cc'p1' /define=("SIMPLE","SIGVOID") lex.c $ cc'p1' /define=("SIMPLE","SIGVOID","RINT") interp $ cc'p1' /define=("SIMPLE","SIGVOID") cmds $ cc'p1' /define=("SIMPLE","SIGVOID") xmalloc $ cc'p1' /define=("SIMPLE","SIGVOID") range $ cc'p1' /define=("SIMPLE","SIGVOID") help $ cc'p1' /define=("SIMPLE","SIGVOID") vmtbl $ cc'p1' /define=("SIMPLE","SIGVOID") screen $ cc'p1' /define=("SIMPLE","SIGVOID") vi $ cc'p1' /define=("SIMPLE","SIGVOID") format $ link'p1' sc.obj,lex.obj,gram.obj,interp.obj,cmds.obj,xmalloc.obj,- range.obj,help.obj,vmtbl.obj,screen.obj,vi.obj,format.obj,- sys$library:vaxccurse.olb/lib,- sys$library:vaxcrtl/shar $ ! $ ! Create VMS foreign command symbol to test SC $ ! $ sc == "$" + f$logical("SYS$DISK") + f$directory() + "SC.EXE" $! $! Now PSC $! !$ cc'p1' psc.c !$ cc'p1' getopt.c $ link'p1' psc,getopt,vmtbl.obj,xmalloc.obj,screen.obj,vi.obj,format.obj,- sys$library:vaxccurse.olb/lib,- sys$library:vaxcrtl/shar $ ! $ ! Create VMS foreign command symbol to test PSC (Note that $ ! PSC reads SYS$INPUT and writes to SYS$OUTPUT, so use $ ! DEFINE/USER to redirect.) $ ! $ psc == "$" + f$logical("SYS$DISK") + f$directory() + "PSC.EXE" sc-7.16_1.1.2/cmds.c000066400000000000000000002554471326046243500137130ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Command routines * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * * $Revision: 7.16 $ */ #include #include #include #include #include #include #include #include #include #include #include #ifndef MSDOS #include #endif #include #include "compat.h" #include "sc.h" void syncref(register struct enode *e); void unspecial(FILE *f, char *str, int delim); static struct ent *deldata1(void); static void deldata2(struct ent *obuf); /* a linked list of free [struct ent]'s, uses .next as the pointer */ extern struct ent *freeents; /* a linked list of free [struct enodes]'s, uses .e.o.left as the pointer */ extern struct enode *freeenodes; #define DEFCOLDELIM ':' extern char *scext; extern char *ascext; extern char *tbl0ext; extern char *tblext; extern char *latexext; extern char *slatexext; extern char *texext; extern int Vopt; extern struct go_save gs; int macrofd; int cslop; struct impexfilt *filt = NULL; /* root of list of impex filters */ /* copy the current row (currow) and place the cursor in the new row */ void duprow(void) { int c1, c2, coltmp = curcol; struct frange *fr; fr = find_frange(currow, curcol); c1 = fr ? fr->or_left->col : 0; c2 = fr ? fr->or_right->col : maxcol; if (currow >= maxrows - 1 || (!fr && maxrow >= maxrows - 1) || (fr && fr->or_right->row >= maxrows - 1)) { if (!growtbl(GROWROW, 0, 0)) return; } modflg++; insertrow(1, 1); for (curcol = c1; curcol <= c2; curcol++) { register struct ent *p = *ATBL(tbl, currow - 1, curcol); if (p) { register struct ent *n; n = lookat(currow, curcol); (void) copyent(n, p, 1, 0, 0, 0, maxrow, maxcol, 0); } } curcol = coltmp; } /* copy the current column (curcol) and place the cursor in the new column */ void dupcol(void) { int rowtmp = currow; if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) { if (!growtbl(GROWCOL, 0, 0)) return; } modflg++; insertcol(1, 1); for (currow = 0; currow <= maxrow; currow++) { register struct ent *p = *ATBL(tbl, currow, curcol - 1); if (p) { register struct ent *n; n = lookat(currow, curcol); copyent(n, p, 0, 1, 0, 0, maxrow, maxcol, 0); } } currow = rowtmp; } /* Insert 'arg' rows. The row(s) will be inserted before currow if delta * is 0; after if it is 1. */ void insertrow(int arg, int delta) { int r, c, i; struct ent **tmprow, **pp; int lim = maxrow - currow + 1; struct frange *fr; if (currow > maxrow) maxrow = currow; maxrow += arg; lim = maxrow - lim + delta; if ((maxrow >= maxrows) && !growtbl(GROWROW, maxrow, 0)) return; if ((fr = find_frange(currow + delta, curcol))) { move_area(currow + arg + delta, fr->or_left->col, currow + delta, fr->or_left->col, fr->or_right->row, fr->or_right->col); if (!delta && fr->ir_left->row == currow + arg) fr->ir_left = lookat(fr->ir_left->row - arg, fr->ir_left->col); if (delta && fr->ir_right->row == currow) fr->ir_right = lookat(fr->ir_right->row + arg, fr->ir_right->col); for (i = 0; i < 37; i++) { /* update all marked cells */ if (savedrow[i] >= currow + delta && savedcol[i] >= fr->or_left->col && savedcol[i] <= fr->or_right->col) savedrow[i] += arg; if (savedstrow[i] >= currow + delta && savedstcol[i] >= fr->or_left->col && savedstcol[i] <= fr->or_right->col) savedstrow[i] += arg; } if (gs.g_row >= currow + delta && gs.g_col >= fr->or_left->col && gs.g_col <= fr->or_right->col) gs.g_row += arg; if (gs.g_lastrow >= currow + delta && gs.g_lastcol >= fr->or_left->col && gs.g_lastcol <= fr->or_right->col) gs.g_lastrow += arg; if (gs.strow >= currow + delta && gs.stcol >= fr->or_left->col && gs.stcol <= fr->or_right->col) gs.strow += arg; for (r = 0; r <= maxrow; r++) { for (c = 0; c <= maxcol; c++) { pp = ATBL(tbl, r, c); if (*pp) { if ((*pp)->nrow >= currow + delta && (*pp)->ncol >= fr->or_left->col && (*pp)->ncol <= fr->or_right->col) ((*pp)->nrow) += arg; if ((*pp)->nlastrow >= currow + delta && (*pp)->nlastcol >= fr->or_left->col && (*pp)->nlastcol <= fr->or_right->col) ((*pp)->nlastrow) += arg; } } } } else { /* * save the last active row+1, shift the rows downward, put the last * row in place of the first */ tmprow = tbl[maxrow]; for (r = maxrow; r > lim; r--) { row_hidden[r] = row_hidden[r-arg]; row_hidden[r-arg] = row_hidden[r-1]; tbl[r] = tbl[r-arg]; tbl[r-arg] = tbl[r-1]; pp = ATBL(tbl, r, 0); for (c = 0; c < maxcols; c++, pp++) if (*pp) (*pp)->row = r; } tbl[r] = tmprow; /* the last row was never used.... */ for (i = 0; i < 37; i++) { /* update all marked cells */ if (savedrow[i] >= currow + delta) savedrow[i] += arg; if (savedstrow[i] >= currow + delta) savedstrow[i] += arg; } if (gs.g_row >= currow + delta) gs.g_row += arg; if (gs.g_lastrow >= currow + delta) gs.g_lastrow += arg; if (gs.strow >= currow + delta) gs.strow += arg; for (r = 0; r <= maxrow; r++) { for (c = 0; c <= maxcol; c++) { pp = ATBL(tbl, r, c); if (*pp) { if ((*pp)->nrow >= currow + delta) ((*pp)->nrow) += arg; if ((*pp)->nlastrow >= currow + delta) ((*pp)->nlastrow) += arg; } } } } fix_ranges(currow + arg * (1 - delta), -1, currow + arg * (1 - delta), -1, delta?0:arg, delta?arg:0); currow += delta; FullUpdate++; modflg++; } /* Insert 'arg' cols. The col(s) will be inserted before curcol if delta * is 0; after if it is 1. */ void insertcol(int arg, int delta) { int r, c; register struct ent **pp; int lim = maxcol - curcol - delta + 1; struct frange *fr; if (curcol + delta > maxcol) maxcol = curcol + delta; maxcol += arg; if ((maxcol >= maxcols) && !growtbl(GROWCOL, 0, maxcol)) return; for (c = maxcol; c >= curcol + delta + arg; c--) { fwidth[c] = fwidth[c-arg]; precision[c] = precision[c-arg]; realfmt[c] = realfmt[c-arg]; col_hidden[c] = col_hidden[c-arg]; } for (c = curcol + delta; c - curcol - delta < arg; c++) { fwidth[c] = DEFWIDTH; precision[c] = DEFPREC; realfmt[c] = DEFREFMT; col_hidden[c] = FALSE; } for (r=0; r <= maxrow; r++) { pp = ATBL(tbl, r, maxcol); for (c = lim; --c >= 0; pp--) if ((pp[0] = pp[-arg])) pp[0]->col += arg; pp = ATBL(tbl, r, curcol + delta); for (c = curcol + delta; c - curcol - delta < arg; c++, pp++) *pp = (struct ent *)0; } /* Update all marked cells. */ for (c=0; c < 37; c++) { if (savedcol[c] >= curcol + delta) savedcol[c] += arg; if (savedstcol[c] >= curcol + delta) savedstcol[c] += arg; } if (gs.g_col >= curcol + delta) gs.g_col += arg; if (gs.g_lastcol >= curcol + delta) gs.g_lastcol += arg; if (gs.stcol >= curcol + delta) gs.stcol += arg; /* Update note links. */ for (r = 0; r <= maxrow; r++) { for (c = 0; c <= maxcol; c++) { pp = ATBL(tbl, r, c); if (*pp) { if ((*pp)->ncol >= curcol + delta) ((*pp)->ncol) += arg; if ((*pp)->nlastcol >= curcol + delta) ((*pp)->nlastcol) += arg; } } } if (!delta && (fr = find_frange(currow, curcol)) && fr->ir_left->col == curcol + arg) fr->ir_left = lookat(fr->ir_left->row, fr->ir_left->col - arg); if (delta && (fr = find_frange(currow, curcol)) && fr->ir_right->col == curcol) fr->ir_right = lookat(fr->ir_right->row, fr->ir_right->col + arg); fix_ranges(-1, curcol + arg * (1 - delta), -1, curcol + arg * (1 - delta), delta?0:arg, delta?arg:0); curcol += delta; FullUpdate++; modflg++; } /* delete 'arg' rows starting at currow (deletes from currow downward) */ void deleterow(register int arg) { int i; int rs = maxrow - currow + 1; struct frange *fr; struct ent *obuf = NULL; char buf[50]; if ((fr = find_frange(currow, curcol))) rs = fr->or_right->row - currow + 1; if (rs - arg < 0) { rs = rs > 0 ? rs : 0; snprintf(buf, sizeof buf, "Can't delete %d row%s %d row%s left", arg, (arg != 1 ? "s," : ","), rs, (rs != 1 ? "s" : "")); error(buf); return; } if (fr) { if (any_locked_cells(currow, fr->or_left->col, currow + arg - 1, fr->or_right->col)) { error("Locked cells encountered. Nothing changed"); } else { FullUpdate++; modflg++; obuf = deldata1(); erase_area(currow, fr->or_left->col, currow + arg - 1, fr->or_right->col, 0); fix_ranges(currow, -1, currow + arg - 1, -1, -1, -1); deldata2(obuf); if (currow + arg > fr->ir_right->row && fr->ir_right->row >= currow) fr->ir_right = lookat(currow - 1, fr->ir_right->col); if (currow + arg > fr->or_right->row) fr->or_right = lookat(currow - 1, fr->or_right->col); else move_area(currow, fr->or_left->col, currow + arg, fr->or_left->col, fr->or_right->row, fr->or_right->col); if (fr->ir_left->row > fr->ir_right->row) add_frange(fr->or_left, fr->or_right, NULL, NULL, 0, 0, 0, 0); /* Update all marked cells. */ for (i = 0; i < 37; i++) { if (savedcol[i] >= fr->or_left->col && savedcol[i] <= fr->or_right->col) { if (savedrow[i] >= currow && savedrow[i] < currow + arg) savedrow[i] = savedcol[i] = -1; if (savedrow[i] >= currow + arg) savedrow[i] -= arg; } if (savedstcol[i] >= fr->or_left->col && savedstcol[i] <= fr->or_right->col) { if (savedstrow[i] >= currow && savedstrow[i] < currow + arg) savedstrow[i] = currow; if (savedstrow[i] >= currow + arg) savedstrow[i] -= arg; } } if (gs.g_col >= fr->or_left->col && gs.g_col <= fr->or_right->col) { if (gs.g_row >= currow && gs.g_row < currow + arg) gs.g_row = currow; if (gs.g_row >= currow + arg) gs.g_row -= arg; } if (gs.g_lastcol >= fr->or_left->col && gs.g_lastcol <= fr->or_right->col) { if (gs.g_lastrow >= currow && gs.g_lastrow < currow + arg) gs.g_lastrow = currow - 1; if (gs.g_lastrow >= currow + arg) gs.g_lastrow -= arg; } if (gs.g_row > gs.g_lastrow) gs.g_row = gs.g_col = -1; if (gs.stcol >= fr->or_left->col && gs.stcol <= fr->or_right->col) { if (gs.strow >= currow && gs.strow < currow + arg) gs.strow = currow; if (gs.strow >= currow + arg) gs.strow -= arg; } } } else { if (any_locked_cells(currow, 0, currow + arg - 1, maxcol)) { error("Locked cells encountered. Nothing changed"); } else { obuf = deldata1(); erase_area(currow, 0, currow + arg - 1, maxcol, 0); fix_ranges(currow, -1, currow + arg - 1, -1, -1, -1); closerow(currow, arg); deldata2(obuf); } } } static struct ent * deldata1(void) { int i; struct ent *obuf = NULL; if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 1]; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1]; delbuf[DELBUFSIZE - 1] = NULL; delbuffmt[DELBUFSIZE - 1] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } sync_refs(); return obuf; } static void deldata2(struct ent *obuf) { int i; struct ent *p; for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) { delbuf[i] = delbuf[i-1]; delbuffmt[i] = delbuffmt[i-1]; } delbuf[DELBUFSIZE - 9] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx]; for (p = delbuf[dbidx]; p; p = p->next) p->flags &= ~MAY_SYNC; } void yankrow(int arg) { int rs = maxrow - currow + 1; int i, qtmp; char buf[50]; struct frange *fr; struct ent *obuf = NULL; if ((fr = find_frange(currow, curcol))) rs = fr->or_right->row - currow + 1; if (rs - arg < 0) { rs = rs > 0 ? rs : 0; snprintf(buf, sizeof buf, "Can't yank %d row%s %d row%s left", arg, (arg != 1 ? "s," : ","), rs, (rs != 1 ? "s" : "")); error(buf); return; } sync_refs(); if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 10]; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 10]; delbuf[DELBUFSIZE - 10] = NULL; delbuffmt[DELBUFSIZE - 10] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } qtmp = qbuf; qbuf = 0; if (fr) { yank_area(currow, fr->or_left->col, currow + arg - 1, fr->or_right->col); } else { yank_area(currow, 0, currow + arg - 1, maxcol); } qbuf = qtmp; for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; delbuf[DELBUFSIZE - 10] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 10] = delbuffmt[dbidx]; } void yankcol(int arg) { int cs = maxcol - curcol + 1; int i, qtmp; char buf[50]; struct ent *obuf = NULL; if (cs - arg < 0) { cs = cs > 0 ? cs : 0; snprintf(buf, sizeof buf, "Can't yank %d column%s %d column%s left", arg, (arg != 1 ? "s," : ","), cs, (cs != 1 ? "s" : "")); error(buf); return; } sync_refs(); if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 10]; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 10]; delbuf[DELBUFSIZE - 10] = NULL; delbuffmt[DELBUFSIZE - 10] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } qtmp = qbuf; qbuf = 0; yank_area(0, curcol, maxrow, curcol + arg - 1); qbuf = qtmp; for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; delbuf[DELBUFSIZE - 10] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 10] = delbuffmt[dbidx]; } /* ignorelock is used when sorting so that locked cells can still be sorted */ void erase_area(int sr, int sc, int er, int ec, int ignorelock) { int r, c; struct ent **pp; if (sr > er) { r = sr; sr = er; er = r; } if (sc > ec) { c = sc; sc = ec; ec = c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); /* Do a lookat() for the upper left and lower right cells of the range * being erased to make sure they are included in the delete buffer so * that pulling cells always works correctly even if the cells at one * or more edges of the range are all empty. */ (void) lookat(sr, sc); (void) lookat(er, ec); delbuffmt[++dbidx] = scxmalloc((4*(ec-sc+1)+(er-sr+1))*sizeof(char)); for (c = sc; c <= ec; c++) { delbuffmt[dbidx][4*(c-sc)] = (char)fwidth[c]; delbuffmt[dbidx][4*(c-sc)+1] = (char)precision[c]; delbuffmt[dbidx][4*(c-sc)+2] = (char)realfmt[c]; delbuffmt[dbidx][4*(c-sc)+3] = (char)col_hidden[c]; } for (r = sr; r <= er; r++) { for (c = sc; c <= ec; c++) { pp = ATBL(tbl, r, c); if (*pp && (!((*pp)->flags&IS_LOCKED) || ignorelock)) { free_ent(*pp, 0); *pp = NULL; } } delbuffmt[dbidx][4*(ec-sc+1)+(r-sr)] = (char)row_hidden[r]; } } void yank_area(int sr, int sc, int er, int ec) { int r, c; if (sr > er) { r = sr; sr = er; er = r; } if (sc > ec) { c = sc; sc = ec; ec = c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); r = currow; currow = sr; c = curcol; curcol = sc; erase_area(sr, sc, er, ec, 0); pullcells('p'); currow = r; curcol = c; } void move_area(int dr, int dc, int sr, int sc, int er, int ec) { struct ent *p; struct ent **pp; int deltar, deltac; int r, c; if (sr > er) { r = sr; sr = er; er = r; } if (sc > ec) { c = sc; sc = ec; ec = c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); r = currow; currow = sr; c = curcol; curcol = sc; /* First we erase the source range, which puts the cells on the delete * buffer stack. */ erase_area(sr, sc, er, ec, 0); currow = r; curcol = c; deltar = dr - sr; deltac = dc - sc; /* Now we erase the destination range, which adds it to the delete buffer * stack, but then we flush it off. We then move the original source * range from the stack to the destination range, adjusting the addresses * as we go, leaving the stack in its original state. */ erase_area(dr, dc, er + deltar, ec + deltac, 0); flush_saved(); for (p = delbuf[dbidx]; p; p = p->next) { pp = ATBL(tbl, p->row + deltar, p->col + deltac); *pp = p; p->row += deltar; p->col += deltac; p->flags &= ~IS_DELETED; } delbuf[dbidx] = NULL; delbuffmt[dbidx--] = NULL; } /* * deletes the expression associated w/ a cell and turns it into a constant * containing whatever was on the screen */ void valueize_area(int sr, int sc, int er, int ec) { int r, c; struct ent *p; if (sr > er) { r = sr; sr = er; er= r; } if (sc > ec) { c = sc; sc = ec; ec= c; } if (sr < 0) sr = 0; if (sc < 0) sc = 0; checkbounds(&er, &ec); for (r = sr; r <= er; r++) { for (c = sc; c <= ec; c++) { p = *ATBL(tbl, r, c); if (p && p->flags&IS_LOCKED) { error(" Cell %s%d is locked", coltoa(c), r); continue; } if (p && p->expr) { efree(p->expr); p->expr = (struct enode *)0; p->flags &= ~IS_STREXPR; } } } } void pullcells(int to_insert) { struct ent *obuf; struct ent *p, *n; struct ent **pp; int deltar, deltac; int minrow, mincol; int mxrow, mxcol; int numrows, numcols; int i; struct frange *fr; if (qbuf && delbuf[qbuf]) { delbuf[++dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; } obuf = delbuf[dbidx]; /* orig. contents of the del. buffer */ if ((qbuf && !delbuf[qbuf]) || dbidx < 0) { error("No data to pull"); qbuf = 0; return; } minrow = maxrows; mincol = maxcols; mxrow = 0; mxcol = 0; for (p = delbuf[dbidx]; p; p = p->next) { if (p->row < minrow) minrow = p->row; if (p->row > mxrow) mxrow = p->row; if (p->col < mincol) mincol = p->col; if (p->col > mxcol) mxcol = p->col; p->flags |= MAY_SYNC; } numrows = mxrow - minrow + 1; numcols = mxcol - mincol + 1; deltar = currow - minrow; deltac = curcol - mincol; if (to_insert == 'C') { minrow = 0; mincol = 0; mxrow = maxrows; mxcol = maxcols; } if (to_insert == 'r') { insertrow(numrows, 0); if ((fr = find_frange(currow, curcol))) deltac = fr->or_left->col - mincol; else { for (i = 0; i < numrows; i++) row_hidden[currow+i] = delbuffmt[dbidx][4*numcols+i]; deltac = 0; } } else if (to_insert == 'c') { insertcol(numcols, 0); for (i = 0; i < numcols; i++) { fwidth[curcol+i] = delbuffmt[dbidx][4*i]; precision[curcol+i] = delbuffmt[dbidx][4*i+1]; realfmt[curcol+i] = delbuffmt[dbidx][4*i+2]; col_hidden[curcol+i] = delbuffmt[dbidx][4*i+3]; } deltar = 0; } else if (to_insert == 'x') { /* Do an exchange. */ struct ent *tmpbuf; char *tmpfmt; /* Save the original contents of the destination range on the * delete buffer stack in preparation for the exchange, then swap * the top two pointers on the stack, so that the original cells * to be pulled are still on top. */ erase_area(minrow + deltar, mincol + deltac, mxrow + deltar, mxcol + deltac, 0); tmpbuf = delbuf[dbidx]; delbuf[dbidx] = delbuf[dbidx - 1]; delbuf[dbidx - 1] = tmpbuf; tmpfmt = delbuffmt[dbidx]; delbuffmt[dbidx] = delbuffmt[dbidx - 1]; delbuffmt[dbidx - 1] = tmpfmt; } else if (to_insert == 'p') { erase_area(minrow + deltar, mincol + deltac, mxrow + deltar, mxcol + deltac, 0); sync_refs(); flush_saved(); } else if (to_insert == 't') { erase_area(minrow + deltar, mincol + deltac, minrow + deltar + mxcol - mincol, mincol + deltac + mxrow - minrow, 0); sync_refs(); flush_saved(); } FullUpdate++; modflg++; /* At this point, we copy the cells from the delete buffer into the * destination range. */ for (p = delbuf[dbidx]; p; p = p->next) { if (to_insert == 't') /* Transpose rows and columns while pulling. */ n = lookat(minrow + deltar + p->col - mincol, mincol + deltac + p->row - minrow); else n = lookat(p->row + deltar, p->col + deltac); copyent(n, p, deltar, deltac, minrow, mincol, mxrow, mxcol, to_insert); } /* Now exchange them so that the original cells from the delete buffer * are in the destination range instead of the copies. When doing a * "pull exchange" ("px" or "pullxchg"), exchange the original contents * of the destination range with the contents of the delete buffer * instead. Don't do this if transposing or merging (including merging * cell formats), or if the expressions in the destination cells have * been adjusted during a copy. */ if (to_insert != 't' && to_insert != 'm' && to_insert != 'f' && to_insert != 'C') { if (to_insert == 'x') { struct ent *tmpbuf = delbuf[dbidx]; char *tmpfmt = delbuffmt[dbidx]; delbuf[dbidx] = delbuf[dbidx - 1]; delbuf[dbidx - 1] = tmpbuf; delbuffmt[dbidx] = delbuffmt[dbidx - 1]; delbuffmt[dbidx - 1] = tmpfmt; } else for (p = delbuf[dbidx++]; p; p = p->next) { pp = ATBL(tbl, p->row + deltar, p->col + deltac); if (*pp && !((*pp)->flags & IS_LOCKED)) { free_ent(*pp, 1); *pp = NULL; } } for (p = delbuf[dbidx - 1]; p; p = p->next) { pp = ATBL(tbl, p->row + deltar, p->col + deltac); *pp = p; p->row += deltar; p->col += deltac; p->flags &= ~IS_DELETED; } delbuf[dbidx - 1] = delbuf[dbidx]; delbuf[dbidx--] = NULL; sync_refs(); /* * Now change the cell addresses in the delete buffer to match * where the original cells came from. */ for (p = delbuf[dbidx]; p; p = p->next) { p->row -= deltar; p->col -= deltac; } } else sync_refs(); /* Now make sure all references to the pulled cells in all named buffers * point to the new set of cells in the delete buffer. */ for (i = 0; i < DELBUFSIZE; i++) if (delbuf[i] == obuf) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } if (qbuf && delbuf[qbuf]) { delbuf[dbidx] = NULL; delbuffmt[dbidx--] = NULL; } qbuf = 0; } void colshow_op(void) { register int i,j; for (i = 0; i < maxcols; i++) if (col_hidden[i]) break; for(j = i; j < maxcols; j++) if (!col_hidden[j]) break; j--; if (i >= maxcols) { error("No hidden columns to show"); } else { snprintf(line, sizeof line, "show %s:", coltoa(i)); linelim = strlen(line); snprintf(line + linelim, sizeof(line) - linelim, "%s", coltoa(j)); linelim = strlen(line); } } void rowshow_op(void) { register int i,j; for (i = 0; i < maxrows; i++) if (row_hidden[i]) break; for(j = i; j < maxrows; j++) if (!row_hidden[j]) { break; } j--; if (i >= maxrows) { error("No hidden rows to show"); } else { snprintf(line, sizeof line, "show %d:%d", i, j); linelim = strlen(line); } } /* * Given a row/column command letter, emit a small menu, then read a qualifier * character for a row/column command and convert it to 'r' (row), 'c' * (column), or 0 (unknown). If ch is 'p', three extra qualifiers, 'm', 'x', * and 't', are allowed. If ch is 'Z', an extra qualifier 'Z' is allowed. */ int get_rcqual(int ch) { int c; error("%sow/column: r: row c: column%s", #ifdef KEY_IC (ch == KEY_IC) ? "Insert r" : #endif (ch == 'i') ? "Insert r" : (ch == 'o') ? "Open r" : (ch == 'a') ? "Append r" : (ch == 'd') ? "Delete r" : (ch == 'y') ? "Yank r" : (ch == 'p') ? "Pull r" : (ch == 'v') ? "Values r" : (ch == 'Z') ? "Zap r" : (ch == 's') ? "Show r" : "R", (ch == 'p') ? " p: paste m: merge x: xchg " : (ch == 'Z') ? " Z: save/exit" : ""); (void) refresh(); switch (c = nmgetch()) { case 'r': return ('r'); case 'c': return ('c'); case 'p': return ((ch == 'p') ? 'p' : 0); case 'm': return ((ch == 'p') ? 'm' : 0); case 'x': return ((ch == 'p') ? 'x' : 0); case 't': return ((ch == 'p') ? 't' : 0); case 'f': return ((ch == 'p') ? 'f' : 0); case 'C': return ((ch == 'p') ? 'C' : 0); case '.': return ((ch == 'p') ? '.' : 0); case 'Z': return ((ch == 'Z') ? 'Z' : 0); case ESC: case ctl('g'): return (ESC); case 'd': if (ch == 'd') { ungetch('x'); return (ESC); } else return (0); case 'y': if (ch == 'y') { yankr(lookat(currow, curcol), lookat(currow, curcol)); return (ESC); } else return (0); case 'v': if (ch == 'v') { valueize_area(currow, curcol, currow, curcol); modflg++; return (ESC); } else return (0); case KEY_UP: case KEY_DOWN: case KEY_PPAGE: case KEY_NPAGE: case 'j': case 'k': case 'J': case 'K': case ctl('f'): case ctl('b'): case ctl('n'): case ctl('p'): if (ch == 'd') snprintf(line, sizeof line, "deleterow [range] "); else if (ch == 'y') snprintf(line, sizeof line, "yankrow [range] "); else if (ch == 'Z') snprintf(line, sizeof line, "hide [range] "); else return (0); edit_mode(); write_line('A'); startshow(); showrange = SHOWROWS; showsr = currow; ungetch(c); return (ESC); case KEY_BACKSPACE: case KEY_LEFT: case KEY_RIGHT: case ' ': case 'h': case 'l': case 'H': case 'L': if (ch == 'd') snprintf(line, sizeof line, "deletecol [range] "); else if (ch == 'y') snprintf(line, sizeof line, "yankcol [range] "); else if (ch == 'Z') snprintf(line, sizeof line, "hide [range] "); else return (0); edit_mode(); write_line('A'); startshow(); showrange = SHOWCOLS; showsc = curcol; ungetch(c); return (ESC); default: return (0); } /*NOTREACHED*/ } /* delete numrow rows, starting with rs */ void closerow(int rs, int numrow) { register struct ent **pp; int r, c, i; struct ent **tmprow; if (rs + numrow - 1 > maxrow) return; r = rs; /* * Rows are dealt with in numrow groups, each group of rows spaced numrow * rows apart. */ for (i = 0; i < numrow; i++) { r = rs + i; /* save the first row of the group and empty it out */ tmprow = tbl[r]; pp = ATBL(tbl, r, 0); for (c = maxcol + 1; --c >= 0; pp++) { if (*pp) { free_ent(*pp, 1); *pp = (struct ent *)0; } } /* move the rows, put the deleted, but now empty, row at the end */ for (; r + numrow < maxrows - 1; r += numrow) { row_hidden[r] = row_hidden[r + numrow]; tbl[r] = tbl[r + numrow]; pp = ATBL(tbl, r, 0); for (c = 0; c < maxcols; c++, pp++) if (*pp) (*pp)->row = r; } tbl[r] = tmprow; } /* Update all marked cells. */ for (i = 0; i < 37; i++) { if (savedrow[i] >= rs && savedrow[i] < rs + numrow) savedrow[i] = savedcol[i] = -1; if (savedrow[i] >= rs + numrow) savedrow[i] -= numrow; if (savedstrow[i] >= rs && savedstrow[i] < rs + numrow) savedstrow[i] = rs; if (savedstrow[i] >= rs + numrow) savedstrow[i] -= numrow; } if (gs.g_row >= rs && gs.g_row < rs + numrow) gs.g_row = rs; if (gs.g_row >= rs + numrow) gs.g_row -= numrow; if (gs.g_lastrow >= rs && gs.g_lastrow < rs + numrow) gs.g_lastrow = rs - 1; if (gs.g_lastrow >= rs + numrow) gs.g_lastrow -= numrow; if (gs.g_row > gs.g_lastrow) gs.g_row = gs.g_col = -1; if (gs.strow >= rs && gs.strow < rs + numrow) gs.strow = rs; if (gs.strow >= rs + numrow) gs.strow -= numrow; maxrow -= numrow; /* Update note links. */ for (r = 0; r <= maxrow; r++) { for (c = 0; c <= maxcol; c++) { pp = ATBL(tbl, r, c); if (*pp) { if ((*pp)->nrow >= rs && (*pp)->nrow < rs + numrow) (*pp)->nrow = rs; if ((*pp)->nrow >= rs + numrow) ((*pp)->nrow) -= numrow; if ((*pp)->nlastrow >= rs && (*pp)->nlastrow < rs + numrow) (*pp)->nlastrow = rs - 1; if ((*pp)->nlastrow >= rs + numrow) ((*pp)->nlastrow) -= numrow; if ((*pp)->nlastrow < (*pp)->nrow) (*pp)->nrow = (*pp)->ncol = -1; } } } FullUpdate++; modflg++; } /* delete group of columns (1 or more) */ void closecol(int arg) { int r, c, i; int cs = maxcol - curcol + 1; struct ent **pp; struct ent *p; struct ent *obuf = NULL; char buf[50]; if (cs - arg < 0) { cs = cs > 0 ? cs : 0; snprintf(buf, sizeof buf, "Can't delete %d column%s %d column%s left", arg, (arg != 1 ? "s," : ","), cs, (cs != 1 ? "s" : "")); error(buf); return; } if (any_locked_cells(0, curcol, maxrow, curcol + arg - 1)) { error("Locked cells encountered. Nothing changed"); return; } if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 1]; delbuf[DELBUFSIZE - 1] = NULL; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1]; delbuffmt[DELBUFSIZE - 1] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } sync_refs(); erase_area(0, curcol, maxrow, curcol + arg - 1, 0); fix_ranges(-1, curcol, -1, curcol + arg - 1, -1, -1); for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) { delbuf[i] = delbuf[i-1]; delbuffmt[i] = delbuffmt[i-1]; } delbuf[DELBUFSIZE - 9] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx]; for (p = delbuf[dbidx]; p; p = p->next) p->flags &= ~MAY_SYNC; /* clear then copy the block left */ cs = maxcols - arg - 1; for (r=0; r<=maxrow; r++) { for (c = curcol; c - curcol < arg; c++) if ((p = *ATBL(tbl, r, c))) free_ent(p, 1); pp = ATBL(tbl, r, curcol); for (c=curcol; c <= cs; c++, pp++) { if (c > cs) *pp = (struct ent *)0; else if ((pp[0] = pp[arg])) pp[0]->col -= arg; } c = arg; for (; --c >= 0; pp++) *pp = (struct ent *)0; } for (i = curcol; i < maxcols - arg - 1; i++) { fwidth[i] = fwidth[i+arg]; precision[i] = precision[i+arg]; realfmt[i] = realfmt[i+arg]; col_hidden[i] = col_hidden[i+arg]; } for (; i < maxcols - 1; i++) { fwidth[i] = DEFWIDTH; precision[i] = DEFPREC; realfmt[i] = DEFREFMT; col_hidden[i] = FALSE; } /* Update all marked cells. */ for (i = 0; i < 37; i++) { if (savedcol[i] >= curcol && savedcol[i] < curcol + arg) savedrow[i] = savedcol[i] = -1; if (savedcol[i] >= curcol + arg) savedcol[i] -= arg; if (savedstcol[i] >= curcol && savedstcol[i] < curcol + arg) savedstcol[i] = curcol; if (savedstcol[i] >= curcol + arg) savedstcol[i] -= arg; } if (gs.g_col >= curcol && gs.g_col < curcol + arg) gs.g_col = curcol; if (gs.g_col >= curcol + arg) gs.g_col -= arg; if (gs.g_lastcol >= curcol && gs.g_lastcol < curcol + arg) gs.g_lastcol = curcol - 1; if (gs.g_lastcol >= curcol + arg) gs.g_lastcol -= arg; if (gs.g_col > gs.g_lastcol) gs.g_row = gs.g_col = -1; if (gs.stcol >= curcol && gs.stcol < curcol + arg) gs.stcol = curcol; if (gs.stcol >= curcol + arg) gs.stcol -= arg; maxcol -= arg; /* Update note links. */ for (r = 0; r <= maxrow; r++) { for (c = 0; c <= maxcol; c++) { pp = ATBL(tbl, r, c); if (*pp) { if ((*pp)->ncol >= curcol && (*pp)->ncol < curcol + arg) (*pp)->ncol = curcol; if ((*pp)->ncol >= curcol + arg) ((*pp)->ncol) -= arg; if ((*pp)->nlastcol >= curcol && (*pp)->nlastcol < curcol + arg) (*pp)->nlastcol = curcol - 1; if ((*pp)->nlastcol >= curcol + arg) ((*pp)->nlastcol) -= arg; if ((*pp)->nlastcol < (*pp)->ncol) (*pp)->nrow = (*pp)->ncol = -1; } } } rowsinrange = 1; colsinrange = fwidth[curcol]; FullUpdate++; modflg++; } void doend(int rowinc, int colinc) { register struct ent *p; int r, c; if (!loading) remember(0); if (VALID_CELL(p, currow, curcol)) { r = currow + rowinc; c = curcol + colinc; if (r >= 0 && r < maxrows && c >= 0 && c < maxcols && !VALID_CELL(p, r, c)) { currow = r; curcol = c; } } if (!VALID_CELL(p, currow, curcol)) { switch (rowinc) { case -1: while (!VALID_CELL(p, currow, curcol) && currow > 0) currow--; break; case 1: while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1) currow++; break; case 0: switch (colinc) { case -1: while (!VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; break; case 1: while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1) curcol++; break; } break; } rowsinrange = 1; colsinrange = fwidth[curcol]; if (!loading) remember(1); CLEAR_LINE; /* clear line */ return; } switch (rowinc) { case -1: while (VALID_CELL(p, currow, curcol) && currow > 0) currow--; break; case 1: while (VALID_CELL(p, currow, curcol) && currow < maxrows-1) currow++; break; case 0: switch (colinc) { case -1: while (VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; break; case 1: while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1) curcol++; break; } break; } if (!VALID_CELL(p, currow, curcol)) { currow -= rowinc; curcol -= colinc; } rowsinrange = 1; colsinrange = fwidth[curcol]; } /* Modified 9/17/90 THA to handle more formats */ void doformat(int c1, int c2, int w, int p, int r) { register int i; int crows = 0; int ccols = c2; if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ; if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ; if (w == 0) { error("Width too small - setting to 1"); w = 1; } if (usecurses && w > COLS - rescol - 2) { error("Width too large - Maximum = %d", COLS - rescol - 2); w = COLS - rescol - 2; } if (p > w) { error("Precision too large"); p = w; } checkbounds(&crows, &ccols); if (ccols < c2) { error("Format statement failed to create implied column %d", c2); return; } for (i = c1; i <= c2; i++) fwidth[i] = w, precision[i] = p, realfmt[i] = r; rowsinrange = 1; colsinrange = fwidth[curcol]; FullUpdate++; modflg++; } void formatcol(int arg) { int c, i; int mf = modflg; int *oldformat; error("Current format is %d %d %d", fwidth[curcol], precision[curcol], realfmt[curcol]); refresh(); oldformat = scxmalloc(arg*3*sizeof(int)); for (i = 0; i < arg; i++) { oldformat[i * 3 + 0] = fwidth[i + curcol]; oldformat[i * 3 + 1] = precision[i + curcol]; oldformat[i * 3 + 2] = realfmt[i + curcol]; } c = nmgetch(); while (c >= 0 && c != ctl('m') && c != 'q' && c != ESC && c != ctl('g') && linelim < 0) { if (c >= '0' && c <= '9') for (i = curcol; i < curcol + arg; i++) realfmt[i] = c - '0'; else switch (c) { case KEY_LEFT: case '<': case 'h': for (i = curcol; i < curcol + arg; i++) { fwidth[i]--; if (fwidth[i] < 1) fwidth[i] = 1; } rowsinrange = 1; colsinrange = fwidth[curcol]; modflg++; break; case KEY_RIGHT: case '>': case 'l': for (i = curcol; i < curcol + arg; i++) { fwidth[i]++; if (fwidth[i] > COLS - rescol - 2) fwidth[i] = COLS - rescol - 2; } rowsinrange = 1; colsinrange = fwidth[curcol]; modflg++; break; case KEY_DOWN: case '-': case 'j': for (i = curcol; i < curcol + arg; i++) { precision[i]--; if (precision[i] < 0) precision[i] = 0; } modflg++; break; case KEY_UP: case '+': case 'k': for (i = curcol; i < curcol + arg; i++) precision[i]++; modflg++; break; case ' ': if (arg == 1) snprintf(line, sizeof line, "format [for column] %s ", coltoa(curcol)); else { snprintf(line, sizeof line, "format [for columns] %s:", coltoa(curcol)); linelim = strlen(line); snprintf(line + strlen(line), sizeof(line) - linelim, "%s ", coltoa(curcol+arg-1)); } linelim = strlen(line); insert_mode(); error("Current format is %d %d %d", fwidth[curcol], precision[curcol], realfmt[curcol]); continue; case '=': error("Define format type (0-9):"); refresh(); if ((c = nmgetch()) >= '0' && c <= '9') { if (colformat[c-'0']) { snprintf(line, sizeof line, "format %c = \"%s\"", c, colformat[c-'0']); edit_mode(); linelim = strlen(line) - 1; } else { snprintf(line, sizeof line, "format %c = \"", c); insert_mode(); linelim = strlen(line); } CLEAR_LINE; } else { error("Invalid format type"); c = -1; } continue; case ctl('l'): FullUpdate++; clearok(stdscr, 1); break; default: break; } error("Current format is %d %d %d", fwidth[curcol], precision[curcol], realfmt[curcol]); FullUpdate++; update(1); refresh(); if (linelim < 0) if ((c = nmgetch()) == ESC || c == ctl('g') || c == 'q') { for (i = 0; i < arg; i++) { fwidth[i + curcol] = oldformat[i * 3 + 0]; precision[i + curcol] = oldformat[i * 3 + 1]; realfmt[i + curcol] = oldformat[i * 3 + 2]; } modflg = mf; FullUpdate++; update(1); } } scxfree((char *)oldformat); if (c >= 0) CLEAR_LINE; } void ljustify(int sr, int sc, int er, int ec) { struct ent *p; int i, j; if (sr > er) { i = sr; sr = er; er = i; } if (sc > ec) { i = sc; sc = ec; ec = i; } for (i = sr; i <= er; i++) { for (j = sc; j <= ec; j++) { p = *ATBL(tbl, i, j); if (p && p->label) { p->flags &= ~IS_LABEL; p->flags |= IS_LEFTFLUSH | IS_CHANGED; changed++; modflg++; } } } } void rjustify(int sr, int sc, int er, int ec) { struct ent *p; int i, j; if (sr > er) { i = sr; sr = er; er = i; } if (sc > ec) { i = sc; sc = ec; ec = i; } for (i = sr; i <= er; i++) { for (j = sc; j <= ec; j++) { p = *ATBL(tbl, i, j); if (p && p->label) { p->flags &= ~(IS_LABEL | IS_LEFTFLUSH); p->flags |= IS_CHANGED; changed++; modflg++; } } } } void center(int sr, int sc, int er, int ec) { struct ent *p; int i, j; if (sr > er) { i = sr; sr = er; er = i; } if (sc > ec) { i = sc; sc = ec; ec = i; } for (i = sr; i <= er; i++) { for (j = sc; j <= ec; j++) { p = *ATBL(tbl, i, j); if (p && p->label) { p->flags &= ~IS_LEFTFLUSH; p->flags |= IS_LABEL | IS_CHANGED; changed++; modflg++; } } } } void print_options(FILE *f) { if ( autocalc && !autoinsert && !autowrap && !cslop && !optimize && !rndtoeven && propagation == 10 && calc_order == BYROWS && !numeric && prescale == 1.0 && !extfunc && showtop && tbl_style == 0 && craction == 0 && pagesize == 0 && rowlimit == -1 && collimit == -1 && !color && !colorneg && !colorerr ) return; /* No reason to do this */ (void) fprintf(f, "set"); if (!autocalc) (void) fprintf(f," !autocalc"); if (autoinsert) (void) fprintf(f," autoinsert"); if (autowrap) (void) fprintf(f," autowrap"); if (cslop) (void) fprintf(f," cslop"); if (optimize) (void) fprintf(f," optimize"); if (rndtoeven) (void) fprintf(f, " rndtoeven"); if (propagation != 10) (void) fprintf(f, " iterations = %d", propagation); if (calc_order != BYROWS ) (void) fprintf(f, " bycols"); if (numeric) (void) fprintf(f, " numeric"); if (prescale != 1.0) (void) fprintf(f, " prescale"); if (extfunc) (void) fprintf(f, " extfun"); if (!showtop) (void) fprintf(f, " !toprow"); if (tbl_style) (void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" : tbl_style == LATEX ? "latex" : tbl_style == SLATEX ? "slatex" : tbl_style == TEX ? "tex" : tbl_style == FRAME ? "frame" : "0" ); if (craction) (void) fprintf(f, " craction = %d", craction); if (pagesize) (void) fprintf(f, " pagesize = %d", pagesize); if (rowlimit >= 0) (void) fprintf(f, " rowlimit = %d", rowlimit); if (collimit >= 0) (void) fprintf(f, " collimit = %d", collimit); if (color) (void) fprintf(f," color"); if (colorneg) (void) fprintf(f," colorneg"); if (colorerr) (void) fprintf(f," colorerr"); (void) fprintf(f, "\n"); } void printfile(char *fname, int r0, int c0, int rn, int cn) { FILE *f; static char *pline = NULL; /* only malloc once, malloc is slow */ static unsigned fbufs_allocated = 0; int plinelim; int pid; unsigned int fieldlen, nextcol; long namelen; int row, col; register struct ent **pp; size_t fnamesiz; char file[256]; char path[1024]; char *tpp; if (fname) { /* printfile will be the [path/]file ---> [path/]file.out */ if (*fname == '\0') { strlcpy(path, curfile, sizeof path); #ifdef MSDOS namelen = 12; if ((tpp = strrchr(path, '\\')) == NULL) { #else if ((tpp = strrchr(path, '/')) == NULL) { namelen = pathconf(".", _PC_NAME_MAX); #endif tpp = path; } else { #ifndef MSDOS *tpp = '\0'; namelen = pathconf(path, _PC_NAME_MAX); *tpp = '/'; #endif tpp++; } strlcpy(file, tpp, sizeof file); if (!strcmp(file + strlen(file) - 3, ".sc")) file[strlen(file) - 3] = '\0'; else if (scext != NULL && file[strlen(file) - strlen(scext) - 1] == '.' && !strcmp(file + strlen(file) - strlen(scext), scext)) file[strlen(file) - strlen(scext)] = '\0'; if (ascext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(ascext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, ascext == NULL ? "asc" : ascext); fname = path; fnamesiz = sizeof path; } else /* strarg in gram.y, always size of \0 terminated string. */ /* TODO: Possible problem if ~ needs to be expanded. */ fnamesiz = strlen(fname) + 1; if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the data base: (y,n)")) { return; } if ((f = openfile(fname, fnamesiz, &pid, NULL)) == (FILE *)0) { error("Can't create file \"%s\"", fname); return; } } else f = stdout; if (!pline && (pline = scxmalloc(FBUFLEN * ++fbufs_allocated)) == (char *)NULL) { error("Malloc failed in printfile()"); return; } for (row = r0; row <= rn; row++) { unsigned int c = 0; if (row_hidden[row]) continue; pline[plinelim=0] = '\0'; for (pp = ATBL(tbl, row, col=c0); col<=cn; pp += nextcol-col, col = nextcol, c += fieldlen) { nextcol = col+1; if (col_hidden[col]) { fieldlen = 0; continue; } fieldlen = fwidth[col]; if (*pp) { char *s; /* * dynamically allocate pline, making sure we are not * attempting to write 'out of bounds'. */ while (c > (fbufs_allocated * FBUFLEN)) { if ((pline = scxrealloc (pline, FBUFLEN * ++fbufs_allocated)) == NULL) { error ("Realloc failed in printfile()"); return; } } while (plinelim < (int)c) pline[plinelim++] = ' '; plinelim = c; if ((*pp)->flags&IS_VALID) { while(plinelim + fwidth[col] > (int)(fbufs_allocated * FBUFLEN)) { if((pline = scxrealloc(pline, FBUFLEN * ++fbufs_allocated)) == NULL) { error("Realloc failed in printfile()"); return; } } if ((*pp)->cellerror) snprintf(pline + plinelim, FBUFLEN * fbufs_allocated - plinelim, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR " : "INVALID ")); else { char *cfmt; cfmt = (*pp)->format ? (*pp)->format : (realfmt[col] >= 0 && realfmt[col] < COLFORMATS && colformat[realfmt[col]]) ? colformat[realfmt[col]] : 0; if (cfmt) { char field[FBUFLEN]; if (*cfmt == ctl('d')) { time_t v = (time_t) ((*pp)->v); strftime(field, sizeof(field), cfmt + 1, localtime(&v)); snprintf(pline + plinelim, FBUFLEN * fbufs_allocated - plinelim, "%-*s", fwidth[col], field); } else { format(cfmt, precision[col], (*pp)->v, field, sizeof(field)); snprintf(pline+plinelim, FBUFLEN * fbufs_allocated - plinelim, "%*s", fwidth[col], field); } } else { char field[FBUFLEN]; engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field)); snprintf(pline+plinelim, FBUFLEN * fbufs_allocated - plinelim, "%*s", fwidth[col], field); } } plinelim += strlen(pline+plinelim); } if ((s = (*pp)->label)) { ssize_t slen; char *start, *last; register char *fp; struct ent *nc; /* * Figure out if the label slops over to a blank field. * A string started with backslash is defining repetition * char */ slen = strlen(s); if (*s == '\\' && *(s+1) != '\0') slen = fwidth[col]; while (slen > (ssize_t)fieldlen && (int)nextcol <= cn && !((nc = lookat(row,nextcol))->flags & IS_VALID) && !(nc->label)) { if (!col_hidden[nextcol]) fieldlen += fwidth[nextcol]; nextcol++; } if (slen > (ssize_t)fieldlen) slen = fieldlen; while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) { if((pline = scxrealloc(pline, FBUFLEN * ++fbufs_allocated)) == NULL) { error ("scxrealloc failed in printfile()"); return; } } /* Now justify and print */ start = (*pp)->flags & IS_LEFTFLUSH ? pline + c : pline + c + fieldlen - slen; if( (*pp)->flags & IS_LABEL ) start = pline + (c + ((fwidth[col] > slen) ? (fwidth[col] - slen) / 2 : 0)); last = pline + c + fieldlen; fp = plinelim < (int)c ? pline + plinelim : pline + c; while (fp < start) *fp++ = ' '; if( *s == '\\' && *(s+1)!= '\0' ) { char *strt; strt = ++s; while(slen--) { *fp++ = *s++; if (*s == '\0') s = strt; } } else { while (slen-- > 0) { if (*s == '\\') { if (s[1] == '"') { ++s; --slen; } else if (s[1] == '\\' && s[2] == '"') { s += 2; slen -= 2; } } *fp++ = *s++; } } if (!((*pp)->flags & IS_VALID) || (int)fieldlen != fwidth[col]) while(fp < last) *fp++ = ' '; if (plinelim < fp - pline) plinelim = fp - pline; } } } pline[plinelim++] = '\n'; pline[plinelim] = '\0'; (void) fputs (pline, f); } if (fname) closefile(f, pid, 0); } void tblprintfile(char *fname, int r0, int c0, int rn, int cn) { FILE *f; int pid; long namelen; int row, col; register struct ent **pp; size_t fnamesiz; char coldelim = DEFCOLDELIM; char file[256]; char path[1024]; char *tpp; /* tblprintfile will be the [path/]file ---> [path/]file.out */ if (*fname == '\0') { strlcpy(path, curfile, sizeof path); #ifdef MSDOS namelen = 12; if ((tpp = strrchr(path, '\\')) == NULL) { #else if ((tpp = strrchr(path, '/')) == NULL) { namelen = pathconf(".", _PC_NAME_MAX); #endif tpp = path; } else { #ifndef MSDOS *tpp = '\0'; namelen = pathconf(path, _PC_NAME_MAX); *tpp = '/'; #endif tpp++; } strlcpy(file, tpp, sizeof file); if (!strcmp(file + strlen(file) - 3, ".sc")) file[strlen(file) - 3] = '\0'; else if (scext != NULL && file[strlen(file) - strlen(scext) - 1] == '.' && !strcmp(file + strlen(file) - strlen(scext), scext)) file[strlen(file) - strlen(scext)] = '\0'; if (tbl_style == 0) { if (tbl0ext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(tbl0ext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, tbl0ext == NULL ? "cln" : tbl0ext); } else if (tbl_style == TBL) { if (tblext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(tblext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, tblext == NULL ? "tbl" : tblext); } else if (tbl_style == LATEX) { if (latexext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(latexext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, latexext == NULL ? "lat" : latexext); } else if (tbl_style == SLATEX) { if (slatexext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(slatexext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, slatexext == NULL ? "stx" : slatexext); } else if (tbl_style == TEX) { if (texext == NULL) file[namelen - 4] = '\0'; else file[namelen - strlen(texext) - 1] = '\0'; snprintf(tpp, sizeof(path) - (tpp - path), "%s.%s", file, texext == NULL ? "tex" : texext); } fname = path; fnamesiz = sizeof path; } else /* TODO: Possible problem if ~ needs to be expanded. */ fnamesiz = strlen(fname) + 1; if ((strcmp(fname, curfile) == 0) && !yn_ask("Confirm that you want to destroy the data base: (y,n)")) return; if ((f = openfile(fname, fnamesiz, &pid, NULL)) == (FILE *)0) { error ("Can't create file \"%s\"", fname); return; } if ( tbl_style == TBL ) { fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname); fprintf(f,"tab(%c);\n",coldelim); for (col=c0;col<=cn; col++) fprintf(f," n"); fprintf(f, ".\n"); } else if ( tbl_style == LATEX ) { fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname); for (col=c0;col<=cn; col++) fprintf(f,"c"); fprintf(f, "}\n"); coldelim = '&'; } else if ( tbl_style == SLATEX ) { fprintf(f,"%% ** %s spreadsheet output\n!begin<",progname); for (col=c0;col<=cn; col++) fprintf(f,"c"); fprintf(f, ">\n"); coldelim = '&'; } else if ( tbl_style == TEX ) { fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n", progname, cn-c0+1); coldelim = '&'; } else if ( tbl_style == FRAME ) { fprintf(f," # generated by the sc spreadsheet calculator\n"); fprintf(f," # This table's ID is 1\n"); fprintf(f," # Table Format Catalog\n"); fprintf(f," > # end of TblFormat\n"); fprintf(f," # Has %d columns\n",cn-c0+1,cn-c0+1); fprintf(f," # Forces lookup in Paragraph Format Catalog\n"); fprintf(f," \n",fname); fprintf(f," > # end of ParaLine\n"); fprintf(f," > # end of Para\n"); fprintf(f," > # end of TblTitleContent\n"); fprintf(f," # in Paragraph Format Catalog\n"); fprintf(f," >\n",'A'+col); fprintf(f," >>> # end of Cell\n"); } fprintf(f," > # end of Row\n"); fprintf(f," > # end of TblH\n"); fprintf(f," # in Paragraph Format Catalog\n"); fprintf(f," flags&IS_VALID) { if ((*pp)->cellerror) { (void) fprintf (f, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID")); } else if ((*pp)->format) { char field[FBUFLEN]; if (*((*pp)->format) == ctl('d')) { time_t v = (time_t) ((*pp)->v); strftime(field, sizeof(field), ((*pp)->format)+1, localtime(&v)); } else format((*pp)->format, precision[col], (*pp)->v, field, sizeof(field)); unspecial(f, field, coldelim); } else { char field[FBUFLEN]; (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field)); unspecial(f, field, coldelim); } } if ((s = (*pp)->label)) { unspecial(f, s, coldelim); } } if (tbl_style == FRAME) { fprintf(f, "'>>\n"); fprintf(f," >>> # end of Cell\n"); } if (col < cn) if (tbl_style != FRAME) (void) fprintf(f,"%c", coldelim); } if (tbl_style == LATEX) { if (row < rn) (void) fprintf (f, "\\\\"); } else if (tbl_style == SLATEX) { if (row < rn) (void) fprintf(f, "!!"); } else if (tbl_style == TEX) { (void) fprintf (f, "\\cr"); } else if (tbl_style == FRAME) { fprintf(f," > # end of Row\n"); } (void) fprintf(f,"\n"); } if (tbl_style == TBL) (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname); else if (tbl_style == LATEX) (void) fprintf(f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname); else if (tbl_style == SLATEX) (void) fprintf (f,"!end\n%% ** end of %s spreadsheet output\n", progname); else if (tbl_style == TEX) (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname); else if (tbl_style == FRAME) { fprintf(f," > # end of TblBody\n"); fprintf(f," ># end of Tbl\n"); fprintf(f,"> # end of Tbls\n"); fprintf(f," \n"); fprintf(f," > # Reference to table ID 1\n"); fprintf(f,">>\n"); } closefile(f, pid, 0); } /* unspecial (backquote) things that are special chars in a table */ void unspecial(FILE *f, char *str, int delim) { if (*str == '\\') str++; /* delete wheeling string operator, OK? */ while (*str) { if (((tbl_style == LATEX) || (tbl_style == SLATEX) || (tbl_style == TEX)) && ((*str == delim) || (*str == '$') || (*str == '#') || (*str == '%') || (*str == '{') || (*str == '}') || (*str == '&'))) putc('\\', f); putc(*str, f); str++; } } struct enode * copye(register struct enode *e, int Rdelta, int Cdelta, int r1, int c1, int r2, int c2, int transpose) { register struct enode *ret; static struct enode *range = NULL; if (e == (struct enode *)0) ret = (struct enode *)0; else if (e->op & REDUCE) { int newrow, newcol; if (freeenodes) { ret = freeenodes; freeenodes = ret->e.o.left; } else ret = scxmalloc(sizeof (struct enode)); ret->op = e->op; newrow = e->e.r.left.vf & FIX_ROW || e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 || e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ? e->e.r.left.vp->row : transpose ? r1 + Rdelta + e->e.r.left.vp->col - c1 : e->e.r.left.vp->row + Rdelta; newcol = e->e.r.left.vf & FIX_COL || e->e.r.left.vp->row < r1 || e->e.r.left.vp->row > r2 || e->e.r.left.vp->col < c1 || e->e.r.left.vp->col > c2 ? e->e.r.left.vp->col : transpose ? c1 + Cdelta + e->e.r.left.vp->row - r1 : e->e.r.left.vp->col + Cdelta; ret->e.r.left.vp = lookat(newrow, newcol); ret->e.r.left.vf = e->e.r.left.vf; newrow = e->e.r.right.vf & FIX_ROW || e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 || e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ? e->e.r.right.vp->row : transpose ? r1 + Rdelta + e->e.r.right.vp->col - c1 : e->e.r.right.vp->row + Rdelta; newcol = e->e.r.right.vf & FIX_COL || e->e.r.right.vp->row < r1 || e->e.r.right.vp->row > r2 || e->e.r.right.vp->col < c1 || e->e.r.right.vp->col > c2 ? e->e.r.right.vp->col : transpose ? c1 + Cdelta + e->e.r.right.vp->row - r1 : e->e.r.right.vp->col + Cdelta; ret->e.r.right.vp = lookat(newrow, newcol); ret->e.r.right.vf = e->e.r.right.vf; } else { struct enode *temprange = NULL; size_t l; if (freeenodes) { ret = freeenodes; freeenodes = ret->e.o.left; } else ret = scxmalloc(sizeof (struct enode)); ret->op = e->op; switch (ret->op) { case SUM: case PROD: case AVG: case COUNT: case STDDEV: case MAX: case MIN: temprange = range; range = e->e.o.left; r1 = 0; c1 = 0; r2 = maxrow; c2 = maxcol; } switch (ret->op) { case 'v': { int newrow, newcol; if (range && e->e.v.vp->row >= range->e.r.left.vp->row && e->e.v.vp->row <= range->e.r.right.vp->row && e->e.v.vp->col >= range->e.r.left.vp->col && e->e.v.vp->col <= range->e.r.right.vp->col) { newrow = range->e.r.left.vf & FIX_ROW ? e->e.v.vp->row : e->e.v.vp->row + Rdelta; newcol = range->e.r.left.vf & FIX_COL ? e->e.v.vp->col : e->e.v.vp->col + Cdelta; } else { newrow = e->e.v.vf & FIX_ROW || e->e.v.vp->row < r1 || e->e.v.vp->row > r2 || e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ? e->e.v.vp->row : transpose ? r1 + Rdelta + e->e.v.vp->col - c1 : e->e.v.vp->row + Rdelta; newcol = e->e.v.vf & FIX_COL || e->e.v.vp->row < r1 || e->e.v.vp->row > r2 || e->e.v.vp->col < c1 || e->e.v.vp->col > c2 ? e->e.v.vp->col : transpose ? c1 + Cdelta + e->e.v.vp->row - r1 : e->e.v.vp->col + Cdelta; } ret->e.v.vp = lookat(newrow, newcol); ret->e.v.vf = e->e.v.vf; break; } case 'k': ret->e.k = e->e.k; break; case 'f': case 'F': if (( range && ret->op == 'F') || (!range && ret->op == 'f') ) Rdelta = Cdelta = 0; ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta, r1, c1, r2, c2, transpose); ret->e.o.right = (struct enode *)0; break; case '$': case EXT: l = strlen(e->e.s) + 1; ret->e.s = scxmalloc(l); strlcpy(ret->e.s, e->e.s, l); if (e->op == '$') /* Drop through if ret->op is EXT */ break; default: ret->e.o.left = copye(e->e.o.left, Rdelta, Cdelta, r1, c1, r2, c2, transpose); ret->e.o.right = copye(e->e.o.right, Rdelta, Cdelta, r1, c1, r2, c2, transpose); break; } switch (ret->op) { case SUM: case PROD: case AVG: case COUNT: case STDDEV: case MAX: case MIN: range = temprange; } } return ret; } /* * sync_refs and syncref are used to remove references to * deleted struct ents. Note that the deleted structure must still * be hanging around before the call, but not referenced by an entry * in tbl. Thus the free_ent calls in sc.c */ void sync_refs(void) { int i, j; register struct ent *p; sync_ranges(); for (i=0; i<=maxrow; i++) for (j=0; j<=maxcol; j++) if ((p = *ATBL(tbl, i, j)) && p->expr) syncref(p->expr); for (i = 0; i < DELBUFSIZE; i++) { p = delbuf[i]; while (p && p->expr) { syncref(p->expr); p = p->next; } } } void syncref(register struct enode *e) { if (e == (struct enode *)0) return; else if (e->op & REDUCE) { e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col); e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col); } else { switch (e->op) { case 'v': if (e->e.v.vp->flags & IS_CLEARED) { e->op = ERR_; e->e.o.left = NULL; e->e.o.right = NULL; } else if (e->e.v.vp->flags & MAY_SYNC) e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col); break; case 'k': break; case '$': break; default: syncref(e->e.o.right); syncref(e->e.o.left); break; } } } /* mark a row as hidden */ void hiderow(int arg) { register int r1; register int r2; r1 = currow; r2 = r1 + arg - 1; if (r1 < 0 || r1 > r2) { error("Invalid range"); return; } if (r2 >= maxrows-1) { if (!growtbl(GROWROW, arg+1, 0)) { error("You can't hide the last row"); return; } } FullUpdate++; modflg++; while (r1 <= r2) row_hidden[r1++] = 1; } /* mark a column as hidden */ void hidecol(int arg) { int c1; int c2; c1 = curcol; c2 = c1 + arg - 1; if (c1 < 0 || c1 > c2) { error ("Invalid range"); return; } if (c2 >= maxcols-1) { if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1)) { error("You can't hide the last col"); return; } } FullUpdate++; modflg++; while (c1 <= c2) col_hidden[c1++] = TRUE; } /* mark a row as not-hidden */ void showrow(int r1, int r2) { if (r1 < 0 || r1 > r2) { error ("Invalid range"); return; } if (r2 > maxrows-1) { r2 = maxrows-1; } FullUpdate++; modflg++; while (r1 <= r2) row_hidden[r1++] = 0; } /* mark a column as not-hidden */ void showcol(int c1, int c2) { if (c1 < 0 || c1 > c2) { error ("Invalid range"); return; } if (c2 > maxcols-1) { c2 = maxcols-1; } FullUpdate++; modflg++; while (c1 <= c2) col_hidden[c1++] = FALSE; } /* Open the input or output file, setting up a pipe if needed */ FILE * openfile(char *fname, size_t fnamesiz, int *rpid, int *rfd) { int pipefd[4]; int pid; FILE *f; char *efname; while (*fname && (*fname == ' ')) { /* Skip leading blanks */ fname++; fnamesiz--; } if (*fname != '|') { /* Open file if not pipe */ *rpid = 0; if (rfd != NULL) *rfd = 1; /* Set to stdout just in case */ efname = findhome(fname, fnamesiz); if (dobackups && rfd == NULL && !backup_file(efname) && (yn_ask("Could not create backup copy. Save anyway?: (y,n)") != 1)) return (0); return (fopen(efname, rfd == NULL ? "w" : "r")); } #if defined(MSDOS) error("Piping not available under MS-DOS\n"); return (0); #else fname++; /* Skip | */ fnamesiz--; efname = findhome(fname, fnamesiz); if (pipe(pipefd) < 0 || (rfd != NULL && pipe(pipefd+2) < 0)) { error("Can't make pipe to child"); *rpid = 0; return (0); } deraw(rfd==NULL); #ifdef VMS fprintf(stderr, "No son tasks available yet under VMS--sorry\n"); #else /* VMS */ if ((pid=fork()) == 0) { /* if child */ (void) close(0); /* close stdin */ (void) close(pipefd[1]); (void) dup(pipefd[0]); /* connect to first pipe */ if (rfd != NULL) { /* if opening for read */ (void) close(1); /* close stdout */ (void) close(pipefd[2]); (void) dup(pipefd[3]); /* connect to second pipe */ } (void) signal(SIGINT, SIG_DFL); /* reset */ execl("/bin/sh", "sh", "-c", efname, (char *)NULL); exit (-127); } else { /* else parent */ *rpid = pid; if ((f = fdopen(pipefd[(rfd==NULL?1:2)], rfd==NULL?"w":"r")) == NULL) { (void) kill(pid, 9); error("Can't fdopen %sput", rfd==NULL?"out":"in"); (void) close(pipefd[1]); if (rfd != NULL) (void) close(pipefd[3]); *rpid = 0; return (0); } } (void) close(pipefd[0]); if (rfd != NULL) { (void) close(pipefd[3]); *rfd = pipefd[1]; } #endif /* VMS */ return (f); #endif /* MSDOS */ } /* close a file opened by openfile(), if process wait for return */ void closefile(FILE *f, int pid, int rfd) { int temp; (void) fclose(f); #ifndef MSDOS if (pid) { while (pid != wait(&temp)) /**/; if (rfd==0) { (void) printf("Press any key to continue "); (void) fflush(stdout); cbreak(); (void) nmgetch(); goraw(); clear(); } else { close(rfd); if (usecurses) { # ifdef VMS VMS_read_raw = 1; # else /* VMS */ # ifdef HAVE_FIXTERM fixterm(); # else cbreak(); nonl(); noecho (); # endif kbd_again(); # endif /* VMS */ if (color && has_colors()) bkgdset(COLOR_PAIR(1) | ' '); } } } #endif /* MSDOS */ if (brokenpipe) { error("Broken pipe"); brokenpipe = FALSE; } } /* Copy a cell (struct ent). "special" indicates special treatment when * merging two cells for the "pm" command, merging formats only for the * "pf" command, or for adjusting cell references when transposing with * the "pt" command. r1, c1, r2, and c2 define the range in which the dr * and dc values should be used. */ void copyent(register struct ent *n, register struct ent *p, int dr, int dc, int r1, int c1, int r2, int c2, int special) { size_t l; if (!n || !p) { error("internal error"); return; } if (special != 'f') { if (special != 'm' || p->flags & IS_VALID) { n->v = p->v; n->flags |= p->flags & IS_VALID; } if (special != 'm' || p->expr) { n->expr = copye(p->expr, dr, dc, r1, c1, r2, c2, special == 't'); if (p->flags & IS_STREXPR) n->flags |= IS_STREXPR; else n->flags &= ~IS_STREXPR; } if (p->label) { if (n->label) scxfree(n->label); l = strlen(p->label) + 1; n->label = scxmalloc(l); strlcpy(n->label, p->label, l); n->flags &= ~IS_LEFTFLUSH; n->flags |= ((p->flags & IS_LABEL) | (p->flags & IS_LEFTFLUSH)); } else if (special != 'm') { n->label = NULL; n->flags &= ~(IS_LABEL | IS_LEFTFLUSH); } n->flags |= p->flags & IS_LOCKED; } if (p->format) { if (n->format) scxfree(n->format); l = strlen(p->format) + 1; n->format = scxmalloc(l); strlcpy(n->format, p->format, l); } else if (special != 'm' && special != 'f') n->format = NULL; n->flags |= IS_CHANGED; } #ifndef MSDOS /* add a plugin/mapping pair to the end of the filter list. type is * r(ead) or w(rite) */ void addplugin(char *ext, char *plugin, char type) { struct impexfilt *fp; char mesg[PATHLEN]; if (!plugin_exists(plugin, strlen(plugin), mesg)) { error("Cannot find plugin %s", plugin); return; } if (filt == NULL) { filt = scxmalloc(sizeof(struct impexfilt)); fp = filt; } else { fp = filt; while (fp->next != NULL) fp = fp->next; fp->next = scxmalloc(sizeof(struct impexfilt)); fp = fp->next; } strlcpy(fp->plugin, plugin, PATHLEN); strlcpy(fp->ext, ext, PATHLEN); fp->type = type; fp->next = NULL; } char * findplugin(char *ext, char type) { struct impexfilt *fp; fp = filt; if (fp == NULL) return (NULL); if ((!strcmp(fp->ext, ext)) && (fp->type == type)) return (fp->plugin); while (fp->next != NULL) { fp = fp->next; if ((!strcmp(fp->ext, ext)) && (fp->type == type)) return (fp->plugin); } return (NULL); } #endif void write_fd(register FILE *f, int r0, int c0, int rn, int cn) { register struct ent **pp; int r, c; (void) fprintf(f, "# This data file was generated by the Spreadsheet "); (void) fprintf(f, "Calculator.\n"); (void) fprintf(f, "# You almost certainly shouldn't edit it.\n\n"); print_options(f); for (c = 0; c < COLFORMATS; c++) if (colformat[c]) (void) fprintf (f, "format %d = \"%s\"\n", c, colformat[c]); for (c = c0; c <= cn; c++) if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC || realfmt[c] != DEFREFMT) (void) fprintf (f, "format %s %d %d %d\n", coltoa(c), fwidth[c], precision[c], realfmt[c]); for (c = c0; c <= cn; c++) if (col_hidden[c]) (void) fprintf(f, "hide %s\n", coltoa(c)); for (r = r0; r <= rn; r++) if (row_hidden[r]) (void) fprintf(f, "hide %d\n", r); write_ranges(f); write_franges(f); write_colors(f, 0); write_cranges(f); if (mdir) (void) fprintf(f, "mdir \"%s\"\n", mdir); if (autorun) (void) fprintf(f, "autorun \"%s\"\n", autorun); for (c = 0; c < FKEYS; c++) if (fkey[c]) (void) fprintf(f, "fkey %d = \"%s\"\n", c + 1, fkey[c]); write_cells(f, r0, c0, rn, cn, r0, c0); for (r = r0; r <= rn; r++) { pp = ATBL(tbl, r, c0); for (c = c0; c <= cn; c++, pp++) if (*pp) { if ((*pp)->flags&IS_LOCKED) (void) fprintf(f, "lock %s%d\n", coltoa((*pp)->col), (*pp)->row); if ((*pp)->nrow >= 0) { (void) fprintf(f, "addnote %s ", v_name((*pp)->row, (*pp)->col)); (void) fprintf(f, "%s\n", r_name((*pp)->nrow, (*pp)->ncol, (*pp)->nlastrow, (*pp)->nlastcol)); } } } /* * Don't try to combine these into a single fprintf(). v_name() has * a single buffer that is overwritten on each call, so the first part * needs to be written to the file before making the second call. */ fprintf(f, "goto %s", v_name(currow, curcol)); fprintf(f, " %s\n", v_name(strow, stcol)); } void write_cells(register FILE *f, int r0, int c0, int rn, int cn, int dr, int dc) { register struct ent **pp; int r, c, mf; int rs = 0; int cs = 0; char *dpointptr; mf = modflg; if (dr != r0 || dc != c0) { yank_area(r0, c0, rn, cn); rn += dr - r0; cn += dc - c0; rs = currow; cs = curcol; currow = dr; curcol = dc; pullcells('x'); } if (Vopt) valueize_area(dr, dc, rn, cn); for (r = dr; r <= rn; r++) { pp = ATBL(tbl, r, dc); for (c = dc; c <= cn; c++, pp++) if (*pp) { if ((*pp)->label || (*pp)->flags & IS_STREXPR) { edits(r, c); (void) fprintf(f, "%s\n", line); } if ((*pp)->flags & IS_VALID) { editv(r, c); dpointptr = strchr(line, dpoint); if (dpointptr != NULL) *dpointptr = '.'; (void) fprintf(f, "%s\n", line); } if ((*pp)->format) { editfmt(r, c); (void) fprintf(f, "%s\n",line); } } } if (dr != r0 || dc != c0) { pullcells('x'); currow = rs; curcol = cs; flush_saved(); } modflg = mf; } int writefile(char *fname, int r0, int c0, int rn, int cn) { register FILE *f; char save[PATHLEN]; char tfname[PATHLEN]; long namelen; char *tpp; char *p; char *plugin; int pid; #ifndef MSDOS /* find the extension and mapped plugin if exists */ if ((p = strrchr(fname, '.'))) { if ((plugin = findplugin(p+1, 'w')) != NULL) { size_t l; if (!plugin_exists(plugin, strlen(plugin), save + 1)) { error("plugin not found"); return -1; } *save = '|'; if ((strlen(save) + strlen(fname) + 20) > PATHLEN) { error("Path too long"); return -1; } l = strlen(save); snprintf(save + l, sizeof(save) - l, " %s%d:", coltoa(c0), r0); l = strlen(save); snprintf(save + l, sizeof(save) - l, "%s%d \"%s\"", coltoa(cn), rn, fname); /* pass it to readfile as an advanced macro */ readfile(save, 0); return (0); } } #endif #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { return (cwritefile(fname, r0, c0, rn, cn)); } #endif /* VMS */ if (*fname == '\0') { if (isatty(STDOUT_FILENO) || *curfile != '\0') fname = curfile; else { write_fd(stdout, r0, c0, rn, cn); return (0); } } #ifdef MSDOS namelen = 12; #else if ((tpp = strrchr(fname, '/')) == NULL) namelen = pathconf(".", _PC_NAME_MAX); else { *tpp = '\0'; namelen = pathconf(fname, _PC_NAME_MAX); *tpp = '/'; } #endif /* MSDOS */ (void) strlcpy(tfname, fname, sizeof tfname); for (tpp = tfname; *tpp != '\0'; tpp++) if (*tpp == '\\' && *(tpp + 1) == '"') (void) memmove(tpp, tpp + 1, strlen(tpp)); if (scext != NULL) { if (strlen(tfname) > 3 && !strcmp(tfname + strlen(tfname) - 3, ".sc")) tfname[strlen(tfname) - 3] = '\0'; else if (strlen(tfname) > strlen(scext) + 1 && tfname[strlen(tfname) - strlen(scext) - 1] == '.' && !strcmp(tfname + strlen(tfname) - strlen(scext), scext)) tfname[strlen(tfname) - strlen(scext) - 1] = '\0'; tfname[namelen - strlen(scext) - 1] = '\0'; strlcat(tfname, ".", sizeof tfname); strlcat(tfname, scext, sizeof tfname); } (void) strlcpy(save, tfname, sizeof save); for (tpp = save; *tpp != '\0'; tpp++) if (*tpp == '"') { (void) memmove(tpp + 1, tpp, strlen(tpp) + 1); *tpp++ = '\\'; } if ((f = openfile(tfname, sizeof tfname, &pid, NULL)) == NULL) { error("Can't create file \"%s\"", save); return (-1); } if (usecurses) { error("Writing file \"%s\"...", save); refresh(); } write_fd(f, r0, c0, rn, cn); closefile(f, pid, 0); if (!pid) { (void) strlcpy(curfile, save, sizeof curfile); modflg = 0; FullUpdate++; if (usecurses) { error("File \"%s\" written", curfile); } else fprintf(stderr, "\nFile \"%s\" written", curfile); } return (0); } int readfile(char *fname, int eraseflg) { register FILE *f; char save[PATHLEN]; int tempautolabel; char *p; char *plugin; int pid = 0; int rfd = STDOUT_FILENO, savefd; tempautolabel = autolabel; /* turn off auto label when */ autolabel = 0; /* reading a file */ if (*fname == '*' && mdir) { (void) strlcpy(save, mdir, sizeof save); (void) strlcat(save, fname, sizeof save); } else { if (*fname == '\0') fname = curfile; (void) strlcpy(save, fname, sizeof save); } #ifndef MSDOS if ((p = strrchr(fname, '.')) && (fname[0] != '|')) { /* exclude macros */ if ((plugin = findplugin(p+1, 'r')) != NULL) { size_t l; if (!(plugin_exists(plugin, strlen(plugin), save + 1))) { error("plugin not found"); return 0; } *save = '|'; if ((strlen(save) + strlen(fname) + 2) > PATHLEN) { error("Path too long"); return 0; } l = strlen(save); snprintf(save + l, sizeof(save) - l, " \"%s\"", fname); eraseflg = 0; /* get filename: could be preceded by params if this is * a save */ while (p > fname) { if (*p == ' ') { p++; break; } p--; } (void) strlcpy(curfile, p, sizeof curfile); } } #endif #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) if (Crypt) { int ret = 0; if (*save == '-' && strlen(fname) == 1) error("Can't use encryption in a pipeline."); else if (*save == '|') error("Can't use encryption with advanced macros."); else ret = creadfile(save, eraseflg); autolabel = tempautolabel; return (ret); } #endif /* VMS */ if (eraseflg && strcmp(fname, curfile) && modcheck(" first")) return 0; #ifndef MSDOS if (fname[0] == '-' && fname[1] == '\0') { f = stdin; *save = '\0'; } else { #endif /* MSDOS */ if ((f = openfile(save, sizeof save, &pid, &rfd)) == NULL) { error("Can't read file \"%s\"", save); autolabel = tempautolabel; return 0; } else if (eraseflg) { if (usecurses) { error("Reading file \"%s\"", save); refresh(); } else fprintf(stderr, "\nReading file \"%s\"", save); } #ifndef MSDOS } if (*fname == '|') *save = '\0'; #endif /* MSDOS */ if (eraseflg) erasedb(); remember(0); loading++; savefd = macrofd; macrofd = rfd; while (!brokenpipe && fgets(line, sizeof(line), f)) { #ifndef MSDOS if (line[0] == '|' && pid != 0) { line[0] = ' '; } #endif /* MSDOS */ linelim = 0; if (line[0] != '#') (void) yyparse(); } macrofd = savefd; --loading; remember(1); closefile(f, pid, rfd); #ifndef MSDOS if (f == stdin) { freopen("/dev/tty", "r", stdin); goraw(); } #endif /* MSDOS */ linelim = -1; if (eraseflg) { (void) strlcpy(curfile, save, sizeof curfile); modflg = 0; cellassign = 0; if (autorun && !skipautorun) (void) readfile(autorun, 0); skipautorun = 0; EvalAll(); } autolabel = tempautolabel; FullUpdate++; return 1; } /* erase the database (tbl, etc.) */ void erasedb(void) { int r, c; char *home; for (c = 0; c<=maxcol; c++) { fwidth[c] = DEFWIDTH; precision[c] = DEFPREC; realfmt[c] = DEFREFMT; } for (r = 0; r<=maxrow; r++) { register struct ent **pp = ATBL(tbl, r, 0); for (c=0; c++<=maxcol; pp++) if (*pp) { if ((*pp)->expr) efree((*pp) -> expr); if ((*pp)->label) scxfree((char *)((*pp) -> label)); (*pp)->next = freeents; /* save [struct ent] for reuse */ freeents = *pp; *pp = (struct ent *)0; } } for (c = 0; c < COLFORMATS; c++) if (colformat[c]) { scxfree(colformat[c]); colformat[c] = NULL; } maxrow = 0; maxcol = 0; clean_range(); clean_frange(); clean_crange(); propagation = 10; calc_order = BYROWS; prescale = 1.0; tbl_style = 0; craction = 0; rowlimit = collimit = -1; qbuf = 0; autocalc=showcell=showtop=1; autoinsert=autowrap=optimize=numeric=extfunc=color=colorneg=colorerr=cslop=0; currow=curcol=strow=stcol=0; if (usecurses && has_colors()) color_set(0, NULL); /* unset all marks */ for (c = 1; c < 37; c++) savedrow[c] = savedcol[c] = savedstrow[c] = savedstcol[c] = -1; if (mdir) { scxfree(mdir); mdir = NULL; } if (autorun) { scxfree(autorun); autorun = NULL; } for (c = 0; c < FKEYS; c++) if (fkey[c]) { scxfree(fkey[c]); fkey[c] = NULL; } #ifndef MSDOS /* * Load $HOME/.scrc if present. */ if ((home = getenv("HOME"))) { strlcpy(curfile, home, sizeof curfile); strlcat(curfile, "/.scrc", sizeof curfile); if ((c = open(curfile, O_RDONLY)) > -1) { close(c); (void) readfile(curfile, 0); } } /* * Load ./.scrc if present and $HOME/.scrc contained `set scrc'. */ if (scrc && strcmp(home, getcwd(curfile, PATHLEN)) && (c = open(".scrc", O_RDONLY)) > -1) { close(c); (void) readfile(".scrc", 0); } #endif /* MSDOS */ *curfile = '\0'; FullUpdate++; } /* moves curcol back one displayed column */ void backcol(int arg) { while (--arg >= 0) { if (curcol) curcol--; else {error ("At column A"); break;} while(col_hidden[curcol] && curcol) curcol--; } rowsinrange = 1; colsinrange = fwidth[curcol]; } /* moves curcol forward one displayed column */ void forwcol(int arg) { while (--arg>=0) { if (curcol < maxcols - 1) curcol++; else if (!growtbl(GROWCOL, 0, arg)) /* get as much as needed */ break; else curcol++; while (col_hidden[curcol] && (curcol < maxcols - 1)) curcol++; } rowsinrange = 1; colsinrange = fwidth[curcol]; } /* moves currow forward one displayed row */ void forwrow(int arg) { while (--arg>=0) { if (currow < maxrows - 1) currow++; else if (!growtbl(GROWROW, arg, 0)) /* get as much as needed */ break; else currow++; while (row_hidden[currow]&&(currow=0) { if (currow) currow--; else {error("At row zero"); break;} while (row_hidden[currow] && currow) currow--; } rowsinrange = 1; colsinrange = fwidth[curcol]; } void markcell(void) { int c; error("Mark cell:"); if ((c=nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; return; } if ((c -= ('a' - 1)) < 1 || c > 26) { error("Invalid mark (must be a-z)"); return; } CLEAR_LINE; savedrow[c] = currow; savedcol[c] = curcol; savedstrow[c] = strow; savedstcol[c] = stcol; } void dotick(int tick) { int c; remember(0); error("Go to marked cell:"); if ((c = nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; return; } if (c == '`' || c == '\'') c = 0; else if (!(((c -= ('a' - 1)) > 0 && c < 27) || ((c += ('a' - '0' + 26)) > 26 && c < 37))) { error("Invalid mark (must be a-z, 0-9, ` or \')"); return; } if (savedrow[c] == -1) { error("Mark not set"); return; } CLEAR_LINE; currow = savedrow[c]; curcol = savedcol[c]; rowsinrange = 1; colsinrange = fwidth[curcol]; if (tick == '\'') { strow = savedstrow[c]; stcol = savedstcol[c]; gs.stflag = 1; } else gs.stflag = 0; remember(1); FullUpdate++; } void gotonote(void) { register struct ent *p; p = lookat(currow, curcol); if (p->nrow == -1) { error("No note attached"); } else moveto(p->nrow, p->ncol, p->nlastrow, p->nlastcol, -1, -1); } /* * Show a cell's label string or expression value. May overwrite value if * there is one already displayed in the cell. Created from old code in * update(), copied with minimal changes. */ void showstring(char *string, /* to display */ int dirflush, /* or rightflush or centered */ int hasvalue, /* is there a numeric value? */ int row, int col, /* spreadsheet location */ int *nextcolp, /* value returned through it */ int mxcol, /* last column displayed? */ int *fieldlenp, /* value returned through it */ int r, int c, /* screen row and column */ struct frange *fr, /* frame range we're currently in, if any */ int frightcols, /* number of frame columns to the right */ int flcols, int frcols) /* width of left and right sides of frame */ { int nextcol = *nextcolp; int fieldlen = *fieldlenp; char field[FBUFLEN]; int slen; char *start, *last; char *fp, *sp; struct ent *nc; struct crange *cr; cr = find_crange(row, col); /* This figures out if the label is allowed to slop over into the next blank field */ slen = strlen(string); for (sp = string; *sp != '\0'; sp++) if (*sp == '\\' && *(sp + 1) == '"') slen--; if (*string == '\\' && *(string+1) != '\0' && *(string+1) != '"') slen = fwidth[col]; if (c + fieldlen == rescol + flcols && nextcol < stcol) nextcol = stcol; if (frightcols && c + fieldlen + fwidth[nextcol] >= COLS - 1 - frcols && nextcol < fr->or_right->col - frightcols + 1) nextcol = fr->or_right->col - frightcols + 1; while ((slen > fieldlen) && (nextcol <= mxcol) && !((nc = lookat(row, nextcol))->flags & IS_VALID) && !(nc->label) && (cslop || find_crange(row, nextcol) == cr)) { if (!col_hidden[nextcol]) fieldlen += fwidth[nextcol]; nextcol++; if (c + fieldlen == rescol + flcols && nextcol < stcol) nextcol = stcol; if (frightcols && c + fieldlen + fwidth[nextcol] >= COLS - 1 - frcols && nextcol < fr->or_right->col - frightcols + 1) nextcol = fr->or_right->col - frightcols + 1; } if (slen > fieldlen) slen = fieldlen; /* Now justify and print */ start = (dirflush&IS_LEFTFLUSH) ? field : field + fieldlen - slen; if (dirflush & IS_LABEL) start = field + ((slenop) { case UPPER: case LOWER: case CAPITAL: case O_SCONST: case '#': case DATE: case FMT: case STINDEX: case EXT: case SVAL: case SUBSTR: return (STR); case '?': case IF: return (etype(e->e.o.right->e.o.left)); case 'f': return (etype(e->e.o.right)); case O_VAR: { register struct ent *p; p = e->e.v.vp; if (p->expr) return (p->flags & IS_STREXPR ? STR : NUM); else if (p->label) return (STR); else return (NUM); } default: return (NUM); } } /* return 1 if yes given, 0 otherwise */ int yn_ask(char *msg) { char ch; (void) move(0, 0); (void) clrtoeol(); (void) addstr(msg); (void) refresh(); while ((ch = nmgetch()) != 'y' && ch != 'Y' && ch != 'n' && ch != 'N') { if (ch == ctl('g') || ch == ESC) return (-1); } if (ch == 'y' || ch == 'Y') return (1); else return (0); } /* expand a ~ in a path to your home directory */ #if !defined(MSDOS) && !defined(VMS) #include #endif char * findhome(char *path, size_t pathsiz) { static char *HomeDir = NULL; if (*path == '~') { char *pathptr; char tmppath[PATHLEN]; if (HomeDir == NULL) { HomeDir = getenv("HOME"); if (HomeDir == NULL) HomeDir = "/"; } pathptr = path + 1; if ((*pathptr == '/') || (*pathptr == '\0')) strlcpy(tmppath, HomeDir, sizeof tmppath); #if !defined(MSDOS) && !defined(VMS) else { struct passwd *pwent; char *namep; char name[50]; namep = name; while ((*pathptr != '\0') && (*pathptr != '/')) *(namep++) = *(pathptr++); *namep = '\0'; if ((pwent = getpwnam(name)) == NULL) { error("Can't find user %s", name); return (NULL); } strlcpy(tmppath, pwent->pw_dir, sizeof tmppath); } #endif strlcat(tmppath, pathptr, sizeof tmppath); strlcpy(path, tmppath, pathsiz); } return (path); } /* * make a backup copy of a file, use the same mode and name in the format * [path/]file~ * return 1 if we were successful, 0 otherwise */ int backup_file(char *path) { struct stat statbuf; struct utimbuf timebuf; char fname[PATHLEN]; char tpath[PATHLEN]; #ifdef sequent static char *buf = NULL; static unsigned buflen = 0; #else char buf[BUFSIZ]; #endif char *tpp; int infd, outfd; int count; mode_t oldumask; /* tpath will be the [path/]file ---> [path/]file~ */ strlcpy(tpath, path, sizeof tpath); if ((tpp = strrchr(tpath, '/')) == NULL) tpp = tpath; else tpp++; strlcpy(fname, tpp, sizeof fname); snprintf(tpp, sizeof(tpath) - (tpp - tpath), "%s~", fname); if (stat(path, &statbuf) == 0) { #ifdef sequent /* if we know the optimum block size, use it */ if ((statbuf.st_blksize > buflen) || (buf == NULL)) { buflen = statbuf.st_blksize; if ((buf = scxrealloc(buf, buflen)) == (char *)0) { buflen = 0; return (0); } } #endif if ((infd = open(path, O_RDONLY, 0)) < 0) return (0); oldumask = umask(0); outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT, statbuf.st_mode); umask(oldumask); if (outfd < 0) return (0); chown(tpath, statbuf.st_uid, statbuf.st_gid); #ifdef sequent while ((count = read(infd, buf, statbuf.st_blksize)) > 0) { #else while ((count = read(infd, buf, sizeof(buf))) > 0) { #endif if (write(outfd, buf, count) != count) { count = -1; break; } } close(infd); close(outfd); /* copy access and modification times from original file */ timebuf.actime = statbuf.st_atime; timebuf.modtime = statbuf.st_mtime; utime(tpath, &timebuf); return ((count < 0) ? 0 : 1); } else if (errno == ENOENT) return (1); return (0); } sc-7.16_1.1.2/color.c000066400000000000000000000161171326046243500140700ustar00rootroot00000000000000 /* SC A Spreadsheet Calculator * Color manipulation routines * * Chuck Martin * Original Version Created: January, 2001 * * $Revision: 7.16 $ */ #include #include #include #include #include #include "compat.h" #include "sc.h" /* a linked list of free [struct ent]'s, uses .next as the pointer */ extern struct ent *freeents; struct colorpair *cpairs[8]; static struct crange *color_base; void initcolor(int colornum) { if (!colornum) { int i; for (i = 0; i < 8; i++) cpairs[i] = scxmalloc(sizeof(struct colorpair)); } /* default colors */ if (!colornum || colornum == 1) { cpairs[0]->fg = COLOR_WHITE; cpairs[0]->bg = COLOR_BLUE; cpairs[0]->expr = NULL; init_pair(1, cpairs[0]->fg, cpairs[0]->bg); } /* default for negative numbers */ if (!colornum || colornum == 2) { cpairs[1]->fg = COLOR_RED; cpairs[1]->bg = COLOR_WHITE; cpairs[1]->expr = NULL; init_pair(2, cpairs[1]->fg, cpairs[1]->bg); } /* default for cells with errors */ if (!colornum || colornum == 3) { cpairs[2]->fg = COLOR_WHITE; cpairs[2]->bg = COLOR_RED; cpairs[2]->expr = NULL; init_pair(3, cpairs[2]->fg, cpairs[2]->bg); } /* default for '*' marking cells with attached notes */ if (!colornum || colornum == 4) { cpairs[3]->fg = COLOR_BLACK; cpairs[3]->bg = COLOR_YELLOW; cpairs[3]->expr = NULL; init_pair(4, cpairs[3]->fg, cpairs[3]->bg); } if (!colornum || colornum == 5) { cpairs[4]->fg = COLOR_BLACK; cpairs[4]->bg = COLOR_CYAN; cpairs[4]->expr = NULL; init_pair(5, cpairs[4]->fg, cpairs[4]->bg); } if (!colornum || colornum == 6) { cpairs[5]->fg = COLOR_RED; cpairs[5]->bg = COLOR_CYAN; cpairs[5]->expr = NULL; init_pair(6, cpairs[5]->fg, cpairs[5]->bg); } if (!colornum || colornum == 7) { cpairs[6]->fg = COLOR_WHITE; cpairs[6]->bg = COLOR_BLACK; cpairs[6]->expr = NULL; init_pair(7, cpairs[6]->fg, cpairs[6]->bg); } if (!colornum || colornum == 8) { cpairs[7]->fg = COLOR_RED; cpairs[7]->bg = COLOR_BLACK; cpairs[7]->expr = NULL; init_pair(8, cpairs[7]->fg, cpairs[7]->bg); } if (color && has_colors()) color_set(1, NULL); } void change_color(int pair, struct enode *e) { int v; if ((--pair) < 0 || pair > 7) { error("Invalid color number"); return; } v = (int) eval(e); if (!cpairs[pair]) cpairs[pair] = scxmalloc(sizeof(struct colorpair)); cpairs[pair]->fg = v & 7; cpairs[pair]->bg = (v >> 3) & 7; cpairs[pair]->expr = e; if (color && has_colors()) init_pair(pair + 1, cpairs[pair]->fg, cpairs[pair]->bg); modflg++; FullUpdate++; } void add_crange(struct ent *r_left, struct ent *r_right, int pair) { struct crange *r; int minr, minc, maxr, maxc; minr = r_left->row < r_right->row ? r_left->row : r_right->row; minc = r_left->col < r_right->col ? r_left->col : r_right->col; maxr = r_left->row > r_right->row ? r_left->row : r_right->row; maxc = r_left->col > r_right->col ? r_left->col : r_right->col; if (!pair) { if (color_base) for (r = color_base; r; r = r->r_next) if ( (r->r_left->row == r_left->row) && (r->r_left->col == r_left->col) && (r->r_right->row == r_right->row) && (r->r_right->col == r_right->col)) { if (r->r_next) r->r_next->r_prev = r->r_prev; if (r->r_prev) r->r_prev->r_next = r->r_next; else color_base = r->r_next; scxfree((char *)r); modflg++; FullUpdate++; return; } error("Color range not defined"); return; } r = scxmalloc(sizeof(struct crange)); r->r_left = lookat(minr, minc); r->r_right = lookat(maxr, maxc); r->r_color = pair; r->r_next = color_base; r->r_prev = (struct crange *)0; if (color_base) color_base->r_prev = r; color_base = r; modflg++; FullUpdate++; } void clean_crange(void) { register struct crange *cr; register struct crange *nextcr; cr = color_base; color_base = (struct crange *)0; while (cr) { nextcr = cr->r_next; scxfree((char *)cr); cr = nextcr; } } struct crange * find_crange(int row, int col) { struct crange *r; if (color_base) for (r = color_base; r; r = r->r_next) { if ((r->r_left->row <= row) && (r->r_left->col <= col) && (r->r_right->row >= row) && (r->r_right->col >= col)) return r; } return 0; } void sync_cranges(void) { struct crange *cr; cr = color_base; while (cr) { cr->r_left = lookat(cr->r_left->row, cr->r_left->col); cr->r_right = lookat(cr->r_right->row, cr->r_right->col); cr = cr->r_next; } } void write_cranges(FILE *f) { register struct crange *r; register struct crange *nextr; for (r = nextr = color_base; nextr; r = nextr, nextr = r->r_next) /**/ ; while (r) { fprintf(f, "color %s", v_name(r->r_left->row, r->r_left->col)); fprintf(f, ":%s", v_name(r->r_right->row, r->r_right->col)); fprintf(f, " %d\n", r->r_color); r = r->r_prev; } } void write_colors(FILE *f, int indent) { int i, c = 0; for (i = 0; i < 8; i++) { if (cpairs[i] && cpairs[i]->expr) { snprintf(line, sizeof line, "color %d = ", i + 1); linelim = strlen(line); decompile(cpairs[i]->expr, 0); line[linelim] = '\0'; fprintf(f, "%*s%s\n", indent, "", line); if (brokenpipe) return; c++; } } if (indent && c) fprintf(f, "\n"); } void list_colors(FILE *f) { struct crange *r; struct crange *nextr; write_colors(f, 2); linelim = -1; if (brokenpipe) return; if (!are_colors()) { fprintf(f, " No color ranges"); return; } fprintf(f, " %-30s %s\n","Range", "Color"); if (!brokenpipe) fprintf(f, " %-30s %s\n","-----", "-----"); for (r = nextr = color_base; nextr; r = nextr, nextr = r->r_next) /* */ ; while (r) { fprintf(f, " %-32s %d\n", r_name(r->r_left->row, r->r_left->col, r->r_right->row, r->r_right->col), r->r_color); if (brokenpipe) return; r = r->r_prev; } } int are_colors(void) { return (color_base != 0); } void fix_colors(int row1, int col1, int row2, int col2, int delta1, int delta2) { int r1, c1, r2, c2; struct crange *cr, *ncr; struct frange *fr; fr = find_frange(currow, curcol); if (color_base) for (cr = color_base; cr; cr = ncr) { ncr = cr->r_next; r1 = cr->r_left->row; c1 = cr->r_left->col; r2 = cr->r_right->row; c2 = cr->r_right->col; if (!(fr && (c1 < fr->or_left->col || c1 > fr->or_right->col))) { if (r1 != r2 && r1 >= row1 && r1 <= row2) r1 = row2 - delta1; if (c1 != c2 && c1 >= col1 && c1 <= col2) c1 = col2 - delta1; } if (!(fr && (c2 < fr->or_left->col || c2 > fr->or_right->col))) { if (r1 != r2 && r2 >= row1 && r2 <= row2) r2 = row1 + delta2; if (c1 != c2 && c2 >= col1 && c2 <= col2) c2 = col1 + delta2; } if (r1 > r2 || c1 > c2 || (row1 >= 0 && row2 >= 0 && row1 <= r1 && row2 >= r2) || (col1 >= 0 && col2 >= 0 && col1 <= c1 && col2 >= c2)) { /* the 0 means delete color range */ add_crange(cr->r_left, cr->r_right, 0); } else { cr->r_left = lookat(r1, c1); cr->r_right = lookat(r2, c2); } } } sc-7.16_1.1.2/compat.c000066400000000000000000000016551326046243500142360ustar00rootroot00000000000000/* Carsten Kunze, 2016 */ #include #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t dstsize) { size_t srcsize; /* Not conform to strlcpy, but avoids to access illegal memory in case * of unterminated strings */ for (srcsize = 0; srcsize < dstsize; srcsize++) if (!src[srcsize]) break; if (dstsize > srcsize) dstsize = srcsize; else if (dstsize) dstsize--; if (dstsize) /* assumes non-overlapping buffers */ memcpy(dst, src, dstsize); dst[dstsize] = 0; return srcsize; } #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t dstsize) { size_t ld, ls; for (ld = 0; ld < dstsize - 1; ld++) if (!dst[ld]) break; dst += ld; dstsize -= ld; for (ls = 0; ls < dstsize; ls++) if (!src[ls]) break; if (dstsize > ls) dstsize = ls; else if (dstsize) dstsize--; if (dstsize) memcpy(dst, src, dstsize); dst[dstsize] = 0; return ld + ls; } #endif sc-7.16_1.1.2/configure000077500000000000000000000310761326046243500145160ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2016-2017, Carsten Kunze # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. usage () { echo "Usage: $0 [-s]" echo " -s Silence output" exit $1 } SFLAG= MAKE= DEFS= LIB_LEX= LIB_CURSES= cat /dev/null > compat.h while [ $# -gt 0 ]; do case $1 in -s) SFLAG=1;; *) echo "$0: $1: Unknown option" >&2 usage 1;; esac shift done check_for () { [ -e $LOG ] && cat <>$LOG ================================================================================ EOT A="Checking for $1 ... " printf "$A\n\n" >>$LOG [ -z "$SFLAG" ] && printf "$A" } compile () { rm -f ${TMPNAM}.o $TMPNAM $OUT $ERR $MAKE -f $OUTMK $TMPNAM > $OUT 2> $ERR RESULT=$? cat $OUT $ERR >> $LOG if [ $RESULT -eq 0 ]; then true; else false; fi } test_result () { RESULT=$? RESULT_TEXT=${1:-0} # 1: omit "no", 2: say nothing if [ $RESULT -eq 0 ]; then echo success >>$LOG [ -z "$SFLAG" -a $RESULT_TEXT -lt 2 ] && \ echo "yes$PASS_TEXT" PASS_TEXT= [ -e $TMPC ] && rm -f $TMPC true else [ -z "$SFLAG" -a $RESULT_TEXT -lt 1 ] && echo no if [ -e $TMPC ]; then echo "Failed program:" >>$LOG pr -n -t $TMPC >>$LOG rm -f $TMPC fi false fi } gen_mk () { [ $# -eq 0 ] && rm -f $OUTMK [ -n "$LEX" ] && echo "LEX=$LEX" >> $OUTMK [ -n "$FLOAT_STORE" ] && echo "FLOAT_STORE=$FLOAT_STORE" >> $OUTMK [ -n "$DEFS" ] && echo "DEFINES=$DEFS" >> $OUTMK [ -n "$INCDIR_CURSES" ] && echo "INCDIR_CURSES=$INCDIR_CURSES" >> $OUTMK [ -n "$RPATH_CURSES" ] && echo "RPATH_CURSES=$RPATH_CURSES" >> $OUTMK [ -n "$LIBDIR_CURSES" ] && echo "LIBDIR_CURSES=$LIBDIR_CURSES" \ >> $OUTMK [ -n "$LIB_CURSES" ] && echo "LIB_CURSES=$LIB_CURSES" >> $OUTMK [ -n "$LIB_AVLBST" ] && echo "LIB_AVLBST=$LIB_AVLBST" >> $OUTMK [ -n "$LIB_LEX" ] && echo "LIB_LEX=$LIB_LEX" >> $OUTMK [ -n "$__CDBG" ] && echo "__CDBG=$__CDBG" >> $OUTMK [ -n "$__CXXDBG" ] && echo "__CXXDBG=$__CXXDBG" >> $OUTMK [ -n "$__CLDBG" ] && echo "__CLDBG=$__CLDBG" >> $OUTMK [ -n "$__CLXXDBG" ] && echo "__CLXXDBG=$__CLXXDBG" >> $OUTMK cat $INMK >> $OUTMK || exit 1 } check_make () { check_for "make(1)" cat <$TMPMK all: true EOT make -f $TMPMK >> $LOG 2>&1 test_result && { MAKE=make return } echo "Failed makefile:" >>$LOG pr -n -t $TMPMK >>$LOG check_for "bmake(1)" cat <$TMPMK all: true EOT bmake -f $TMPMK >> $LOG 2>&1 test_result && MAKE=bmake } check_Sanitizer () { check_for "CC Sanitizer" __CDBG= __CLDBG= __CCXXDBG= __CXXDBG= __CLXXDBG= __CDBG="$__CDBG -Wmissing-prototypes" __CDBG="$__CDBG -Wstrict-prototypes" __CCXXDBG="$__CCXXDBG -g -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls" __CCXXDBG="$__CCXXDBG -Wall" __CCXXDBG="$__CCXXDBG -Wextra" __CCXXDBG="$__CCXXDBG -Wsign-compare" __CCXXDBG="$__CCXXDBG -Wcast-align" __CCXXDBG="$__CCXXDBG -Wcast-qual" __CCXXDBG="$__CCXXDBG -Wunused-parameter" __CCXXDBG="$__CCXXDBG -Wunused-function" __CCXXDBG="$__CCXXDBG -Wshadow" __CCXXDBG="$__CCXXDBG -Wwrite-strings" __CLXXDBG="$__CLXXDBG -fno-common -fsanitize=address" [ -n "$CC" ] || CC=cc VER=`$CC --version` if echo $VER | grep -iq gcc || echo $VER | \ grep -iq 'Free Software Foundation'; then [ -z "$SFLAG" ] && printf "(gcc) " #__CLDBG="$__CLDBG -fprofile-arcs -ftest-coverage" __CLDBG="$__CLDBG -fsanitize=undefined" __CLDBG="$__CLDBG -fsanitize=float-divide-by-zero" __CLDBG="$__CLDBG -fsanitize=float-cast-overflow" elif echo $VER | grep -q clang; then [ -z "$SFLAG" ] && printf "(clang) " __CCXXDBG="$__CCXXDBG -Wincompatible-pointer-types-discards-qualifiers" __CCXXDBG="$__CCXXDBG -Wmissing-variable-declarations" __CXXDBG="$__CXXDBG -Wunused-private-field" __CLXXDBG="$__CLXXDBG -fsanitize=undefined" __CLXXDBG="$__CLXXDBG -fsanitize=unsigned-integer-overflow" else [ -z "$SFLAG" ] && echo "Unknown compiler" return fi # Most C++ options can be used for C too __CDBG="$__CDBG $__CCXXDBG" __CLDBG="$__CLDBG $__CLXXDBG" __CXXDBG="$__CXXDBG $__CCXXDBG" cat <$TMPC int main() { return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(__CDBG) \$(__CLDBG) -o \$@ ${TMPNAM}.o EOT compile test_result || { __CLDBG= __CLXXDBG= } } check_float_store () { FLOAT_STORE=-ffloat-store check_for "$FLOAT_STORE" cat <$TMPC int main() { return 0; } EOT gen_mk cat <>$OUTMK ${TMPNAM}.o: \$(CC) $FLOAT_STORE -c $TMPC EOT rm -f ${TMPNAM}.o $MAKE -f $OUTMK ${TMPNAM}.o >> $LOG 2> $TMPNAM cat $TMPNAM >> $LOG if [ -s $TMPNAM ]; then if grep -e $FLOAT_STORE $TMPNAM | grep -q 'not supported' then false elif grep -e $FLOAT_STORE $TMPNAM | grep -q 'argument unused' then false else true fi else true fi test_result || FLOAT_STORE= } check_strlcpy () { check_for "strlcpy(3)" cat <$TMPC #include int main(int argc, char **argv) { char a[10]; (void)argc; strlcpy(a, *argv, sizeof a); return 0; } EOT compile if test_result; then DEFS="$DEFS -DHAVE_STRLCPY" else H=compat.h grep -q '' $H 2>/dev/null || cat <>$H #include EOT cat <>$H size_t strlcpy(char *, const char *, size_t); EOT fi } check_strlcat () { check_for "strlcat(3)" cat <$TMPC #include int main(int argc, char **argv) { char a[10]; (void)argc; *a = 0; strlcat(a, *argv, sizeof a); return 0; } EOT compile if test_result; then DEFS="$DEFS -DHAVE_STRLCAT" else H=compat.h grep -q '' $H 2>/dev/null || cat <>$H #include EOT cat <>$H size_t strlcat(char *, const char *, size_t); EOT fi } check_fixterm () { check_for 'fixterm(3)' cat <$TMPC #include "compat.h" int main() { fixterm(); return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) \$(_LDFLAGS) \$(RPATH_CURSES) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result && DEFS="$DEFS -DHAVE_FIXTERM" } check_resetterm () { check_for 'resetterm(3)' cat <$TMPC #include "compat.h" int main() { resetterm(); return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) \$(_LDFLAGS) \$(RPATH_CURSES) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result && DEFS="$DEFS -DHAVE_RESETTERM" } compile_curses () { cat <$TMPC #include "compat.h" int main() { return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) \$(_LDFLAGS) \$(RPATH_CURSES) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result 1 } test_curses () { compile_curses && return [ $# -gt 0 ] && for i in $*; do compile_curses $i && return done } check_ncursesw () { LIB_CURSES="-lncursesw" check_for $LIB_CURSES ODEFS=$DEFS # At first test for specific ncursesw.h to not used -lncursesw with # traditional curses.h if both curses systems are installed. DEFS="$DEFS -DHAVE_NCURSESW_CURSES_H" INCDIR_CURSES="-I/usr/include/ncurses6" RPATH_CURSES="-Wl,-rpath,/usr/lib64/ncurses6" LIBDIR_CURSES="-L/usr/lib64/ncurses6" PASS_TEXT=" (#include , -I/usr/include/ncurses6, \ -L/usr/lib64/ncurses6)" test_curses && return # Linux NC6 INCDIR_CURSES= RPATH_CURSES= LIBDIR_CURSES= PASS_TEXT=" (#include )" test_curses && return # Linux INCDIR_CURSES="-I/usr/pkg/include" RPATH_CURSES="-Wl,-rpath,/usr/pkg/lib" LIBDIR_CURSES="-L/usr/pkg/lib" PASS_TEXT=" (#include , -I/usr/pkg/include, \ -L/usr/pkg/lib)" test_curses && return # NetBSD DEFS=$ODEFS INCDIR_CURSES= RPATH_CURSES= LIBDIR_CURSES= PASS_TEXT= test_curses && return # OpenBSD, FreeBSD [ -z "$SFLAG" ] && echo no false } check_ncurses () { LIB_CURSES="-lncurses" check_for $LIB_CURSES ODEFS=$DEFS DEFS="$DEFS -DHAVE_NCURSES_CURSES_H" PASS_TEXT=" (#include )" test_curses && return # OpenIndiana DEFS="$DEFS -DHAVE_NCURSES_CURSES_H" INCDIR_CURSES="-I/usr/pkg/include" RPATH_CURSES="-Wl,-rpath,/usr/pkg/lib" LIBDIR_CURSES="-L/usr/pkg/lib" PASS_TEXT=" (#include , -I/usr/pkg/include, \ -L/usr/pkg/lib)" test_curses && return # NetBSD DEFS=$ODEFS INCDIR_CURSES= RPATH_CURSES= LIBDIR_CURSES= PASS_TEXT= test_curses && return [ -z "$SFLAG" ] && echo no false } check_curses () { LIB_CURSES="-lcurses" check_for $LIB_CURSES test_curses || { [ -z "$SFLAG" ] && echo no false return } check_for attr_t cat <$TMPC #include "compat.h" int main(int argc, char **argv) { (void)argv; attr_t c = argc + '0'; return (int)c; } EOT compile test_result && DEFS="$DEFS -DHAVE_ATTR_T" check_for 'attr_get(3)' cat <$TMPC #include "compat.h" int main() { return attr_get(NULL, NULL, NULL); } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result && DEFS="$DEFS -DHAVE_ATTR_GET" check_fixterm check_resetterm true } curses_mk_compat_h () { cat <>compat.h #ifndef _XOPEN_SOURCE_EXTENDED # define _XOPEN_SOURCE_EXTENDED #endif #if defined(HAVE_NCURSESW_CURSES_H) # include #elif defined(HAVE_NCURSES_CURSES_H) # include #else # include # ifndef false # define false 0 # endif # ifndef true # define true 1 # endif #endif EOT } check_lib_curses () { curses_mk_compat_h DEFS0=$DEFS DEFS="$DEFS -DHAVE_ATTR_T -DHAVE_ATTR_GET -DHAVE_FIXTERM \ -DHAVE_RESETTERM" check_ncursesw && return check_ncurses && return DEFS=$DEFS0 check_curses && return } check_netbsd_curses () { curses_mk_compat_h [ `uname` = "NetBSD" ] && check_curses && return DEFS0=$DEFS DEFS="$DEFS -DHAVE_ATTR_T -DHAVE_ATTR_GET -DHAVE_FIXTERM \ -DHAVE_RESETTERM" check_ncursesw && return check_ncurses && return DEFS=$DEFS0 check_curses && return } check_curses_wch () { check_for "wins_wch(3)" cat <$TMPC #include "compat.h" int main() { cchar_t c; wins_wch(stdscr, &c); return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) \$(_LDFLAGS) \$(RPATH_CURSES) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result || return; DEFS="$DEFS -DHAVE_CURSES_WCH" } check_curses_keyname () { check_for "keyname(3)" cat <$TMPC #include "compat.h" int main(int argc, char **argv) { (void)argv; puts(keyname(argc)); return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) \$(_LDFLAGS) \$(RPATH_CURSES) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result || return; DEFS="$DEFS -DHAVE_CURSES_KEYNAME" } check_isfinite () { check_for "isfinite(3)" cat <$TMPC #include int main(int argc, char **argv) { (void)argv; return isfinite((double)argc); } EOT compile test_result && { DEFS="$DEFS -DHAVE_ISFINITE" return } check_for "finite(3)" cat <$TMPC #include int main(int argc, char **argv) { (void)argv; return finite((double)argc); } EOT compile test_result 2 && { [ -s $ERR ] || grep -iq implicite $ERR || grep -iq warning $ERR || { [ -z "$SFLAG" ] && echo yes return } } cat <$TMPC #include #include int main(int argc, char **argv) { (void)argv; return finite(argc); } EOT PASS_TEXT=" (#include )" compile test_result && DEFS="$DEFS -DUSE_IEEEFP_H" PASS_TEXT= } check_sc_attr_get () { check_for "sc(1) attr_get(3)" cat <$TMPC #include #include "compat.h" #include "sc.h" int main() { attr_get(NULL, NULL, NULL); return 0; } EOT gen_mk cat <>$OUTMK $TMPNAM: ${TMPNAM}.o \$(CC) \$(_CFLAGS) -o \$@ ${TMPNAM}.o \$(LDADD) EOT compile test_result || DEFS="$DEFS -DNO_ATTR_GET" } check_stdbool_h () { check_for "" cat <$TMPC #include int main() { return 0; } EOT compile test_result && DEFS="$DEFS -DHAVE_STDBOOL_H" } [ ! -s compat.h ] && rm compat.h OUTMK=Makefile INMK=${OUTMK}.in CFG=config TMPNAM=.$CFG TMPMK=${TMPNAM}.mk TMPC=${TMPNAM}.c OUT=${TMPNAM}.out ERR=${TMPNAM}.err LOG=${CFG}.log rm -f $LOG gen_mk check_make #check_Sanitizer check_float_store check_strlcpy check_strlcat check_lib_curses check_curses_wch check_curses_keyname check_isfinite check_sc_attr_get check_stdbool_h gen_mk rm -f $TMPNAM* echo "#define _COMPAT_H" >>compat.h sc-7.16_1.1.2/crypt.c000066400000000000000000000075661326046243500141230ustar00rootroot00000000000000/* * Encryption utilites * Bradley Williams * {allegra,ihnp4,uiucdcs,ctvax}!convex!williams * $Revision: 7.16 $ */ #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) #include #include #include #include #include "compat.h" #include "sc.h" int Crypt = 0; #define MAXKEYWORDSIZE 30 char KeyWord[MAXKEYWORDSIZE] = {""}; void creadfile(char *save, int eraseflg) { register FILE *f; int pipefd[2]; int fildes; int pid; if (eraseflg && strcmp(save, curfile) && modcheck(" first")) return; if ((fildes = open(findhome(save), O_RDONLY, 0)) < 0) { error ("Can't read file \"%s\"", save); return; } if (eraseflg) erasedb(); if (pipe(pipefd) < 0) { error("Can't make pipe to child"); return; } deraw(1); strlcpy(KeyWord, getpass("Enter key:"), sizeof KeyWord); goraw(); if ((pid=fork()) == 0) { /* if child */ (void) close(0); /* close stdin */ (void) close(1); /* close stdout */ (void) close(pipefd[0]); /* close pipe input */ (void) dup(fildes); /* standard in from file */ (void) dup(pipefd[1]); /* connect to pipe */ (void) fprintf(stderr, " "); (void) execl(CRYPT_PATH, "crypt", KeyWord, 0); (void) fprintf(stderr, "execl(%s, \"crypt\", %s, 0) in creadfile() failed", CRYPT_PATH, KeyWord); exit(-127); } else { /* else parent */ (void) close(fildes); (void) close(pipefd[1]); /* close pipe output */ if ((f = fdopen(pipefd[0], "r")) == (FILE *)0) { (void) kill(pid, 9); error("Can't fdopen file \"%s\"", save); (void)close(pipefd[0]); return; } } loading++; while (fgets(line, sizeof(line), f)) { linelim = 0; if (line[0] != '#') (void) yyparse(); } --loading; (void) fclose(f); (void) close(pipefd[0]); while (pid != wait(&fildes)) /**/; linelim = -1; if (eraseflg) { strlcpy(curfile, save, sizeof curfile); modflg = 0; } } int cwritefile(char *fname, int r0, int c0, int rn, int cn) { register FILE *f; int pipefd[2]; int fildes; int pid; char save[PATHLEN]; char *fn; char *busave; if (*fname == '\0') fname = &curfile[0]; fn = fname; while (*fn && (*fn == ' ')) /* Skip leading blanks */ fn++; if (*fn == '|') { error("Can't have encrypted pipe"); return (-1); } strlcpy(save, fname, sizeof save); busave = findhome(save); if (dobackups && !backup_file(busave) && (yn_ask("Could not create backup copy, Save anyway?: (y,n)") != 1)) return (0); if ((fildes = open (busave, O_TRUNC|O_WRONLY|O_CREAT, 0600)) < 0) { error("Can't create file \"%s\"", save); return (-1); } if (pipe(pipefd) < 0) { error("Can't make pipe to child\n"); return (-1); } if (KeyWord[0] == '\0') { deraw(1); strlcpy(KeyWord, getpass("Enter key:"), sizeof KeyWord); goraw(); } if ((pid=fork()) == 0) { /* if child */ (void) close(0); /* close stdin */ (void) close(1); /* close stdout */ (void) close(pipefd[1]); /* close pipe output */ (void) dup(pipefd[0]); /* connect to pipe input */ (void) dup(fildes); /* standard out to file */ (void) fprintf(stderr, " "); (void) execl(CRYPT_PATH, "crypt", KeyWord, 0); (void) fprintf(stderr, "execl(%s, \"crypt\", %s, 0) in cwritefile() failed", CRYPT_PATH, KeyWord); exit (-127); } else { /* else parent */ (void) close(fildes); (void) close(pipefd[0]); /* close pipe input */ f = fdopen(pipefd[1], "w"); if (f == 0) { (void) kill(pid, -9); error("Can't fdopen file \"%s\"", save); (void) close(pipefd[1]); return (-1); } } write_fd(f, r0, c0, rn, cn); (void) fclose(f); (void) close(pipefd[1]); while (pid != wait(&fildes)) /**/; strlcpy(curfile, save, sizeof curfile); modflg = 0; error("File \"%s\" written (encrypted).", curfile); return (0); } #endif /* CRYPT_PATH */ sc-7.16_1.1.2/eres.sed000066400000000000000000000000661326046243500142350ustar00rootroot00000000000000/%token.*K_/!d /%token.*K_\(.*\)/s// { "\1", K_\1 },/ sc-7.16_1.1.2/format.c000066400000000000000000000374571326046243500142540ustar00rootroot00000000000000/***************************************************************************** * * Mark Nagel * 20 July 1989 * * $Revision: 7.16 $ * * bool * format(fmt, precision, num, buf, buflen) * char *fmt; * double num; * char buf[]; * int buflen; * * The format function will produce a string representation of a number * given a _format_ (described below) and a double value. The result is * written into the passed buffer -- if the resulting string is too * long to fit into the passed buffer, the function returns false. * Otherwise the function returns true. * * The fmt parameter contains the format to use to convert the number. * * # Digit placeholder. If the number has fewer digits on either * side of the decimal point than there are '#' characters in * the format, the extra '#' characters are ignored. The number * is rounded to the number of digit placeholders as there are * to the right of the decimal point. If there are more digits * in the number than there are digit placeholders on the left * side of the decimal point, then those digits are displayed. * * 0 Digit placeholder. Same as for '#' except that the number * is padded with zeroes on either side of the decimal point. * The number of zeroes used in padding is determined by the * number of digit placeholders after the '0' for digits on * the left side of the decimal point and by the number of * digit placeholders before the '0' for digits on the right * side of the decimal point. * * . Decimal point. Determines how many digits are placed on * the right and left sides of the decimal point in the number. * Note that numbers smaller than 1 will begin with a decimal * point if the left side of the decimal point contains only * a '#' digit placeholder. Use a '0' placeholder to get a * leading zero in decimal formats. * * % Percentage. For each '%' character in the format, the actual * number gets multiplied by 100 (only for purposes of formatting * -- the original number is left unmodified) and the '%' character * is placed in the same position as it is in the format. * * , Thousands separator. The presence of a ',' in the format * (multiple commas are treated as one) will cause the number * to be formatted with a ',' separating each set of three digits * in the integer part of the number with numbering beginning * from the right end of the integer. * * & Precision. When this character is present in the fractional * part of the number, it is equavalent to a number of 0's equal * to the precision specified in the column format command. For * example, if the precision is 3, "&" is equivalent to "000". * * \ Quote. This character causes the next character to be * inserted into the formatted string directly with no * special interpretation. * * E- E+ e- e+ * Scientific format. Causes the number to formatted in scientific * notation. The case of the 'E' or 'e' given is preserved. If * the format uses a '+', then the sign is always given for the * exponent value. If the format uses a '-', then the sign is * only given when the exponent value is negative. Note that if * there is no digit placeholder following the '+' or '-', then * that part of the formatted number is left out. In general, * there should be one or more digit placeholders after the '+' * or '-'. * * ; Format selector. Use this character to separate the format * into two distinct formats. The format to the left of the * ';' character will be used if the number given is zero or * positive. The format to the right of the ';' character is * used if the number given is negative. * * Any * Self insert. Any other character will be inserted directly * into the formatted number with no change made to the actual * number. * *****************************************************************************/ /*****************************************************************************/ #include #include #include #include #include #include "compat.h" #include "sc.h" #define EOS '\0' #define MAXBUF 256 static char *fmt_int(char *val, char *fmt, bool comma, bool negative); static char *fmt_frac(char *val, char *fmt, int lprecision); static char *fmt_exp(int val, char *fmt); static void reverse(register char *buf); char *colformat[COLFORMATS]; /*****************************************************************************/ bool format(char *fmt, int lprecision, double val, char *buf, size_t buflen) { register char *cp; char *tmp, *tp; bool comma = false, negative = false; char *integer = NULL, *decimal = NULL; char *exponent = NULL; int exp_val = 0; int width; char prtfmt[32]; static char *mantissa = NULL; static char *tmpfmt1 = NULL, *tmpfmt2 = NULL, *exptmp = NULL; static unsigned mantlen = 0, fmtlen = 0; char *fraction = NULL; int zero_pad = 0; if (fmt == NULL) return(true); if (strlen(fmt) + 1 > fmtlen) { fmtlen = strlen(fmt) + 40; tmpfmt1 = scxrealloc(tmpfmt1, fmtlen); tmpfmt2 = scxrealloc(tmpfmt2, fmtlen); exptmp = scxrealloc(exptmp, fmtlen); } strlcpy(tmpfmt1, fmt, fmtlen); fmt = tmpfmt1; if (buflen + 1 > mantlen) { mantlen = buflen + 40; mantissa = scxrealloc(mantissa, mantlen); } /* * select positive or negative format if necessary */ for (cp = fmt; *cp != ';' && *cp != EOS; cp++) { if (*cp == '\\') cp++; } if (*cp == ';') { if (val < 0.0) { val = -val; /* format should provide sign if desired */ fmt = cp + 1; } else *cp = EOS; } /* * extract other information from format and produce a * format string stored in tmpfmt2 also scxmalloc()'d above */ tmp = tmpfmt2; for (cp = fmt, tp = tmp; *cp != EOS; cp++) { switch (*cp) { case '\\': *tp++ = *cp++; *tp++ = *cp; break; case ',': comma = true; break; case '.': if (decimal == NULL) decimal = tp; *tp++ = *cp; break; case '%': val *= 100.0; *tp++ = *cp; break; default: *tp++ = *cp; break; } } *tp = EOS; fmt = tmpfmt2; /* The following line was necessary due to problems with the gcc * compiler and val being a negative zero. Thanks to Mike Novack for * the suggestion. - CRM */ val = (val + 1.0) - 1.0; if (val < 0.0) { negative = true; val = -val; } /* * extract the exponent from the format if present */ for (cp = fmt; *cp != EOS; cp++) { if (*cp == '\\') cp++; else if (*cp == 'e' || *cp == 'E') { if (cp[1] == '+' || cp[1] == '-') { strlcpy(exptmp, cp, fmtlen); exponent = exptmp; *cp = EOS; if (val != 0.0) { while (val < 1.0) { val *= 10.0; exp_val--; } while (val >= 10.0) { val /= 10.0; exp_val++; } } break; } } } /* * determine maximum decimal places and use sprintf * to build initial character form of formatted value. */ width = 0; if (decimal) { *decimal++ = EOS; for (cp = decimal; *cp != EOS; cp++) { switch (*cp) { case '\\': cp++; break; case '#': width++; break; case '0': zero_pad = ++width; break; case '&': width += lprecision; zero_pad = width; break; } } zero_pad = strlen(decimal) - zero_pad; } snprintf(prtfmt, sizeof prtfmt, "%%.%dlf", width); snprintf(mantissa, mantlen, prtfmt, val); for (cp = integer = mantissa; *cp != dpoint && *cp != EOS; cp++) { if (*integer == '0') integer++; } if (*cp == dpoint) { fraction = cp + 1; *cp = EOS; cp = fraction + strlen(fraction) - 1; for (; zero_pad > 0; zero_pad--, cp--) { if (*cp == '0') *cp = EOS; else break; } } else fraction = ""; /* * format the puppy */ { static char *citmp = NULL, *cftmp = NULL; static unsigned cilen = 0, cflen = 0; char *ci, *cf, *ce; unsigned int len_ci, len_cf, len_ce; bool ret = false; ci = fmt_int(integer, fmt, comma, negative); len_ci = strlen(ci); if (len_ci >= cilen) { cilen = len_ci + 40; citmp = scxrealloc(citmp, cilen); } strlcpy(citmp, ci, cilen); ci = citmp; cf = decimal ? fmt_frac(fraction, decimal, lprecision) : ""; len_cf = strlen(cf); if (len_cf >= cflen) { cflen = len_cf + 40; cftmp = scxrealloc(cftmp, cilen); } strlcpy(cftmp, cf, cilen); cf = cftmp; ce = (exponent) ? fmt_exp(exp_val, exponent) : ""; len_ce = strlen(ce); /* * Skip copy assuming sprintf doesn't call our format functions * ce = strcpy(scxmalloc((unsigned)((len_ce = strlen(ce)) + 1)), ce); */ if (len_ci + len_cf + len_ce < buflen) { snprintf(buf, buflen, "%s%s%s", ci, cf, ce); ret = true; } return (ret); } } /*****************************************************************************/ static char * fmt_int(char *val, /* integer part of the value to be formatted */ char *fmt, /* integer part of the format */ bool comma, /* true if we should comma-ify the value */ bool negative) /* true if the value is actually negative */ { int digit, f, v; int thousands = 0; char *cp; static char buf[MAXBUF]; char *bufptr = buf; /* * locate the leftmost digit placeholder */ for (cp = fmt; *cp != EOS; cp++) { if (*cp == '\\') cp++; else if (*cp == '#' || *cp == '0') break; } digit = (*cp == EOS) ? -1 : cp - fmt; /* * format the value */ f = strlen(fmt) - 1; v = (digit >= 0) ? (ssize_t)strlen(val) - 1 : -1; while (f >= 0 || v >= 0) { if (f > 0 && fmt[f-1] == '\\') { *bufptr++ = fmt[f--]; } else if (f >= 0 && (fmt[f] == '#' || fmt[f] == '0')) { if (v >= 0 || fmt[f] == '0') { *bufptr++ = v < 0 ? '0' : val[v]; if (comma && (thousands = (thousands + 1) % 3) == 0 && v > 0 && thsep != '\0') *bufptr++ = thsep; v--; } } else if (f >= 0) { *bufptr++ = fmt[f]; } if (v >= 0 && f == digit) { continue; } f--; } if (negative && digit >= 0) *bufptr++ = '-'; *bufptr = EOS; reverse(buf); return (buf); } /*****************************************************************************/ static char * fmt_frac(char *val, /* fractional part of the value to be formatted */ char *fmt, /* fractional portion of format */ int lprecision) /* precision, for interpreting the "&" */ { static char buf[MAXBUF]; register char *bufptr = buf; register char *fmtptr = fmt, *valptr = val; *bufptr++ = dpoint; while (*fmtptr != EOS) { if (*fmtptr == '&') { int i; for (i = 0; i < lprecision; i++) *bufptr++ = (*valptr != EOS) ? *valptr++ : '0'; } else if (*fmtptr == '\\') *bufptr++ = *++fmtptr; else if (*fmtptr == '#' || *fmtptr == '0') { if (*valptr != EOS || *fmtptr == '0') *bufptr++ = (*valptr != EOS) ? *valptr++ : '0'; } else *bufptr++ = *fmtptr; fmtptr++; } *bufptr = EOS; if (buf[1] < '0' || buf[1] > '9') return (buf + 1); else return (buf); } /*****************************************************************************/ static char * fmt_exp(int val, /* value of the exponent */ char *fmt) /* exponent part of the format */ { static char buf[MAXBUF]; register char *bufptr = buf; char valbuf[64]; bool negative = false; *bufptr++ = *fmt++; if (*fmt == '+') *bufptr++ = (val < 0) ? '-' : '+'; else if (val < 0) *bufptr++ = '-'; fmt++; *bufptr = EOS; if (val < 0) { val = -val; negative = false; } snprintf(valbuf, sizeof valbuf, "%d", val); strlcat(buf, fmt_int(valbuf, fmt, false, negative), sizeof buf); return (buf); } /*****************************************************************************/ static void reverse(register char *buf) { register char *cp = buf + strlen(buf) - 1; register char tmp; while (buf < cp) { tmp = *cp; *cp-- = *buf; *buf++ = tmp; } } /*****************************************************************************/ /* * Tom Anderson * 10/14/90 * * This routine takes a value and formats it using fixed, scientific, * or engineering notation. The format command 'f' determines which * format is used. The formats are: example * 0: Fixed point (default) 0.00010 * 1: Scientific 1.00E-04 * 2: Engineering 100.00e-06 * * The format command 'f' now uses three values. The first two are the * width and precision, and the last one is the format value 0, 1, or 2 as * described above. The format value is passed in the variable fmt. * * This formatted value is written into the passed buffer. if the * resulting string is too long to fit into the passed buffer, the * function returns false. Otherwise the function returns true. * * When a number is formatted as engineering and is outside of the range, * the format reverts to scientific. * * To preserve compatability with old spreadsheet files, the third value * may be missing, and the default will be fixed point (format 0). * * When an old style sheet is saved, the third value will be stored. * */ /* defined in sc.h */ #ifndef REFMTFIX #define REFMTFIX 0 #define REFMTFLT 1 #define REFMTENG 2 #define REFMTDATE 3 #define REFMTLDATE 4 #endif bool engformat(int fmt, int width, int lprecision, double val, char *buf, int buflen) { static char *engmult[] = { "-18", "-15", "-12", "-09", "-06", "-03", "+00", "+03", "+06", "+09", "+12", "+15", "+18" }; int engind = 0; double engmant, pow(), engabs, engexp; if (buflen < width) return (false); if (fmt >= 0 && fmt < COLFORMATS && colformat[fmt]) return (format(colformat[fmt], lprecision, val, buf, buflen)); if (fmt == REFMTFIX) snprintf(buf, buflen, "%*.*f", width, lprecision, val); if (fmt == REFMTFLT) snprintf(buf, buflen, "%*.*E", width, lprecision, val); if (fmt == REFMTENG) { if (val == 0e0) { /* Hack to get zeroes to line up in engr fmt */ snprintf(buf - 1, buflen, "%*.*f ", width, lprecision, val); } else { engabs = (val); if ( engabs < 0e0) engabs = -engabs; if ((engabs >= 1e-18) && (engabs < 1e-15)) engind=0; if ((engabs >= 1e-15) && (engabs < 1e-12)) engind=1; if ((engabs >= 1e-12) && (engabs < 1e-9 )) engind=2; if ((engabs >= 1e-9) && (engabs < 1e-6 )) engind=3; if ((engabs >= 1e-6) && (engabs < 1e-3 )) engind=4; if ((engabs >= 1e-3) && (engabs < 1 )) engind=5; if ((engabs >= 1) && (engabs < 1e3 )) engind=6; if ((engabs >= 1e3) && (engabs < 1e6 )) engind=7; if ((engabs >= 1e6) && (engabs < 1e9 )) engind=8; if ((engabs >= 1e9) && (engabs < 1e12 )) engind=9; if ((engabs >= 1e12) && (engabs < 1e15 )) engind=10; if ((engabs >= 1e15) && (engabs < 1e18 )) engind=11; if ((engabs >= 1e18) && (engabs < 1e21 )) engind=12; if ((engabs < 1e-18) || (engabs >= 1e21 )) { /* Revert to floating point */ snprintf(buf, buflen, "%*.*E", width, lprecision, val); } else { engexp = (double) (engind-6)*3; engmant = val/pow(10.0e0,engexp); snprintf(buf, buflen, "%*.*fe%s", width-4, lprecision, engmant, engmult[engind]); } } } if (fmt == REFMTDATE) { int i; time_t secs; if (buflen < 9) { for (i = 0; i < width; i++) buf[i] = '*'; buf[i] = '\0'; } else { secs = (time_t)val; strftime(buf,buflen,"%e %b %y",localtime(&secs)); for (i = 9; i < width; i++) buf[i] = ' '; buf[i] = '\0'; } } if (fmt == REFMTLDATE) { int i; time_t secs; if (buflen < 11) { for (i = 0; i < width; i++) buf[i] = '*'; buf[i] = '\0'; } else { secs = (time_t)val; strftime(buf,buflen,"%e %b %Y",localtime(&secs)); for (i = 11; i < width; i++) buf[i] = ' '; buf[i] = '\0'; } } return (true); } sc-7.16_1.1.2/frame.c000066400000000000000000000161741326046243500140470ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Framed range manipulation * * Chuck Martin * Originally created: December, 2000 * * $Revision: 7.16 $ */ #include #include #include #include #include #include #include "compat.h" #include "sc.h" static struct frange *frame_base; extern struct frange *lastfr; void add_frange(struct ent *or_left, struct ent *or_right, struct ent *ir_left, struct ent *ir_right, int toprows, int bottomrows, int leftcols, int rightcols) { struct frange *r; int minr, minc, maxr, maxc; minr = or_left->row < or_right->row ? or_left->row : or_right->row; minc = or_left->col < or_right->col ? or_left->col : or_right->col; maxr = or_left->row > or_right->row ? or_left->row : or_right->row; maxc = or_left->col > or_right->col ? or_left->col : or_right->col; or_left = lookat(minr, minc); or_right = lookat(maxr, maxc); if (ir_left) { minr = ir_left->row < ir_right->row ? ir_left->row : ir_right->row; minc = ir_left->col < ir_right->col ? ir_left->col : ir_right->col; maxr = ir_left->row > ir_right->row ? ir_left->row : ir_right->row; maxc = ir_left->col > ir_right->col ? ir_left->col : ir_right->col; ir_left = lookat(minr, minc); ir_right = lookat(maxr, maxc); if (ir_left->row < or_left->row || ir_left->col < or_left->col || ir_right->row > or_right->row || ir_right->col > or_right->col) { error("Invalid parameters"); return; } } if (frame_base) { /* * Has this frange already been created? If so, any negative * parameters mean "don't change this value." */ for (r = frame_base; r; r = r->r_next) { if ((r->or_left == or_left) && (r->or_right == or_right)) { if (ir_left) { r->ir_left = ir_left; r->ir_right = ir_right; } else { if (toprows < 0) toprows = r->ir_left->row - r->or_left->row; if (bottomrows < 0) bottomrows = r->or_right->row - r->ir_right->row; if (leftcols < 0) leftcols = r->ir_left->col - r->or_left->col; if (rightcols < 0) rightcols = r->or_right->col - r->ir_right->col; r->ir_left = lookat(r->or_left->row + toprows, r->or_left->col + leftcols); r->ir_right = lookat(r->or_right->row - bottomrows, r->or_right->col - rightcols); } /* If all frame sides are 0, delete the frange */ if (r->ir_left == r->or_left && r->ir_right == r->or_right) { if (r->r_next) r->r_next->r_prev = r->r_prev; if (r->r_prev) r->r_prev->r_next = r->r_next; else frame_base = r->r_next; scxfree((char *)r); if (lastfr == r) lastfr = NULL; } modflg++; FullUpdate++; return; } } /* * See if the specified range overlaps any previously created frange. */ for (r = frame_base; r; r = r->r_next) { if ( !(r->or_left->row > or_right->row || r->or_right->row < or_left->row || r->or_left->col > or_right->col || r->or_right->col < or_left->col)) { error("Framed ranges may not be nested or overlapping"); return; } } } if (ir_left != or_left || ir_right != or_right) { r = scxmalloc(sizeof(struct frange)); r->or_left = or_left; r->or_right = or_right; if (ir_left) { r->ir_left = ir_left; r->ir_right = ir_right; } else { if (toprows < 0) toprows = 0; if (bottomrows < 0) bottomrows = 0; if (leftcols < 0) leftcols = 0; if (rightcols < 0) rightcols = 0; r->ir_left = lookat(r->or_left->row + toprows, r->or_left->col + leftcols); r->ir_right = lookat(r->or_right->row - bottomrows, r->or_right->col - rightcols); } r->r_next = frame_base; r->r_prev = NULL; if (frame_base) frame_base->r_prev = r; frame_base = r; modflg++; FullUpdate++; } } void clean_frange(void) { register struct frange *fr; register struct frange *nextfr; fr = frame_base; frame_base = NULL; while (fr) { nextfr = fr->r_next; scxfree((char *)fr); fr = nextfr; } lastfr = NULL; } struct frange * find_frange(int row, int col) { struct frange *r; if (frame_base) for (r = frame_base; r; r = r->r_next) { if ((r->or_left->row <= row) && (r->or_left->col <= col) && (r->or_right->row >= row) && (r->or_right->col >= col)) return r; } return 0; } void sync_franges(void) { struct frange *fr; fr = frame_base; while (fr) { fr->or_left = lookat(fr->or_left->row, fr->or_left->col); fr->or_right = lookat(fr->or_right->row, fr->or_right->col); fr->ir_left = lookat(fr->ir_left->row, fr->ir_left->col); fr->ir_right = lookat(fr->ir_right->row, fr->ir_right->col); fr = fr->r_next; } } void write_franges(FILE *f) { register struct frange *r; register struct frange *nextr; for (r = nextr = frame_base; nextr; r = nextr, nextr = r->r_next) /**/ ; while (r) { fprintf(f, "frame %s", v_name(r->or_left->row, r->or_left->col)); fprintf(f, ":%s", v_name(r->or_right->row, r->or_right->col)); fprintf(f, " %s", v_name(r->ir_left->row, r->ir_left->col)); fprintf(f, ":%s\n", v_name(r->ir_right->row, r->ir_right->col)); r = r->r_prev; } } void list_frames(FILE *f) { register struct frange *r; register struct frange *nextr; if (!are_frames()) { fprintf(f, " No frames"); return; } (void) fprintf(f, " %-30s %s\n","Outer Range","Inner Range"); if (!brokenpipe) (void) fprintf(f, " %-30s %s\n","-----------","-----------"); for (r = nextr = frame_base; nextr; r = nextr, nextr = r->r_next) /* */ ; while (r) { fprintf(f, " %-30s", r_name(r->or_left->row, r->or_left->col, r->or_right->row, r->or_right->col)); fprintf(f, " %s\n", r_name(r->ir_left->row, r->ir_left->col, r->ir_right->row, r->ir_right->col)); if (brokenpipe) return; r = r->r_prev; } } int are_frames(void) { return (frame_base != 0); } void fix_frames(int row1, int col1, int row2, int col2, int delta1, int delta2) { int r1, r2, c1, c2; struct frange *fr, *cfr; cfr = find_frange(currow, curcol); if (frame_base) for (fr = frame_base; fr; fr = fr->r_next) { r1 = fr->or_left->row; c1 = fr->or_left->col; r2 = fr->or_right->row; c2 = fr->or_right->col; if (!(cfr && (c1 < cfr->or_left->col || c1 > cfr->or_right->col))) { if (r1 >= row1 && r1 <= row2) r1 = row2 - delta1; if (c1 >= col1 && c1 <= col2) c1 = col2 - delta1; } if (!(cfr && (c2 < cfr->or_left->col || c2 > cfr->or_right->col))) { if (r2 >= row1 && r2 <= row2) r2 = row1 + delta2; if (c2 >= col1 && c2 <= col2) c2 = col1 + delta2; } fr->or_left = lookat(r1, c1); fr->or_right = lookat(r2, c2); r1 = fr->ir_left->row; c1 = fr->ir_left->col; r2 = fr->ir_right->row; c2 = fr->ir_right->col; if (!(cfr && (c1 < cfr->or_left->col || c1 > cfr->or_right->col))) { if (r1 >= row1 && r1 <= row2) r1 = row2 - delta1; if (c1 >= col1 && c1 <= col2) c1 = col2 - delta1; } if (!(cfr && (c2 < cfr->or_left->col || c2 > cfr->or_right->col))) { if (r2 >= row1 && r2 <= row2) r2 = row1 + delta2; if (c2 >= col1 && c2 <= col2) c2 = col1 + delta2; } fr->ir_left = lookat(r1, c1); fr->ir_right = lookat(r2, c2); } } sc-7.16_1.1.2/gram.y000066400000000000000000001204601326046243500137230ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Command and expression parser * * original by James Gosling, September 1982 * modified by Mark Weiser and Bruce Israel, * University of Maryland * * more mods Robert Bond 12/86 * * More mods by Alan Silverstein, 3/88, see list of changes. * * $Revision: 7.16 $ */ %{ #include #include #ifdef USELOCALE #include #endif #ifndef MSDOS #include #endif #include "compat.h" #include "sc.h" #define ENULL (struct enode *)0 %} %union { int ival; double fval; struct ent_ptr ent; struct enode *enode; char *sval; struct range_s rval; } %type var %type num %type range %type var_or_range %type strarg %type e term expr_list %token STRING %token NUMBER %token FNUMBER %token RANGE %token VAR %token WORD %token PLUGIN %token COL /* * When adding new commands, make sure that any commands that may take * COL as an argument precede S_FORMAT in the %token list. All other * commands must come after S_FORMAT. This is necessary so that range * names can be less than three letters without being parsed as column * names. */ %token S_SHOW %token S_HIDE %token S_INSERTCOL %token S_OPENCOL %token S_DELETECOL %token S_YANKCOL %token S_GETFORMAT %token S_FORMAT %token S_FMT %token S_LET %token S_LABEL %token S_LEFTSTRING %token S_RIGHTSTRING %token S_LEFTJUSTIFY %token S_RIGHTJUSTIFY %token S_CENTER %token S_COLOR %token S_ADDNOTE %token S_DELNOTE %token S_GET %token S_PUT %token S_MERGE %token S_WRITE %token S_TBL %token S_COPY %token S_MOVE %token S_ERASE %token S_YANK %token S_FILL %token S_SORT %token S_LOCK %token S_UNLOCK %token S_GOTO %token S_DEFINE %token S_UNDEFINE %token S_ABBREV %token S_UNABBREV %token S_FRAME %token S_FRAMETOP %token S_FRAMEBOTTOM %token S_FRAMELEFT %token S_FRAMERIGHT %token S_UNFRAME %token S_VALUE %token S_MDIR %token S_AUTORUN %token S_FKEY %token S_HISTFILE %token S_SCEXT %token S_ASCEXT %token S_TBL0EXT %token S_TBLEXT %token S_LATEXEXT %token S_SLATEXEXT %token S_TEXEXT %token S_SET %token S_UP %token S_DOWN %token S_LEFT %token S_RIGHT %token S_ENDUP %token S_ENDDOWN %token S_ENDLEFT %token S_ENDRIGHT %token S_SELECT %token S_INSERTROW %token S_OPENROW %token S_DELETEROW %token S_YANKROW %token S_PULL %token S_PULLMERGE %token S_PULLROWS %token S_PULLCOLS %token S_PULLXCHG %token S_PULLTP %token S_PULLFMT %token S_PULLCOPY %token S_WHEREAMI %token S_GETNUM %token S_FGETNUM %token S_GETSTRING %token S_GETEXP %token S_GETFMT %token S_GETFRAME %token S_GETRANGE %token S_EVAL %token S_SEVAL %token S_QUERY %token S_GETKEY %token S_ERROR %token S_RECALC %token S_REDRAW %token S_QUIT %token S_STATUS %token S_RUN %token S_PLUGIN %token S_PLUGOUT %token K_ERROR %token K_INVALID %token K_FIXED %token K_SUM %token K_PROD %token K_AVG %token K_STDDEV %token K_COUNT %token K_ROWS %token K_COLS %token K_ABS %token K_ACOS %token K_ASIN %token K_ATAN %token K_ATAN2 %token K_CEIL %token K_COS %token K_EXP %token K_FABS %token K_FLOOR %token K_HYPOT %token K_LN %token K_LOG %token K_PI %token K_POW %token K_SIN %token K_SQRT %token K_TAN %token K_DTR %token K_RTD %token K_MAX %token K_MIN %token K_RND %token K_ROUND %token K_IF %token K_PV %token K_FV %token K_PMT %token K_HOUR %token K_MINUTE %token K_SECOND %token K_MONTH %token K_DAY %token K_YEAR %token K_NOW %token K_DATE %token K_DTS %token K_TTS %token K_FMT %token K_SUBSTR %token K_UPPER %token K_LOWER %token K_CAPITAL %token K_STON %token K_EQS %token K_EXT %token K_NVAL %token K_SVAL %token K_LOOKUP %token K_HLOOKUP %token K_VLOOKUP %token K_INDEX %token K_STINDEX %token K_AUTO %token K_AUTOCALC %token K_AUTOINSERT %token K_AUTOWRAP %token K_CSLOP %token K_BYROWS %token K_BYCOLS %token K_OPTIMIZE %token K_ITERATIONS %token K_NUMERIC %token K_PRESCALE %token K_EXTFUN %token K_CELLCUR %token K_TOPROW %token K_COLOR %token K_COLORNEG %token K_COLORERR %token K_BRAILLE %token K_BACKUP %token K_MOUSE %token K_BLACK %token K_RED %token K_GREEN %token K_YELLOW %token K_BLUE %token K_MAGENTA %token K_CYAN %token K_WHITE %token K_TBLSTYLE %token K_TBL %token K_LATEX %token K_SLATEX %token K_TEX %token K_FRAME %token K_RNDTOEVEN %token K_FILENAME %token K_MYROW %token K_MYCOL %token K_LASTROW %token K_LASTCOL %token K_COLTOA %token K_CRACTION %token K_CRROW %token K_CRCOL %token K_ROWLIMIT %token K_COLLIMIT %token K_PAGESIZE %token K_NUMITER %token K_ERR %token K_SCRC %token K_LOCALE %right ';' %left '?' ':' %left '|' %left '&' %nonassoc '<' '=' '>' '!' %left '+' '-' '#' %left '*' '/' '%' %left '^' %% command: S_LET var_or_range '=' e { let($2.left.vp, $4); } | S_LET var_or_range '=' { $2.left.vp->v = (double) 0.0; if ($2.left.vp->expr && !($2.left.vp->flags & IS_STREXPR)) { efree($2.left.vp->expr); $2.left.vp->expr = NULL; } $2.left.vp->cellerror = CELLOK; $2.left.vp->flags &= ~IS_VALID; $2.left.vp->flags |= IS_CHANGED; changed++; FullUpdate++; modflg++; } | S_LABEL var_or_range '=' e { slet($2.left.vp, $4, 0); } | S_LEFTSTRING var_or_range '=' e { slet($2.left.vp, $4, -1); } | S_RIGHTSTRING var_or_range '=' e { slet($2.left.vp, $4, 1); } | S_LEFTJUSTIFY var_or_range { ljustify($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col); } | S_LEFTJUSTIFY { if (showrange) ljustify(showsr, showsc, currow, curcol); } | S_RIGHTJUSTIFY var_or_range { rjustify($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col); } | S_RIGHTJUSTIFY { if (showrange) rjustify(showsr, showsc, currow, curcol); } | S_CENTER var_or_range { center($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col); } | S_CENTER { if (showrange) center(showsr, showsc, currow, curcol); } | S_ADDNOTE var { if (showrange) { $2.vp->nrow=currowncol=curcolnlastrow=currownlastcol=curcolnrow=currow; $2.vp->ncol=curcol; $2.vp->nlastrow=currow; $2.vp->nlastcol=curcol; } $2.vp->flags |= IS_CHANGED; FullUpdate++; modflg++; } | S_ADDNOTE var var_or_range { $2.vp->nrow = $3.left.vp->row; $2.vp->ncol = $3.left.vp->col; $2.vp->nlastrow = $3.right.vp->row; $2.vp->nlastcol = $3.right.vp->col; $2.vp->flags |= IS_CHANGED; FullUpdate++; modflg++; } | S_DELNOTE var { $2.vp->nrow = $2.vp->ncol = -1; } | S_DELNOTE { struct ent *p; p = lookat(currow, curcol); p->nrow = p->ncol = -1; p->flags |= IS_CHANGED; modflg++; } | S_FORMAT COL ':' COL NUMBER NUMBER NUMBER { doformat($2,$4,$5,$6,$7); } | S_FORMAT COL NUMBER NUMBER NUMBER { doformat($2,$2,$3,$4,$5); } | S_FORMAT COL ':' COL NUMBER NUMBER { doformat($2,$4,$5,$6, REFMTFIX); } | S_FORMAT COL NUMBER NUMBER { doformat($2,$2,$3,$4, REFMTFIX); } | S_FORMAT NUMBER '=' STRING { if ($2 >= 0 && $2 < 10) { if (colformat[$2]) scxfree(colformat[$2]); if (strlen($4)) colformat[$2] = $4; else colformat[$2] = NULL; FullUpdate++; modflg++; } else error("Invalid format number"); } | S_GET strarg { /* This tmp hack is because readfile * recurses back through yyparse. */ char *tmp; tmp = $2; readfile(tmp, 1); scxfree(tmp); } | S_MERGE strarg { char *tmp; tmp = $2; readfile(tmp, 0); scxfree(tmp); } | S_MDIR strarg { if (mdir) scxfree(mdir); if (strlen($2)) mdir = $2; modflg++; } | S_AUTORUN strarg { if (autorun) scxfree(autorun); if (strlen($2)) autorun = $2; modflg++; } | S_FKEY NUMBER '=' strarg { if ($2 > 0 && $2 <= FKEYS) { if (fkey[$2 - 1]) { scxfree(fkey[$2 - 1]); fkey[$2 - 1] = NULL; } if (strlen($4)) fkey[$2 - 1] = $4; modflg++; } else error("Invalid function key"); } | S_SCEXT strarg { if (scext) scxfree(scext); scext = $2; } | S_HISTFILE strarg { strlcpy(histfile, $2, sizeof histfile); scxfree($2); } | S_ASCEXT strarg { if (ascext) scxfree(ascext); ascext = $2; } | S_TBL0EXT strarg { if (tbl0ext) scxfree(tbl0ext); tbl0ext = $2; } | S_TBLEXT strarg { if (tblext) scxfree(tblext); tblext = $2; } | S_LATEXEXT strarg { if (latexext) scxfree(latexext); latexext = $2; } | S_SLATEXEXT strarg { if (slatexext) scxfree(slatexext); slatexext = $2; } | S_TEXEXT strarg { if (texext) scxfree(texext); texext = $2; } | S_PUT strarg range { (void) writefile($2, ($3.left.vp)->row, ($3.left.vp)->col, ($3.right.vp)->row, ($3.right.vp)->col); scxfree($2); } | S_PUT strarg { (void) writefile($2, 0, 0, maxrow, maxcol); scxfree($2); } | S_PUT range { (void) write_cells(stdout, $2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $2.left.vp->row, $2.left.vp->col); } | S_PUT range '/' var_or_range { (void) write_cells(stdout, $2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4.left.vp->row, $4.left.vp->col); } | S_PUT '%' '/' var_or_range { (void) write_cells(stdout, 0, 0, maxrow, maxcol, $4.left.vp->row, $4.left.vp->col); } | S_PUT '/' var_or_range { (void) write_cells(stdout, showsr, showsc, currow, curcol, $3.left.vp->row, $3.left.vp->col); } | S_PUT '%' { (void) write_cells(stdout, 0, 0, maxrow, maxcol, 0, 0); } | S_PUT { (void) write_cells(stdout, 0, 0, maxrow, maxcol, 0, 0); } | S_WRITE strarg range { (void) printfile($2, ($3.left.vp)->row, ($3.left.vp)->col, ($3.right.vp)->row, ($3.right.vp)->col); scxfree($2); } | S_WRITE strarg { (void) printfile($2, 0, 0, maxrow, maxcol); scxfree($2); } | S_WRITE range { (void) printfile(NULL, $2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col); } | S_WRITE '%' { (void) printfile(NULL, 0, 0, maxrow, maxcol); } | S_WRITE { (void) printfile(NULL, 0, 0, maxrow, maxcol); } | S_TBL strarg range { (void) tblprintfile($2, ($3.left.vp)->row, ($3.left.vp)->col, ($3.right.vp)->row, ($3.right.vp)->col); scxfree($2); } | S_TBL strarg { (void)tblprintfile($2, 0, 0, maxrow, maxcol); scxfree($2); } | S_SHOW COL ':' COL { showcol($2, $4); } | S_SHOW NUMBER ':' NUMBER { showrow($2, $4); } | S_HIDE { int a; if (showrange == SHOWROWS) { if (showsr < currow) { int r = currow; currow = showsr; showsr = r; } a = showsr - currow + 1; hiderow(a); } else if (showrange == SHOWCOLS) { if (showsc < curcol) { int c = curcol; curcol = showsc; showsc = c; } a = showsc - curcol + 1; hidecol(a); } else a = 1; } | S_HIDE COL { hide_col($2); } | S_HIDE COL ':' COL { int c = curcol, a; if ($2 < $4) { curcol = $2; a = $4 - $2 + 1; } else { curcol = $4; a = $2 - $4 + 1; } hidecol(a); curcol = c < curcol ? c : c < curcol + a ? curcol : c - a; } | S_HIDE NUMBER { hide_row($2); } | S_HIDE NUMBER ':' NUMBER { int r = currow, a; if ($2 < $4) { currow = $2; a = $4 - $2 + 1; } else { currow = $4; a = $2 - $4 + 1; } hiderow(a); currow = r < currow ? r : r < currow + a ? currow : r - a; } | S_COPY { if (showrange) { showrange = 0; copy(lookat(showsr, showsc), lookat(currow, curcol), NULL, NULL); } else copy(lookat(currow, curcol), lookat(currow, curcol), NULL, NULL); } | S_COPY range { copy($2.left.vp, $2.right.vp, NULL, NULL); } | S_COPY range var_or_range { copy($2.left.vp, $2.right.vp, $3.left.vp, $3.right.vp); } | S_MOVE var { mover($2.vp, lookat(showsr, showsc), lookat(currow, curcol)); } | S_MOVE var var_or_range { mover($2.vp, $3.left.vp, $3.right.vp); } | S_ERASE { eraser(lookat(showsr, showsc), lookat(currow, curcol)); } | S_ERASE var_or_range { eraser($2.left.vp, $2.right.vp); } | S_YANK { yankr(lookat(showsr, showsc), lookat(currow, curcol)); } | S_YANK var_or_range { yankr($2.left.vp, $2.right.vp); } | S_VALUE { valueize_area(showsr, showsc, currow, curcol); modflg++; } | S_VALUE var_or_range { valueize_area(($2.left.vp)->row, ($2.left.vp)->col, ($2.right.vp)->row, ($2.right.vp)->col); modflg++; } | S_FILL var_or_range num num { fill($2.left.vp, $2.right.vp, $3, $4); } | S_SORT { sortrange(lookat(showsr, showsc), lookat(currow, curcol), NULL); } | S_SORT range { sortrange($2.left.vp, $2.right.vp, NULL); } | S_SORT range strarg { sortrange($2.left.vp, $2.right.vp, $3); } | S_FMT var_or_range STRING { format_cell($2.left.vp, $2.right.vp, $3); } | S_LOCK { lock_cells(lookat(showsr, showsc), lookat(currow, curcol)); } | S_LOCK var_or_range { lock_cells($2.left.vp, $2.right.vp); } | S_UNLOCK { unlock_cells(lookat(showsr, showsc), lookat(currow, curcol)); } | S_UNLOCK var_or_range { unlock_cells($2.left.vp, $2.right.vp); } | S_GOTO var_or_range var_or_range { moveto($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $3.left.vp->row, $3.left.vp->col); } | S_GOTO var_or_range { moveto($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, -1, -1); } | S_GOTO num range { num_search($2, $3.left.vp->row, $3.left.vp->col, $3.right.vp->row, $3.right.vp->col, 0); } | S_GOTO num { num_search($2, 0, 0, maxrow, maxcol, 0); } | S_GOTO errlist | S_GOTO STRING range { str_search($2, $3.left.vp->row, $3.left.vp->col, $3.right.vp->row, $3.right.vp->col, 0); } | S_GOTO '#' STRING range { str_search($3, $4.left.vp->row, $4.left.vp->col, $4.right.vp->row, $4.right.vp->col, 1); } | S_GOTO '%' STRING range { str_search($3, $4.left.vp->row, $4.left.vp->col, $4.right.vp->row, $4.right.vp->col, 2); } | S_GOTO STRING { str_search($2, 0, 0, maxrow, maxcol, 0); } | S_GOTO '#' STRING { str_search($3, 0, 0, maxrow, maxcol, 1); } | S_GOTO '%' STRING { str_search($3, 0, 0, maxrow, maxcol, 2); } | S_GOTO { go_last(); } | S_GOTO WORD { /* don't repeat last goto on "unintelligible word" */ ; } | S_DEFINE strarg { struct ent_ptr arg1, arg2; arg1.vp = lookat(showsr, showsc); arg1.vf = 0; arg2.vp = lookat(currow, curcol); arg2.vf = 0; if (arg1.vp == arg2.vp || !showrange) add_range($2, arg2, arg2, 0); else add_range($2, arg1, arg2, 1); } | S_DEFINE strarg range { add_range($2, $3.left, $3.right, 1); } | S_DEFINE strarg var { add_range($2, $3, $3, 0); } | S_UNDEFINE var_or_range { del_range($2.left.vp, $2.right.vp); } | S_ABBREV STRING { add_abbr($2); } | S_ABBREV { add_abbr(NULL); } | S_UNABBREV STRING { del_abbr($2); } | S_FRAME range range { add_frange($2.left.vp, $2.right.vp, $3.left.vp, $3.right.vp, 0, 0, 0, 0); } | S_FRAME range { if (showrange) { showrange = 0; add_frange($2.left.vp, $2.right.vp, lookat(showsr, showsc), lookat(currow, curcol), 0, 0, 0, 0); } else { struct frange *cfr; cfr = find_frange(currow, curcol); if (cfr) { add_frange(cfr->or_left, cfr->or_right, $2.left.vp, $2.right.vp, 0, 0, 0, 0); } } } | S_FRAME { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (showrange && cfr) { showrange = 0; add_frange(cfr->or_left, cfr->or_right, lookat(showsr, showsc), lookat(currow, curcol), 0, 0, 0, 0); } else { error("Need both outer and inner" " ranges to create frame"); } } | S_FRAMETOP range NUMBER { add_frange($2.left.vp, $2.right.vp, NULL, NULL, $3, -1, -1, -1); } | S_FRAMETOP NUMBER { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (cfr) add_frange(cfr->or_left, cfr->or_right, NULL, NULL, $2, -1, -1, -1); } | S_FRAMEBOTTOM range NUMBER { add_frange($2.left.vp, $2.right.vp, NULL, NULL, -1, $3, -1, -1); } | S_FRAMEBOTTOM NUMBER { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (cfr) add_frange(cfr->or_left, cfr->or_right, NULL, NULL, -1, $2, -1, -1); } | S_FRAMELEFT range NUMBER { add_frange($2.left.vp, $2.right.vp, NULL, NULL, -1, -1, $3, -1); } | S_FRAMELEFT NUMBER { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (cfr) add_frange(cfr->or_left, cfr->or_right, NULL, NULL, -1, -1, $2, -1); } | S_FRAMERIGHT range NUMBER { add_frange($2.left.vp, $2.right.vp, NULL, NULL, -1, -1, -1, $3); } | S_FRAMERIGHT NUMBER { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (cfr) add_frange(cfr->or_left, cfr->or_right, NULL, NULL, -1, -1, -1, $2); } | S_UNFRAME range { add_frange($2.left.vp, $2.right.vp, NULL, NULL, 0, 0, 0, 0); } | S_UNFRAME { struct frange *cfr; /* cfr points to current frange */ cfr = find_frange(currow, curcol); if (cfr) add_frange(cfr->or_left, cfr->or_right, NULL, NULL, 0, 0, 0, 0); } | S_COLOR NUMBER '=' { if ($2 > 0 && $2 < 9) initcolor($2); else error("Invalid color number"); } | S_COLOR NUMBER '=' e { change_color($2, $4); } | S_COLOR range NUMBER { add_crange($2.left.vp, $2.right.vp, $3); } | S_SET setlist { modflg++; } | S_UP { backrow( 1); } | S_UP NUMBER { backrow($2); } | S_DOWN { forwrow( 1); } | S_DOWN NUMBER { forwrow($2); } | S_LEFT { backcol( 1); } | S_LEFT NUMBER { backcol($2); } | S_RIGHT { forwcol( 1); } | S_RIGHT NUMBER { forwcol($2); } | S_ENDUP { doend(-1, 0); } | S_ENDDOWN { doend( 1, 0); } | S_ENDLEFT { doend( 0,-1); } | S_ENDRIGHT { doend( 0, 1); } | S_SELECT STRING { int c; if ((c = *$2) >= '0' && c <= '9') { qbuf = c - '0' + (DELBUFSIZE - 10); } else if (c >= 'a' && c <= 'z') { qbuf = c - 'a' + (DELBUFSIZE - 36); } else if (c == '"') { qbuf = 0; } else error("Invalid buffer"); scxfree($2); } | S_INSERTROW { insertrow( 1, 0); } | S_INSERTROW '*' NUMBER { insertrow($3, 0); } | S_OPENROW { insertrow( 1, 1); } | S_OPENROW '*' NUMBER { insertrow($3, 1); } | S_INSERTCOL { insertcol( 1, 0); } | S_INSERTCOL '*' NUMBER { insertcol($3, 0); } | S_OPENCOL { insertcol( 1, 1); } | S_OPENCOL '*' NUMBER { insertcol($3, 1); } | S_DELETEROW { int a; if (showrange == SHOWROWS) { if (showsr < currow) { int r = currow; currow = showsr; showsr = r; } a = showsr - currow + 1; } else a = 1; deleterow(a); } | S_DELETEROW '*' NUMBER { deleterow($3); } | S_DELETEROW NUMBER { int r = currow; currow = $2; deleterow(1); currow = r <= currow ? r : r - 1; } | S_DELETEROW NUMBER ':' NUMBER { int r = currow, a; if ($2 < $4) { currow = $2; a = $4 - $2 + 1; } else { currow = $4; a = $2 - $4 + 1; } deleterow(a); currow = r < currow ? r : r < currow + a ? currow : r - a; } | S_DELETECOL { int a; if (showrange == SHOWCOLS) { if (showsc < curcol) { int c = curcol; curcol = showsc; showsc = c; } a = showsc - curcol + 1; } else a = 1; closecol(a); } | S_DELETECOL COL { int r = curcol; curcol = $2; closecol(1); curcol = r <= curcol ? r : r - 1; } | S_DELETECOL '*' NUMBER { closecol($3); } | S_DELETECOL COL ':' COL { int c = curcol, a; if ($2 < $4) { curcol = $2; a = $4 - $2 + 1; } else { curcol = $4; a = $2 - $4 + 1; } closecol(a); curcol = c < curcol ? c : c < curcol + a ? curcol : c - a; } | S_YANKROW { int r = currow, a; if (showrange == SHOWROWS) { if (showsr < currow) { currow = showsr; showsr = r; } a = showsr - currow + 1; } else a = 1; yankrow(a); currow = r; } | S_YANKROW '*' NUMBER { yankrow($3); } | S_YANKROW NUMBER { int r = currow; currow = $2; yankrow(1); currow = r; } | S_YANKROW NUMBER ':' NUMBER { int r = currow, a; if ($2 < $4) { currow = $2; a = $4 - $2 + 1; } else { currow = $4; a = $2 - $4 + 1; } yankrow(a); currow = r; } | S_YANKCOL { int c = curcol, a; if (showrange == SHOWCOLS) { if (showsc < curcol) { curcol = showsc; showsc = c; } a = showsc - curcol + 1; } else a = 1; yankcol(a); curcol = c; } | S_YANKCOL NUMBER { int c = curcol; curcol = $2; yankcol(1); curcol = c; } | S_YANKCOL '*' NUMBER { yankcol($3); } | S_YANKCOL COL ':' COL { int c = curcol, a; if ($2 < $4) { curcol = $2; a = $4 - $2 + 1; } else { curcol = $4; a = $2 - $4 + 1; } yankcol(a); curcol = c; } | S_PULL { pullcells('p'); } | S_PULLMERGE { pullcells('m'); } | S_PULLROWS { pullcells('r'); } | S_PULLCOLS { pullcells('c'); } | S_PULLXCHG { pullcells('x'); } | S_PULLTP { pullcells('t'); } | S_PULLFMT { pullcells('f'); } | S_PULLCOPY { copy(NULL, NULL, NULL, NULL); } | S_PULLCOPY var_or_range { copy($2.left.vp, $2.right.vp, NULL, (struct ent *)1); } | S_WHEREAMI { size_t l; snprintf(line, sizeof line, "%s%d ", coltoa(curcol), currow); l = strlen(line); snprintf(line + l, sizeof(line) - l, "%s%d\n", coltoa(stcol), strow); write(macrofd, line, strlen(line)); line[0] = '\0'; } | S_WHEREAMI '|' NUMBER { size_t l; snprintf(line, sizeof line, "%s%d ", coltoa(curcol), currow); l = strlen(line); snprintf(line + l, sizeof(line) - l, "%s%d\n", coltoa(stcol), strow); write($3, line, strlen(line)); line[0] = '\0'; } | S_GETNUM var_or_range { getnum($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, macrofd); } | S_GETNUM var_or_range '|' NUMBER { getnum($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4); } | S_GETNUM { getnum(currow, curcol, currow, curcol, macrofd); } | S_GETNUM '|' NUMBER { getnum(currow, curcol, currow, curcol, $3); } | S_FGETNUM var_or_range { fgetnum($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, macrofd); } | S_FGETNUM var_or_range '|' NUMBER { fgetnum($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4); } | S_FGETNUM { fgetnum(currow, curcol, currow, curcol, macrofd); } | S_FGETNUM '|' NUMBER { fgetnum(currow, curcol, currow, curcol, $3); } | S_GETSTRING var_or_range { getstring($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, macrofd); } | S_GETSTRING var_or_range '|' NUMBER { getstring($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4); } | S_GETSTRING { getstring(currow, curcol, currow, curcol, macrofd); } | S_GETSTRING '|' NUMBER { getstring(currow, curcol, currow, curcol, $3); } | S_GETEXP var_or_range { getexp($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, macrofd); } | S_GETEXP var_or_range '|' NUMBER { getexp($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4); } | S_GETEXP { getexp(currow, curcol, currow, curcol, macrofd); } | S_GETEXP '|' NUMBER { getexp(currow, curcol, currow, curcol, $3); } | S_GETFORMAT COL { getformat($2, macrofd); } | S_GETFORMAT COL '|' NUMBER { getformat($2, $4); } | S_GETFORMAT { getformat(curcol, macrofd); } | S_GETFORMAT '|' NUMBER { getformat(curcol, $3); } | S_GETFMT var_or_range { getfmt($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, macrofd); } | S_GETFMT var_or_range '|' NUMBER { getfmt($2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, $4); } | S_GETFMT { getfmt(currow, curcol, currow, curcol, macrofd); } | S_GETFMT '|' NUMBER { getfmt(currow, curcol, currow, curcol, $3); } | S_GETFRAME { getframe(macrofd); } | S_GETFRAME '|' NUMBER { getframe($3); } | S_GETRANGE STRING { getrange($2, macrofd); } | S_GETRANGE STRING '|' NUMBER { getrange($2, $4); } | S_EVAL e { doeval($2, NULL, currow, curcol, macrofd); } | S_EVAL e STRING { doeval($2, $3, currow, curcol, macrofd); } | S_EVAL e STRING '|' NUMBER { doeval($2, $3, currow, curcol, $5); } | S_SEVAL e { doseval($2, currow, curcol, macrofd); } | S_QUERY STRING STRING { doquery($2, $3, macrofd); } | S_QUERY STRING STRING '|' NUMBER { doquery($2, $3, $5); } | S_QUERY STRING { doquery($2, NULL, macrofd); } | S_QUERY STRING '|' NUMBER { doquery($2, NULL, $4); } | S_QUERY { doquery(NULL, NULL, macrofd); } | S_QUERY '|' NUMBER { doquery(NULL, NULL, $3); } | S_GETKEY { dogetkey(); } | S_ERROR STRING { error($2); } | S_STATUS { dostat(macrofd); } | S_STATUS '|' NUMBER { dostat($3); } | S_RECALC { EvalAll(); update(1); changed = 0; } | S_REDRAW { if (usecurses) { clearok(stdscr, TRUE); linelim = -1; update(1); refresh(); changed = 0; } } | S_QUIT { stopdisp(); exit(0); } | S_RUN STRING { deraw(1); system($2); if (*($2 + strlen($2) - 1) != '&') { printf("Press any key to continue "); fflush(stdout); cbreak(); nmgetch(); } goraw(); scxfree($2); } | S_PLUGIN STRING '=' STRING { addplugin($2, $4, 'r'); } | S_PLUGOUT STRING '=' STRING { addplugin($2, $4, 'w'); } | PLUGIN { *line = '|'; snprintf(line + 1, sizeof(line) - 1, $1); readfile(line, 0); scxfree($1); } | /* nothing */ | error; term: var { $$ = new_var(O_VAR, $1); } | '@' K_FIXED term { $$ = new('f', $3, ENULL); } | '(' '@' K_FIXED ')' term { $$ = new('F', $5, ENULL); } | '@' K_SUM '(' var_or_range ')' { $$ = new(SUM, new_range(REDUCE | SUM, $4), ENULL); } | '@' K_SUM '(' range ',' e ')' { $$ = new(SUM, new_range(REDUCE | SUM, $4), $6); } | '@' K_PROD '(' var_or_range ')' { $$ = new(PROD, new_range(REDUCE | PROD, $4), ENULL); } | '@' K_PROD '(' range ',' e ')' { $$ = new(PROD, new_range(REDUCE | PROD, $4), $6); } | '@' K_AVG '(' var_or_range ')' { $$ = new(AVG, new_range(REDUCE | AVG, $4), ENULL); } | '@' K_AVG '(' range ',' e ')' { $$ = new(AVG, new_range(REDUCE | AVG, $4), $6); } | '@' K_STDDEV '(' var_or_range ')' { $$ = new(STDDEV, new_range(REDUCE | STDDEV, $4), ENULL); } | '@' K_STDDEV '(' range ',' e ')' { $$ = new(STDDEV, new_range(REDUCE | STDDEV, $4), $6); } | '@' K_COUNT '(' var_or_range ')' { $$ = new(COUNT, new_range(REDUCE | COUNT, $4), ENULL); } | '@' K_COUNT '(' range ',' e ')' { $$ = new(COUNT, new_range(REDUCE | COUNT, $4), $6); } | '@' K_MAX '(' var_or_range ')' { $$ = new(MAX, new_range(REDUCE | MAX, $4), ENULL); } | '@' K_MAX '(' range ',' e ')' { $$ = new(MAX, new_range(REDUCE | MAX, $4), $6); } | '@' K_MAX '(' e ',' expr_list ')' { $$ = new(LMAX, $6, $4); } | '@' K_MIN '(' var_or_range ')' { $$ = new(MIN, new_range(REDUCE | MIN, $4), ENULL); } | '@' K_MIN '(' range ',' e ')' { $$ = new(MIN, new_range(REDUCE | MIN, $4), $6); } | '@' K_MIN '(' e ',' expr_list ')' { $$ = new(LMIN, $6, $4); } | '@' K_ROWS '(' var_or_range ')' { $$ = new_range(REDUCE | 'R', $4); } | '@' K_COLS '(' var_or_range ')' { $$ = new_range(REDUCE | 'C', $4); } | '@' K_ABS '(' e ')' { $$ = new(ABS, $4, ENULL); } | '@' K_ACOS '(' e ')' { $$ = new(ACOS, $4, ENULL); } | '@' K_ASIN '(' e ')' { $$ = new(ASIN, $4, ENULL); } | '@' K_ATAN '(' e ')' { $$ = new(ATAN, $4, ENULL); } | '@' K_ATAN2 '(' e ',' e ')' { $$ = new(ATAN2, $4, $6); } | '@' K_CEIL '(' e ')' { $$ = new(CEIL, $4, ENULL); } | '@' K_COS '(' e ')' { $$ = new(COS, $4, ENULL); } | '@' K_EXP '(' e ')' { $$ = new(EXP, $4, ENULL); } | '@' K_FABS '(' e ')' { $$ = new(FABS, $4, ENULL); } | '@' K_FLOOR '(' e ')' { $$ = new(FLOOR, $4, ENULL); } | '@' K_HYPOT '(' e ',' e ')' { $$ = new(HYPOT, $4, $6); } | '@' K_LN '(' e ')' { $$ = new(LOG, $4, ENULL); } | '@' K_LOG '(' e ')' { $$ = new(LOG10, $4, ENULL); } | '@' K_POW '(' e ',' e ')' { $$ = new(POW, $4, $6); } | '@' K_SIN '(' e ')' { $$ = new(SIN, $4, ENULL); } | '@' K_SQRT '(' e ')' { $$ = new(SQRT, $4, ENULL); } | '@' K_TAN '(' e ')' { $$ = new(TAN, $4, ENULL); } | '@' K_DTR '(' e ')' { $$ = new(DTR, $4, ENULL); } | '@' K_RTD '(' e ')' { $$ = new(RTD, $4, ENULL); } | '@' K_RND '(' e ')' { $$ = new(RND, $4, ENULL); } | '@' K_ROUND '(' e ',' e ')' { $$ = new(ROUND, $4, $6); } | '@' K_IF '(' e ',' e ',' e ')' { $$ = new(IF, $4,new(',',$6,$8)); } | '@' K_PV '(' e ',' e ',' e ')' { $$ = new(PV, $4,new(':',$6,$8)); } | '@' K_FV '(' e ',' e ',' e ')' { $$ = new(FV, $4,new(':',$6,$8)); } | '@' K_PMT '(' e ',' e ',' e ')' { $$ = new(PMT, $4,new(':',$6,$8)); } | '@' K_HOUR '(' e ')' { $$ = new(HOUR, $4, ENULL); } | '@' K_MINUTE '(' e ')' { $$ = new(MINUTE, $4, ENULL); } | '@' K_SECOND '(' e ')' { $$ = new(SECOND, $4, ENULL); } | '@' K_MONTH '(' e ')' { $$ = new(MONTH, $4, ENULL); } | '@' K_DAY '(' e ')' { $$ = new(DAY, $4, ENULL); } | '@' K_YEAR '(' e ')' { $$ = new(YEAR, $4, ENULL); } | '@' K_NOW { $$ = new(NOW, ENULL, ENULL);} | '@' K_DTS '(' e ',' e ',' e ')' { $$ = new(DTS, $4, new(',', $6, $8));} | NUMBER '.' NUMBER '.' NUMBER { $$ = new(DTS, new_const(O_CONST, (double) $1), new(',', new_const(O_CONST, (double) $3), new_const(O_CONST, (double) $5)));} | '@' K_TTS '(' e ',' e ',' e ')' { $$ = new(TTS, $4, new(',', $6, $8));} | '@' K_STON '(' e ')' { $$ = new(STON, $4, ENULL); } | '@' K_EQS '(' e ',' e ')' { $$ = new(EQS, $4, $6); } | '@' K_DATE '(' e ')' { $$ = new(DATE, $4, ENULL); } | '@' K_DATE '(' e ',' e ')' { $$ = new(DATE, $4, $6); } | '@' K_FMT '(' e ',' e ')' { $$ = new(FMT, $4, $6); } | '@' K_UPPER '(' e ')' { $$ = new(UPPER, $4, ENULL); } | '@' K_LOWER '(' e ')' { $$ = new(LOWER, $4, ENULL); } | '@' K_CAPITAL '(' e ')' { $$ = new(CAPITAL, $4, ENULL); } | '@' K_INDEX '(' range ',' e ')' { $$ = new(INDEX, new_range(REDUCE | INDEX, $4), $6); } | '@' K_INDEX '(' e ',' range ')' { $$ = new(INDEX, new_range(REDUCE | INDEX, $6), $4); } | '@' K_INDEX '(' range ',' e ',' e ')' { $$ = new(INDEX, new_range(REDUCE | INDEX, $4), new(',', $6, $8)); } | '@' K_LOOKUP '(' range ',' e ')' { $$ = new(LOOKUP, new_range(REDUCE | LOOKUP, $4), $6); } | '@' K_LOOKUP '(' e ',' range ')' { $$ = new(LOOKUP, new_range(REDUCE | LOOKUP, $6), $4); } | '@' K_HLOOKUP '(' range ',' e ',' e ')' { $$ = new(HLOOKUP, new_range(REDUCE | HLOOKUP, $4), new(',', $6, $8)); } | '@' K_HLOOKUP '(' e ',' range ',' e ')' { $$ = new(HLOOKUP, new_range(REDUCE | HLOOKUP, $6), new(',', $4, $8)); } | '@' K_VLOOKUP '(' range ',' e ',' e ')' { $$ = new(VLOOKUP, new_range(REDUCE | VLOOKUP, $4), new(',', $6, $8)); } | '@' K_VLOOKUP '(' e ',' range ',' e ')' { $$ = new(VLOOKUP, new_range(REDUCE | VLOOKUP, $6), new(',', $4, $8)); } | '@' K_STINDEX '(' range ',' e ')' { $$ = new(STINDEX, new_range(REDUCE | STINDEX, $4), $6); } | '@' K_STINDEX '(' e ',' range ')' { $$ = new(STINDEX, new_range(REDUCE | STINDEX, $6), $4); } | '@' K_STINDEX '(' range ',' e ',' e ')' { $$ = new(STINDEX, new_range(REDUCE | STINDEX, $4), new(',', $6, $8)); } | '@' K_EXT '(' e ',' e ')' { $$ = new(EXT, $4, $6); } | '@' K_NVAL '(' e ',' e ')' { $$ = new(NVAL, $4, $6); } | '@' K_SVAL '(' e ',' e ')' { $$ = new(SVAL, $4, $6); } | '@' K_SUBSTR '(' e ',' e ',' e ')' { $$ = new(SUBSTR, $4, new(',', $6, $8)); } | '(' e ')' { $$ = $2; } | '+' term { $$ = $2; } | '-' term { $$ = new('m', $2, ENULL); } | NUMBER { $$ = new_const(O_CONST, (double) $1); } | FNUMBER { $$ = new_const(O_CONST, $1); } | '@' K_PI { $$ = new(PI_, ENULL, ENULL); } | STRING { $$ = new_str($1); } | '~' term { $$ = new('!', $2, ENULL); } | '!' term { $$ = new('!', $2, ENULL); } | '@' K_FILENAME '(' e ')' { $$ = new(FILENAME, $4, ENULL); } | '@' K_MYROW { $$ = new(MYROW, ENULL, ENULL);} | '@' K_MYCOL { $$ = new(MYCOL, ENULL, ENULL);} | '@' K_LASTROW { $$ = new(LASTROW, ENULL, ENULL);} | '@' K_LASTCOL { $$ = new(LASTCOL, ENULL, ENULL);} | '@' K_COLTOA '(' e ')' { $$ = new(COLTOA, $4, ENULL);} | '@' K_NUMITER { $$ = new(NUMITER, ENULL, ENULL);} | '@' K_ERR { $$ = new(ERR_, ENULL, ENULL); } | K_ERR { $$ = new(ERR_, ENULL, ENULL); } | '@' K_BLACK { $$ = new(BLACK, ENULL, ENULL); } | '@' K_RED { $$ = new(RED, ENULL, ENULL); } | '@' K_GREEN { $$ = new(GREEN, ENULL, ENULL); } | '@' K_YELLOW { $$ = new(YELLOW, ENULL, ENULL); } | '@' K_BLUE { $$ = new(BLUE, ENULL, ENULL); } | '@' K_MAGENTA { $$ = new(MAGENTA, ENULL, ENULL); } | '@' K_CYAN { $$ = new(CYAN, ENULL, ENULL); } | '@' K_WHITE { $$ = new(WHITE, ENULL, ENULL); } ; /* expressions */ e: e '+' e { $$ = new('+', $1, $3); } | e '-' e { $$ = new('-', $1, $3); } | e '*' e { $$ = new('*', $1, $3); } | e '/' e { $$ = new('/', $1, $3); } | e '%' e { $$ = new('%', $1, $3); } | e '^' e { $$ = new('^', $1, $3); } | term | e '?' e ':' e { $$ = new('?', $1, new(':', $3, $5)); } | e ';' e { $$ = new(';', $1, $3); } | e '<' e { $$ = new('<', $1, $3); } | e '=' e { $$ = new('=', $1, $3); } | e '>' e { $$ = new('>', $1, $3); } | e '&' e { $$ = new('&', $1, $3); } | e '|' e { $$ = new('|', $1, $3); } | e '<' '=' e { $$ = new('!', new('>', $1, $4), ENULL); } | e '!' '=' e { $$ = new('!', new('=', $1, $4), ENULL); } | e '<' '>' e { $$ = new('!', new('=', $1, $4), ENULL); } | e '>' '=' e { $$ = new('!', new('<', $1, $4), ENULL); } | e '#' e { $$ = new('#', $1, $3); } ; expr_list: e { $$ = new(ELIST, ENULL, $1); } | expr_list ',' e { $$ = new(ELIST, $1, $3); } ; range: var ':' var { $$.left = $1; $$.right = $3; } | RANGE { $$ = $1; } ; var: COL NUMBER { $$.vp = lookat($2, $1); $$.vf = 0; } | '$' COL NUMBER { $$.vp = lookat($3, $2); $$.vf = FIX_COL; } | COL '$' NUMBER { $$.vp = lookat($3, $1); $$.vf = FIX_ROW; } | '$' COL '$' NUMBER { $$.vp = lookat($4, $2); $$.vf = FIX_ROW | FIX_COL; } | VAR { $$ = $1.left; } ; var_or_range: range { $$ = $1; } | var { $$.left = $1; $$.right = $1; } ; num: NUMBER { $$ = (double) $1; } | FNUMBER { $$ = $1; } | '-' num { $$ = -$2; } | '+' num { $$ = $2; } ; strarg: STRING { $$ = $1; } | var { char *s, *s1; size_t l; s1 = $1.vp->label; if (!s1) s1 = "NULL_STRING"; l = strlen(s1) + 1; s = scxmalloc(l); strlcpy(s, s1, l); $$ = s; } ; /* allows >=1 'setitem's to be listed in the same 'set' command */ setlist : | setlist setitem ; /* things that you can 'set' */ setitem : K_AUTO { setauto(1); } | K_AUTOCALC { setauto(1); } | '~' K_AUTO { setauto(0); } | '~' K_AUTOCALC { setauto(0); } | '!' K_AUTO { setauto(0); } | '!' K_AUTOCALC { setauto(0); } | K_BYCOLS { setorder(BYCOLS); } | K_BYROWS { setorder(BYROWS); } | K_OPTIMIZE { optimize = 1; } | '~' K_OPTIMIZE { optimize = 0; } | '!' K_OPTIMIZE { optimize = 0; } | K_NUMERIC { numeric = 1; } | '~' K_NUMERIC { numeric = 0; } | '!' K_NUMERIC { numeric = 0; } | K_PRESCALE { prescale = 0.01; } | '~' K_PRESCALE { prescale = 1.0; } | '!' K_PRESCALE { prescale = 1.0; } | K_EXTFUN { extfunc = 1; } | '~' K_EXTFUN { extfunc = 0; } | '!' K_EXTFUN { extfunc = 0; } | K_CELLCUR { showcell = 1; } | '~' K_CELLCUR { showcell = 0; } | '!' K_CELLCUR { showcell = 0; } | K_TOPROW { showtop = 1; } | '~' K_TOPROW { showtop = 0; } | '!' K_TOPROW { showtop = 0; } | K_AUTOINSERT { autoinsert = 1; } | '~' K_AUTOINSERT { autoinsert = 0; } | '!' K_AUTOINSERT { autoinsert = 0; } | K_AUTOWRAP { autowrap = 1; } | '~' K_AUTOWRAP { autowrap = 0; } | '!' K_AUTOWRAP { autowrap = 0; } | K_CSLOP { cslop = 1; FullUpdate++; } | '~' K_CSLOP { cslop = 0; FullUpdate++; } | '!' K_CSLOP { cslop = 0; FullUpdate++; } | K_COLOR { color = 1; if (usecurses && has_colors()) { color_set(1, NULL); bkgd(COLOR_PAIR(1) | ' '); FullUpdate++; } } | '!' K_COLOR { color = 0; if (usecurses && has_colors()) { color_set(0, NULL); bkgd(COLOR_PAIR(0) | ' '); } } | '~' K_COLOR { color = 0; if (usecurses && has_colors()) { color_set(0, NULL); bkgd(COLOR_PAIR(0) | ' '); } } | K_COLORNEG { colorneg = 1; } | '!' K_COLORNEG { colorneg = 0; } | '~' K_COLORNEG { colorneg = 0; } | K_COLORERR { colorerr = 1; } | '!' K_COLORERR { colorerr = 0; } | '~' K_COLORERR { colorerr = 0; } | K_BRAILLE { braille = 1; } | '!' K_BRAILLE { braille = 0; } | '~' K_BRAILLE { braille = 0; } | K_BACKUP { dobackups = 1; } | '!' K_BACKUP { dobackups = 0; } | '~' K_BACKUP { dobackups = 0; } | K_MOUSE { mouseon(); } | '!' K_MOUSE { mouseoff(); } | '~' K_MOUSE { mouseoff(); } | K_ITERATIONS '=' NUMBER { setiterations($3); } | K_TBLSTYLE '=' NUMBER { tbl_style = $3; } | K_TBLSTYLE '=' K_TBL { tbl_style = TBL; } | K_TBLSTYLE '=' K_LATEX { tbl_style = LATEX; } | K_TBLSTYLE '=' K_SLATEX { tbl_style = SLATEX; } | K_TBLSTYLE '=' K_TEX { tbl_style = TEX; } | K_TBLSTYLE '=' K_FRAME { tbl_style = FRAME; } | K_RNDTOEVEN { rndtoeven = 1; FullUpdate++; } | '!' K_RNDTOEVEN { rndtoeven = 0; FullUpdate++; } | '~' K_RNDTOEVEN { rndtoeven = 0; FullUpdate++; } | K_CRACTION '=' NUMBER { craction = $3; } | K_ROWLIMIT '=' NUMBER { rowlimit = $3; } | K_COLLIMIT '=' NUMBER { collimit = $3; } | K_PAGESIZE '=' NUMBER { pagesize = $3; } | K_SCRC { scrc++; } | K_LOCALE { #ifdef USELOCALE struct lconv *locstruct; char *loc; loc = setlocale(LC_ALL, ""); if (loc != NULL) { locstruct = localeconv(); dpoint = (locstruct->decimal_point)[0]; thsep = (locstruct->thousands_sep)[0]; } else { dpoint = '.'; thsep = ','; } FullUpdate++; #else error("Locale support not available"); #endif } | '!' K_LOCALE { dpoint = '.'; thsep = ','; FullUpdate++; } | '~' K_LOCALE { dpoint = '.'; thsep = ','; FullUpdate++; } ; /* types of errors, to 'goto' */ errlist : K_ERROR range { num_search((double)0, $2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, CELLERROR); } | K_ERROR { num_search((double)0, 0, 0, maxrow, maxcol, CELLERROR); } | K_INVALID range { num_search((double)0, $2.left.vp->row, $2.left.vp->col, $2.right.vp->row, $2.right.vp->col, CELLINVALID); } | K_INVALID { num_search((double)0, 0, 0, maxrow, maxcol, CELLINVALID); } ; sc-7.16_1.1.2/help.c000066400000000000000000000542431326046243500137040ustar00rootroot00000000000000/* * Help functions for sc * R. Bond, 1988 * J. Buhrt 1990 * $Revision: 7.16 $ */ #include #ifdef QREF #include static char *header = " Quick Reference"; static char *revision = "$Revision: 7.16 $"; #else #include #include "compat.h" #include "sc.h" #endif /* QREF */ static char *intro[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " Overview:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " A: This overview", " B: Toggle Options", " C: Set Options", " D: Cursor movement commands", " E: Cell entry and editing commands", " F: Line Editing", " G: File commands", " H: Row and column commands", " I: Range commands", " J: Miscellaneous commands", " K: Variable names/Expressions", " L: Range functions", " M: Numeric functions", " N: String functions", " O: Financial functions", " P: Time and date functions", " ", " Q: Return to main spreadsheet", (char *)0 }; static char *toggleoptions[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " B: Toggle Options", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " ^To Toggle options. Toggle one option selected by o:", " a Recalculate automatically or on ``@'' commands.", " o Optimize expressions upon entry if enabled.", " c Current cell highlighting enable/disable.", " e External function execution enable/disable.", " l Autolabeling defined cells enable/disable.", " n If enabled, a digit starts a numeric value.", " t Top line display enable/disable.", #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) " x Encrypt/decrypt database and listing files.", #else " x Encrypt/decrypt database and listing files (Not available).", #endif " $ Dollar prescale. If enabled, all numeric constants", " (not expressions) entered are multipled by 0.01.", " r Newline action. Toggle between no action, move down", " after entry and move right after entry.", " z Set the newline action limits to the current row and column", " (for r && z see also set rowlimit=n, collimit=n)", (char *)0 }; static char *setoptions[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " C: Set Options", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " S Set options. Options include:", " autocalc Automatic recalculation.", " byrows Recalculate in row order. (default)", " bycols Recalculate in column order.", " optimize Optimize expressions upon entry. (default off)", " iterations=n Set the number of iterations allowed. (10)", " tblstyle=xx Set ``T'' output style to:", " 0 (none), tex, latex, slatex, or tbl.", " rndtoeven Round *.5 to nearest even number instead of", " always up.", " rowlimit=n Set the remembered row limit for newline action.", " collimit=n Set the remembered column limit for newline action.", " (rowlimit and collimit can both be set by ^Tz)", (char *)0 }; static char *cursor[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " D: Cell cursor movement (always OK):", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " ^N ^P Down, up", " j k l h Down, up, right, left", " Arrow keys (if the terminal and termcap support them.)", " ^Ed Go to end of range. Follow ^E by a direction indicator", " such as ^P or j.", " ", " J K L H Down, up, right, left by 1/2 pages (or 'pagesize' rows)", " SPACE Forward", " ^H Back", " TAB Forward, otherwise starts/ends a range", " ^ Up to row 0 of the current column.", " # Down to the last valid row of the current column.", " 0 Back to column A. Preface with ^U if numeric mode.", " $ Forward to the last valid column of the current row.", " b Back then up to the previous valid cell.", " w Forward then down to the next valid cell.", " g Go to a cell. Cell name, range name, quoted string,", " a number, 'error', or 'invalid' to specify which cell.", " ` ' Go to a marked cell (see help screen e for more info.", (char *)0 }; static char *cell[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " E: Cell entry and editing commands:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " = Enter a numeric constant or expression.", " < Enter a left justified string or string expression.", #if defined(QREF) && defined(TROFF) " \\\\ Enter a centered label.", #else " \\ Enter a centered label.", #endif " > Enter a right justified string or string expression.", " e Edit the current cell's numeric value.", " E Edit the current cell's string part.", " F Assign a format to the current cell's numeric value.", " x Clear the current cell.", " m Followed by any lowercase letter, marks the current cell", " with that letter.", " c Copy a marked cell to the current cell.", " + Increment numeric part", " - Decrement numeric part", " RETURN Enter insert mode if the input line was empty (ESC to edit)", " ", " In numeric mode, a decimal digit, ``+'', ``-'', and ``.'' all start", " a new numeric constant or expression.", (char *)0 }; static char *vi[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " F: Line Editor", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " Hitting the ESC key while entering any command on the top line", " will start a one-line vi-style editor. Supported commands:", " ", " ESC q Abort command entry.", " h l Move cursor forward, backward.", " 0 $ Move cursor to the beginning, end of the line.", " b w Move cursor forward/back one word.", " e Move cursor to next end-of-word.", " fc Move cursor to character c.", " tc Move the cursor to the character before c.", " i a Enter insert mode before/after the cursor.", " I A Move to column 0/end of line and enter insert mode.", " x X Delete the character under/before the cursor.", " rc Replace the character under the cursor with c.", " cm dm Change/Delete - m = b,e,f,h,l,t or w.", " R Enter replace (overstrike) mode.", " s Delete character under cursor and enter insert mode.", " + j - k / ? Forward/backward/search the command history.", " n N Repeat last history search (N = opposite direction).", " . u Repeat/undo the last command.", " ^V Enter navigate mode. Another ^V enters current cell address.", " ^W Type, in the command line, the current cell's expression.", " ^A Type, in the command line, the current cell's numeric value.", (char *)0 }; static char *file[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " G: File commands:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " G Get a new database from a file. ", " M Merge a new file into the current database.", " P Put the current database into a file.", " W Write a listing of the current database into a file in", " a form that matches its appearance on the screen.", " T Write a listing of the current database to a file, but", " put delimiters between each pair of fields.", " Optionally brackets output with control lines for ``tbl'',", " ``LaTeX'', ``SLaTex'', or ``TeX''.", " ", #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) " If encryption mode is set, file I/O will be encrypted/decrypted.", " ``\"| program\"'' for a file name will pipe (unencrypted) output to", #else " ``\"| program\"'' for a file name will pipe output to", #endif " a program for Put, Write and Table. If a cell name is used", " as the file name, the cell's string part will be used as the", " file name.", (char *)0 }; static char *row[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " H: Row and column commands:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " ir, ic Insert a new, empty row (column)", " ar, ac Append a new copy of the current row (column)", " dr, dc Delete the current row (column)", " pr, pc, pm Pull deleted cells back into the spreadsheet", " Insert rows, columns or merge the cells.", " vr, vc Remove expressions from the affected rows (columns),", " leaving only the values.", " Zr, Zc Hide (``zap'') the current row (column)", " sr, sc Show hidden rows (columns)", " f Set the output format to be used with the values of", " each cell in this column. Enter field width and", " number of fractional digits. A preceding count can be", " used to change more than one column.", " ", " Commands which move or copy cells also modify the row and column ", " references in the new cell expressions. Use ``fixed'' or the", " ``$'' style cell reference to supress the change.", " ", " @myrow, @mycol return the row or column of the current cell", " @lastrow, @lastcol return the row or column of the current cell", (char *)0 }; static char *range[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " I: Range commands:", #if defined(QREF) && defined(TROFF) ".Lp", #endif " rx Clear a range. ", " rv Remove the expressions from a range of cells, leaving ", " just the values.", " rc Copy a source range to a destination range.", " rf Fill a range with constant values starting with a given", " value and increasing by a given increment.", " rd Assign a name to a cell or a range of cells. Give the", " the name, surrounded by quotes, and either a cell name such", " as ``A10'' or a range such as ``a1:b20''.", " rl Locks a cell or a range of cells, i.e makes it unchangeable.", " rU Unlocks a locked cell, i.e makes it changeable.", " rs Shows the currently defined range names. Pipe output to", " sort, then to less.", " ru Use this command to undefine a previously defined range name.", " rF Assign a format string to a range of cells.", " ", " Range operations affect a rectangular region on the screen", " defined by the upper left and lower right cells in the region.", " A range is specified by giving the cell names separated by ``:'',", " such as ``a20:k52''. Another way to refer to a range is to use", " a name previously defined using ``rd''.", (char *)0 }; static char *misc[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " J: Miscellaneous commands:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " Q q ^C Exit from the program.", " ^G ESC Abort entry of the current command.", " ? Help", " ! Shell escape. Enter a command to run. ``!!'' repeats", " the last command. Just ``!'' starts an interactive shell.", " ^L Redraw the screen.", " ^R Redraw the screen. Highlight cells with values but no", " expressions.", " ^X Redraw the screen. Show formulas, not values.", " C Redraw the screen with the row containing the current cell", " centered.", " @ Recalculate the spreadsheet.", " TAB When the character cursor is on the top line TAB can be used", " to start or stop the display of the default range.", (char *)0 }; static char *var[] = { " ", #if defined(QREF) && defined(TROFF) ".SH", #endif " K: Variable names:", " ", #if defined(QREF) && defined(TROFF) ".Lp", #endif " K20 Row and column can vary on copies.", " $K$20 Row and column stay fixed on copies.", " $K20 Row can vary; column stays fixed on copies.", " K$20 Row stays fixed; column can vary on copies.", " fixed holds following expession fixed on copies.", " Cells and ranges can be given a symbolic name via ``rd''.", " ", " Expressions:", " -e Negation e<=e Less than or equal", " e+e Addition e=e Equal", " e-e Subtraction e!=e Not Equal", " e*e Multiplication e>=e Greater than or equal", " e/e Division e>e Greater than", " e%e Modulo e #ifdef aiws #undef _C_func /* Fixes for undefined symbols on AIX */ #endif #ifdef USE_IEEEFP_H # include #endif #include #include #include #include #include #include #include #include #include #ifndef MSDOS #include #endif #ifdef REGCOMP #include #endif #include "compat.h" #include "sc.h" #ifdef RE_COMP extern char *re_comp(char *s); extern char *re_exec(char *s); #endif #ifdef REGCMP char *regcmp(); char *regex(); #endif /* Use this structure to save the last 'g' command */ struct go_save gs; /* g_type can be: */ #define G_NONE 0 /* Starting value - must be 0 */ #define G_NUM 1 #define G_STR 2 #define G_NSTR 3 #define G_XSTR 4 #define G_CELL 5 #define ISVALID(r,c) ((r)>=0 && (r)=0 && (c)e.o.left) - 1; c = minc + (int) eval(val->e.o.right) - 1; } if (c <= maxc && c >=minc && r <= maxr && r >=minr) p = *ATBL(tbl, r, c); if (p && p->label) { size_t l = strlen(p->label) + 1; pr = scxmalloc(l); strlcpy(pr, p->label, l); if (p->cellerror) cellerror = CELLINVALID; return (pr); } else return ((char *)0); } static double doindex(int minr, int minc, int maxr, int maxc, struct enode *val) { int r, c; register struct ent *p; if (val->op == ',') { /* index by both row and column */ r = minr + (int) eval(val->e.o.left) - 1; c = minc + (int) eval(val->e.o.right) - 1; } else if (minr == maxr) { /* look along the row */ r = minr; c = minc + (int) eval(val) - 1; } else if (minc == maxc) { /* look down the column */ r = minr + (int) eval(val) - 1; c = minc; } else { error("Improper indexing operation"); return (double) 0; } if (c <= maxc && c >=minc && r <= maxr && r >=minr && (p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->cellerror) cellerror = CELLINVALID; return p->v; } else return (double) 0; } double dolookup(struct enode * val, int minr, int minc, int maxr, int maxc, int offset, int vflag) { double v, ret = (double)0; int r, c; register struct ent *p = (struct ent *)0; int incr, incc, fndr, fndc; char *s; incr = vflag; incc = 1 - vflag; if (etype(val) == NUM) { cellerror = CELLOK; v = eval(val); for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) { if ((p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->v <= v) { fndr = incc ? (minr + offset) : r; fndc = incr ? (minc + offset) : c; if (ISVALID(fndr, fndc)) p = *ATBL(tbl, fndr, fndc); else { error(" range specified to @[hv]lookup"); cellerror = CELLERROR; } if (p && p->flags&IS_VALID) { if (p->cellerror) cellerror = CELLINVALID; ret = p->v; } } else break; } } } else { cellerror = CELLOK; s = seval(val); for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) { if ((p = *ATBL(tbl, r, c)) && p->label) { if (strcmp(p->label,s) == 0) { fndr = incc ? (minr + offset) : r; fndc = incr ? (minc + offset) : c; if (ISVALID(fndr,fndc)) { p = *ATBL(tbl, fndr, fndc); if (p->cellerror) cellerror = CELLINVALID; } else { error(" range specified to @[hv]lookup"); cellerror = CELLERROR; } break; } } } if (p && p->flags&IS_VALID) ret = p->v; scxfree(s); } return ret; } static double docount(int minr, int minc, int maxr, int maxc, struct enode *e) { int v; int r, c; int cellerr = CELLOK; register struct ent *p; v = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags & IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; v++; } } cellerror = cellerr; rowoffset = coloffset = 0; return v; } static double dosum(int minr, int minc, int maxr, int maxc, struct enode *e) { double v; int r, c; int cellerr = CELLOK; register struct ent *p; v = (double)0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags & IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; v += p->v; } } cellerror = cellerr; rowoffset = coloffset = 0; return v; } static double doprod(int minr, int minc, int maxr, int maxc, struct enode *e) { double v; int r, c; int cellerr = CELLOK; register struct ent *p; v = 1; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags & IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; v *= p->v; } } cellerror = cellerr; rowoffset = coloffset = 0; return v; } static double doavg(int minr, int minc, int maxr, int maxc, struct enode *e) { double v; int r, c; int count; int cellerr = CELLOK; register struct ent *p; v = (double)0; count = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; v += p->v; count++; } } cellerror = cellerr; rowoffset = coloffset = 0; if (count == 0) return ((double)0); return (v / (double)count); } static double dostddev(int minr, int minc, int maxr, int maxc, struct enode *e) { double lp, rp, v, nd; int r, c; int n; int cellerr = CELLOK; register struct ent *p; n = 0; lp = 0; rp = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; v = p->v; lp += v*v; rp += v; n++; } } cellerror = cellerr; rowoffset = coloffset = 0; if ((n == 0) || (n == 1)) return ((double)0); nd = (double)n; return (sqrt((nd*lp-rp*rp)/(nd*(nd-1)))); } static double domax(int minr, int minc, int maxr, int maxc, struct enode *e) { double v = (double)0; int r, c; int count; int cellerr = CELLOK; register struct ent *p; count = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; if (!count) { v = p->v; count++; } else if (p->v > v) v = p->v; } } cellerror = cellerr; rowoffset = coloffset = 0; if (count == 0) return ((double)0); return (v); } static double domin(int minr, int minc, int maxr, int maxc, struct enode *e) { double v = (double)0; int r, c; int count; int cellerr = CELLOK; register struct ent *p; count = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { if (e) { rowoffset = r - minr; coloffset = c - minc; } if (!e || eval(e)) if ((p = *ATBL(tbl, r, c)) && p->flags&IS_VALID) { if (p->cellerror) cellerr = CELLINVALID; if (!count) { v = p->v; count++; } else if (p->v < v) v = p->v; } } cellerror = cellerr; rowoffset = coloffset = 0; if (count == 0) return ((double)0); return (v); } int mdays[12]={ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static double dodts(int e1, int e2, int e3) { int yr, mo, day; time_t secs; struct tm t; if (e2 > 12 || e3 > 31) { mo = e1; day = e2; yr = e3; } else { yr = e1; mo = e2; day = e3; } mdays[1] = 28 + (yr%4 == 0) - (yr%100 == 0) + (yr%400 == 0); t.tm_hour = t.tm_min = t.tm_sec = 0; t.tm_mon = --mo; t.tm_mday = day; t.tm_year = yr -= 1900; t.tm_isdst = -1; if (mo < 0 || mo > 11 || day < 1 || day > mdays[mo] || (secs = mktime(&t)) == -1) { error("@dts: invalid argument or date out of range"); cellerror = CELLERROR; return (0.0); } return ((double)secs); } static double dotts(int hr, int min, int sec) { if (hr < 0 || hr > 23 || min < 0 || min > 59 || sec < 0 || sec > 59) { error ("@tts: Invalid argument"); cellerror = CELLERROR; return ((double) 0); } return ((double)(sec+min*60+hr*3600)); } static double dotime(int which, double when) { static time_t t_cache; static struct tm tm_cache; struct tm *tp; time_t tloc; if (which == NOW) return (double)time(NULL); tloc = (time_t)when; if (tloc != t_cache) { tp = localtime(&tloc); tm_cache = *tp; tm_cache.tm_mon += 1; tm_cache.tm_year += 1900; t_cache = tloc; } switch (which) { case HOUR: return ((double)(tm_cache.tm_hour)); case MINUTE: return ((double)(tm_cache.tm_min)); case SECOND: return ((double)(tm_cache.tm_sec)); case MONTH: return ((double)(tm_cache.tm_mon)); case DAY: return ((double)(tm_cache.tm_mday)); case YEAR: return ((double)(tm_cache.tm_year)); } /* Safety net */ cellerror = CELLERROR; return ((double)0); } static double doston(char *s) { double v; if (!s) return ((double)0); v = strtod(s, NULL); scxfree(s); return(v); } static double doeqs(char *s1, char *s2) { double v; if (!s1 && !s2) return ((double)1.0); if (!s1 || !s2) v = 0.0; else if (strcmp(s1, s2) == 0) v = 1.0; else v = 0.0; if (s1) scxfree(s1); if (s2) scxfree(s2); return(v); } /* * Given a string representing a column name and a value which is a row * number, return a pointer to the selected cell's entry, if any, else NULL. * Use only the integer part of the column number. Always free the string. */ static struct ent * getent(char *colstr, double rowdoub) { int collen; /* length of string */ int row, col; /* integer values */ struct ent *p = (struct ent *)0; /* selected entry */ if (!colstr) { cellerror = CELLERROR; return ((struct ent *)0); } if (((row = (int) floor(rowdoub)) >= 0) && (row < maxrows) /* in range */ && ((collen = strlen (colstr)) <= 2) /* not too long */ && ((col = atocol (colstr, collen)) >= 0) && (col < maxcols)) { /* in range */ p = *ATBL(tbl, row, col); if ((p != NULL) && p->cellerror) cellerror = CELLINVALID; } scxfree(colstr); return (p); } /* * Given a string representing a column name and a value which is a column * number, return the selected cell's numeric value, if any. */ static double donval(char *colstr, double rowdoub) { struct ent *ep; return (((ep = getent(colstr, rowdoub)) && ((ep->flags) & IS_VALID)) ? (ep->v) : (double)0); } /* * The list routines (e.g. dolmax) are called with an LMAX enode. * The left pointer is a chain of ELIST nodes, the right pointer * is a value. */ static double dolmax(struct enode *ep) { register int count = 0; register double maxval = 0; /* Assignment to shut up lint */ register struct enode *p; register double v; cellerror = CELLOK; for (p = ep; p; p = p->e.o.left) { v = eval(p->e.o.right); if (!count || v > maxval) maxval = v, count++; } if (count) return maxval; else return (double)0; } static double dolmin(struct enode *ep) { register int count = 0; register double minval = 0; /* Assignment to shut up lint */ register struct enode *p; register double v; cellerror = CELLOK; for (p = ep; p; p = p->e.o.left) { v = eval(p->e.o.right); if (!count || v < minval) minval = v, count++; } if (count) return minval; else return (double)0; } double eval(register struct enode *e) { if (e == (struct enode *)0) { cellerror = CELLINVALID; return (double)0; } switch (e->op) { case '+': return (eval(e->e.o.left) + eval(e->e.o.right)); case '-': { double l, r; l = eval(e->e.o.left); r = eval(e->e.o.right); return l - r; } case '*': return (eval(e->e.o.left) * eval(e->e.o.right)); case '/': { double num, denom; num = eval(e->e.o.left); denom = eval(e->e.o.right); if (cellerror) { cellerror = CELLINVALID; return ((double) 0); } else if (denom) return (num/denom); else { cellerror = CELLERROR; return ((double) 0); } } case '%': { double num, denom; num = floor(eval(e->e.o.left)); denom = floor(eval(e->e.o.right)); if (denom) return (num - floor(num/denom)*denom); else { cellerror = CELLERROR; return ((double) 0); } } case '^': return (fn2_eval(pow,eval(e->e.o.left),eval(e->e.o.right))); case '<': return (eval(e->e.o.left) < eval(e->e.o.right)); case '=': { double l, r; l = eval(e->e.o.left); r = eval(e->e.o.right); return (l == r); } case '>': return (eval(e->e.o.left) > eval(e->e.o.right)); case '&': return (eval(e->e.o.left) && eval(e->e.o.right)); case '|': return (eval(e->e.o.left) || eval(e->e.o.right)); case IF: case '?': return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left) : eval(e->e.o.right->e.o.right); case 'm': return (-eval(e->e.o.left)); case 'f': { int rtmp = rowoffset; int ctmp = coloffset; double ret; rowoffset = coloffset = 0; ret = eval(e->e.o.left); rowoffset = rtmp; coloffset = ctmp; return (ret); } case 'F': return (eval(e->e.o.left)); case '!': return (eval(e->e.o.left) == 0.0); case ';': return (((int)eval(e->e.o.left) & 7) + (((int)eval(e->e.o.right) & 7) << 3)); case O_CONST: if (! #ifdef HAVE_ISFINITE isfinite( #else finite( #endif e->e.k)) { e->op = ERR_; e->e.k = (double) 0; cellerror = CELLERROR; } return (e->e.k); case O_VAR: { struct ent *vp = e->e.v.vp; int row, col; if (vp && (rowoffset || coloffset)) { row = e->e.v.vf & FIX_ROW ? vp->row : vp->row + rowoffset; col = e->e.v.vf & FIX_COL ? vp->col : vp->col + coloffset; checkbounds(&row, &col); vp = *ATBL(tbl, row, col); } if (!vp || vp->flags & IS_DELETED) { cellerror = CELLERROR; return (double) 0; } if (vp->cellerror) cellerror = CELLINVALID; return (vp->v); } case SUM: case PROD: case AVG: case COUNT: case STDDEV: case MAX: case MIN: case INDEX: case LOOKUP: case HLOOKUP: case VLOOKUP: { int r, c; int maxr, maxc; int minr, minc; maxr = e->e.o.left->e.r.right.vp->row; maxc = e->e.o.left->e.r.right.vp->col; minr = e->e.o.left->e.r.left.vp->row; minc = e->e.o.left->e.r.left.vp->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; switch (e->op) { case LOOKUP: return dolookup(e->e.o.right, minr, minc, maxr, maxc, 1, minc==maxc); case HLOOKUP: return dolookup(e->e.o.right->e.o.left, minr,minc,maxr,maxc, (int) eval(e->e.o.right->e.o.right), 0); case VLOOKUP: return dolookup(e->e.o.right->e.o.left, minr,minc,maxr,maxc, (int) eval(e->e.o.right->e.o.right), 1); case INDEX: return doindex(minr, minc, maxr, maxc, e->e.o.right); case SUM: return dosum(minr, minc, maxr, maxc, e->e.o.right); case PROD: return doprod(minr, minc, maxr, maxc, e->e.o.right); case AVG: return doavg(minr, minc, maxr, maxc, e->e.o.right); case COUNT: return docount(minr, minc, maxr, maxc, e->e.o.right); case STDDEV: return dostddev(minr, minc, maxr, maxc, e->e.o.right); case MAX: return domax(minr, minc, maxr, maxc, e->e.o.right); case MIN: return domin(minr, minc, maxr, maxc, e->e.o.right); } } case REDUCE | 'R': case REDUCE | 'C': { int r, c; int maxr, maxc; int minr, minc; maxr = e->e.r.right.vp->row; maxc = e->e.r.right.vp->col; minr = e->e.r.left.vp->row; minc = e->e.r.left.vp->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; switch (e->op) { case REDUCE | 'R': return (maxr - minr + 1); case REDUCE | 'C': return (maxc - minc + 1); } } case ABS: return (fn1_eval( fabs, eval(e->e.o.left))); case ACOS: return (fn1_eval( acos, eval(e->e.o.left))); case ASIN: return (fn1_eval( asin, eval(e->e.o.left))); case ATAN: return (fn1_eval( atan, eval(e->e.o.left))); case ATAN2: return (fn2_eval( atan2, eval(e->e.o.left), eval(e->e.o.right))); case CEIL: return (fn1_eval( ceil, eval(e->e.o.left))); case COS: return (fn1_eval( cos, eval(e->e.o.left))); case EXP: return (fn1_eval( exp, eval(e->e.o.left))); case FABS: return (fn1_eval( fabs, eval(e->e.o.left))); case FLOOR: return (fn1_eval( floor, eval(e->e.o.left))); case HYPOT: return (fn2_eval( hypot, eval(e->e.o.left), eval(e->e.o.right))); case LOG: return (fn1_eval( log, eval(e->e.o.left))); case LOG10: return (fn1_eval( log10, eval(e->e.o.left))); case POW: return (fn2_eval( pow, eval(e->e.o.left), eval(e->e.o.right))); case SIN: return (fn1_eval( sin, eval(e->e.o.left))); case SQRT: return (fn1_eval( sqrt, eval(e->e.o.left))); case TAN: return (fn1_eval( tan, eval(e->e.o.left))); case DTR: return (dtr(eval(e->e.o.left))); case RTD: return (rtd(eval(e->e.o.left))); case RND: if (rndtoeven) return rint(eval(e->e.o.left)); else { double temp = eval(e->e.o.left); return (temp - floor(temp) < 0.5 ? floor(temp) : ceil(temp)); } case ROUND: { int prec = (int) eval(e->e.o.right); double scale = 1; if (0 < prec) do scale *= 10; while (0 < --prec); else if (prec < 0) do scale /= 10; while (++prec < 0); if (rndtoeven) return (rint(eval(e->e.o.left) * scale) / scale); else { double temp = eval(e->e.o.left); temp *= scale; /* xxx */ /* temp = (temp > 0.0 ? floor(temp + 0.5) : ceil(temp - 0.5)); */ temp = ((temp - floor(temp)) < 0.5 ? floor(temp) : ceil(temp)); return (temp / scale); } } case FV: case PV: case PMT: return (finfunc(e->op, eval(e->e.o.left), eval(e->e.o.right->e.o.left), eval(e->e.o.right->e.o.right))); case HOUR: return (dotime(HOUR, eval(e->e.o.left))); case MINUTE: return (dotime(MINUTE, eval(e->e.o.left))); case SECOND: return (dotime(SECOND, eval(e->e.o.left))); case MONTH: return (dotime(MONTH, eval(e->e.o.left))); case DAY: return (dotime(DAY, eval(e->e.o.left))); case YEAR: return (dotime(YEAR, eval(e->e.o.left))); case NOW: return (dotime(NOW, (double)0.0)); case DTS: return (dodts((int)eval(e->e.o.left), (int)eval(e->e.o.right->e.o.left), (int)eval(e->e.o.right->e.o.right))); case TTS: return (dotts((int)eval(e->e.o.left), (int)eval(e->e.o.right->e.o.left), (int)eval(e->e.o.right->e.o.right))); case STON: return (doston(seval(e->e.o.left))); case EQS: return (doeqs(seval(e->e.o.right), seval(e->e.o.left))); case LMAX: return dolmax(e); case LMIN: return dolmin(e); case NVAL: return (donval(seval(e->e.o.left),eval(e->e.o.right))); case MYROW: return ((double) (gmyrow + rowoffset)); case MYCOL: return ((double) (gmycol + coloffset)); case LASTROW: return ((double) maxrow); case LASTCOL: return ((double) maxcol); case NUMITER: return ((double) repct); case ERR_: cellerror = CELLERROR; return ((double) 0); case PI_: return ((double) M_PI); case BLACK: return ((double) COLOR_BLACK); case RED: return ((double) COLOR_RED); case GREEN: return ((double) COLOR_GREEN); case YELLOW: return ((double) COLOR_YELLOW); case BLUE: return ((double) COLOR_BLUE); case MAGENTA: return ((double) COLOR_MAGENTA); case CYAN: return ((double) COLOR_CYAN); case WHITE: return ((double) COLOR_WHITE); default: error ("Illegal numeric expression"); exprerr = 1; } cellerror = CELLERROR; return ((double)0.0); } static #ifdef SIGVOID void #else int #endif eval_fpe(int i) /* Trap for FPE errors in eval */ { (void)i; #if defined(i386) && !defined(M_XENIX) asm(" fnclex"); asm(" fwait"); #else #ifdef IEEE_MATH (void)fpsetsticky((fp_except)0); /* Clear exception */ #endif /* IEEE_MATH */ #ifdef PC _fpreset(); #endif #endif /* re-establish signal handler for next time */ (void) signal(SIGFPE, eval_fpe); longjmp(fpe_save, 1); } double fn1_eval(double (*fn)(), double a) { double res; errno = 0; res = (*fn)(a); if (errno) cellerror = CELLERROR; return res; } double fn2_eval(double (*fn)(), double arg1, double arg2) { double res; errno = 0; res = (*fn)(arg1, arg2); if (errno) cellerror = CELLERROR; return res; } /* * Rules for string functions: * Take string arguments which they scxfree. * All returned strings are assumed to be xalloced. */ static char * docat(register char *s1, register char *s2) { register char *p; char *arg1, *arg2; size_t l; if (!s1 && !s2) return ((char *)0); arg1 = s1 ? s1 : ""; arg2 = s2 ? s2 : ""; l = strlen(arg1) + strlen(arg2) + 1; p = scxmalloc(l); strlcpy(p, arg1, l); strlcat(p, arg2, l); if (s1) scxfree(s1); if (s2) scxfree(s2); return (p); } static char * dodate(time_t tloc, char *fmtstr) { char buff[FBUFLEN]; char *p; size_t l; if (!fmtstr) fmtstr = "%a %b %d %H:%M:%S %Y"; strftime(buff, FBUFLEN, fmtstr, localtime(&tloc)); l = strlen(buff) + 1; p = scxmalloc(l); strlcpy(p, buff, l); return (p); } static char * dofmt(char *fmtstr, double v) { char buff[FBUFLEN]; char *p; size_t l; if (!fmtstr) return ((char *)0); (void) snprintf(buff, FBUFLEN, fmtstr, v); l = strlen(buff) + 1; p = scxmalloc(l); strlcpy(p, buff, l); scxfree(fmtstr); return (p); } /* * Given a command name and a value, run the command with the given value and * read and return its first output line (only) as an allocated string, always * a copy of se->e.o.s, which is set appropriately first unless external * functions are disabled, in which case the previous value is used. The * handling of se->e.o.s and freeing of command is tricky. Returning an * allocated string in all cases, even if null, insures cell expressions are * written to files, etc. */ #if defined(VMS) || defined(MSDOS) char * doext(struct enode *se) { char *command = seval(se->e.o.left); double value = eval(se->e.o.right); char *buf; error("Warning: External functions unavailable on VMS"); cellerror = CELLERROR; /* not sure if this should be a cellerror */ if (command) scxfree(command); buf = scxmalloc(1); strlcpy(buf, "\0", 1); return buf; } #else /* if defined(VMS) || defined(MSDOS) */ static char * doext(struct enode *se) { char buff[FBUFLEN]; /* command line/return, not permanently alloc */ char *command; double value; char *buf; size_t l; command = seval(se->e.o.left); value = eval(se->e.o.right); if (!extfunc) { error("Warning: external functions disabled; using %s value", (se->e.o.s && *se->e.o.s) ? "previous" : "null"); if (command) scxfree(command); } else { if ((! command) || (! *command)) { error ("Warning: external function given null command name"); cellerror = CELLERROR; if (command) scxfree(command); } else { FILE *pp; snprintf(buff, sizeof buff, "%s %g", command, value); /* build cmd line */ scxfree(command); error("Running external function..."); (void) refresh(); if ((pp = popen(buff, "r")) == (FILE *) NULL) { /* run it */ error("Warning: running \"%s\" failed", buff); cellerror = CELLERROR; } else { if (fgets(buff, sizeof(buff)-1, pp) == NULL) { /* one line */ error("Warning: external function returned nothing"); } else { char *cp; CLEAR_LINE; /* erase notice */ buff[sizeof(buff)-1] = '\0'; if ((cp = strchr(buff, '\n'))) /* contains newline */ *cp = '\0'; /* end string there */ l = strlen(buff); if (!se->e.o.s || l != strlen(se->e.o.s)) se->e.o.s = scxrealloc(se->e.o.s, l); strlcpy(se->e.o.s, buff, l); /* save alloc'd copy */ } (void) pclose(pp); } /* else */ } /* else */ } /* else */ if (se->e.o.s) { l = strlen(se->e.o.s) + 1; buf = scxmalloc(l); strlcpy(buf, se->e.o.s, l); } else { buf = scxmalloc(1); strlcpy(buf, "", 1); } return buf; } #endif /* if !(defined(VMS) || defined(MSDOS)) */ /* * Given a string representing a column name and a value which is a column * number, return the selected cell's string value, if any. Even if none, * still allocate and return a null string so the cell has a label value so * the expression is saved in a file, etc. */ static char * dosval(char *colstr, double rowdoub) { struct ent *ep; char *llabel; char *buf; size_t l; llabel = (ep = getent(colstr, rowdoub)) ? (ep -> label) : ""; l = strlen(llabel) + 1; buf = scxmalloc(l); strlcpy(buf, llabel, l); return buf; } /* * Substring: Note that v1 and v2 are one-based to users, but zero-based * when calling this routine. */ static char * dosubstr(char *s, register int v1, register int v2) { register char *s1, *s2; char *p; if (!s) return ((char *)0); if (v2 >= (ssize_t)strlen(s)) /* past end */ v2 = strlen(s) - 1; /* to end */ if (v1 < 0 || v1 > v2) { /* out of range, return null string */ scxfree(s); p = scxmalloc(1); p[0] = '\0'; return (p); } s2 = p = scxmalloc(v2-v1+2); s1 = &s[v1]; for (; v1 <= v2; s1++, s2++, v1++) *s2 = *s1; *s2 = '\0'; scxfree(s); return(p); } /* * character casing: make upper case, make lower case */ static char * docase(int acase, char *s) { char *p = s; if (s == NULL) return(NULL); if( acase == UPPER ) { while( *p != '\0' ) { if( islower((int)*p) ) *p = toupper((int)*p); p++; } } else if (acase == LOWER) { while (*p != '\0') { if (isupper((int)*p)) *p = tolower((int)*p); p++; } } return (s); } /* * make proper capitals of every word in a string * if the string has mixed case we say the string is lower * and we will upcase only first letters of words * if the string is all upper we will lower rest of words. */ static char * docapital(char *s) { char *p; int skip = 1; int AllUpper = 1; if (s == NULL) return (NULL); for (p = s; *p != '\0' && AllUpper != 0; p++) if (isalpha((int)*p) && islower((int)*p)) AllUpper = 0; for (p = s; *p != '\0'; p++) { if (!isalnum((int)*p)) skip = 1; else if (skip == 1) { skip = 0; if (islower((int)*p)) *p = toupper((int)*p); } else /* if the string was all upper before */ if (isupper((int)*p) && AllUpper != 0) *p = tolower((int)*p); } return (s); } char * seval(register struct enode *se) { register char *p; size_t l; char *buf; if (se == (struct enode *)0) return (char *)0; switch (se->op) { case O_SCONST: l = strlen(se->e.s) + 1; p = scxmalloc(l); strlcpy(p, se->e.s, l); return (p); case O_VAR: { struct ent *vp = se->e.v.vp; int row, col; if (vp && (rowoffset || coloffset)) { row = se->e.v.vf & FIX_ROW ? vp->row : vp->row + rowoffset; col = se->e.v.vf & FIX_COL ? vp->col : vp->col + coloffset; checkbounds(&row, &col); vp = *ATBL(tbl, row, col); } if (!vp || !vp->label) return (NULL); l = strlen(vp->label) + 1; p = scxmalloc(l); strlcpy(p, vp->label, l); return (p); } case '#': return (docat(seval(se->e.o.left), seval(se->e.o.right))); case 'f': { int rtmp = rowoffset; int ctmp = coloffset; char *ret; rowoffset = coloffset = 0; ret = seval(se->e.o.left); rowoffset = rtmp; coloffset = ctmp; return (ret); } case 'F': return (seval(se->e.o.left)); case IF: case '?': return (eval(se->e.o.left) ? seval(se->e.o.right->e.o.left) : seval(se->e.o.right->e.o.right)); case DATE: return (dodate((time_t)(eval(se->e.o.left)), seval(se->e.o.right))); case FMT: return (dofmt(seval(se->e.o.left), eval(se->e.o.right))); case UPPER: return (docase(UPPER, seval(se->e.o.left))); case LOWER: return (docase(LOWER, seval(se->e.o.left))); case CAPITAL:return (docapital(seval(se->e.o.left))); case STINDEX: { int r, c; int maxr, maxc; int minr, minc; maxr = se->e.o.left->e.r.right.vp->row; maxc = se->e.o.left->e.r.right.vp->col; minr = se->e.o.left->e.r.left.vp->row; minc = se->e.o.left->e.r.left.vp->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; return dostindex(minr, minc, maxr, maxc, se->e.o.right); } case EXT: return (doext(se)); case SVAL: return (dosval(seval(se->e.o.left), eval(se->e.o.right))); case SUBSTR: return (dosubstr(seval(se->e.o.left), (int)eval(se->e.o.right->e.o.left) - 1, (int)eval(se->e.o.right->e.o.right) - 1)); case COLTOA: buf = scxmalloc(10); strlcpy(buf, coltoa((int)eval(se->e.o.left)), 10); return buf; case FILENAME: { int n = eval(se->e.o.left); char *s = strrchr(curfile, '/'); if (n || s++ == NULL) s = curfile; l = strlen(s) + 1; p = scxmalloc(l); strlcpy(p, s, l); return (p); } default: error("Illegal string expression"); exprerr = 1; return (NULL); } } /* * The graph formed by cell expressions which use other cells's values is not * evaluated "bottom up". The whole table is merely re-evaluated cell by cell, * top to bottom, left to right, in RealEvalAll(). Each cell's expression uses * constants in other cells. However, RealEvalAll() notices when a cell gets a * new numeric or string value, and reports if this happens for any cell. * EvalAll() repeats calling RealEvalAll() until there are no changes or the * evaluation count expires. */ int propagation = 10; /* max number of times to try calculation */ int repct = 1; /* Make repct a global variable so that the function @numiter can access it */ void setiterations(int i) { if (i < 1) { error("iteration count must be at least 1"); propagation = 1; } else propagation = i; } void EvalAll(void) { int lastcnt, pair, v; repct = 1; (void) signal(SIGFPE, eval_fpe); while ((lastcnt = RealEvalAll()) && (++repct <= propagation)); if ((propagation > 1) && (lastcnt > 0)) error("Still changing after %d iterations", repct - 1); if (usecurses && color && has_colors()) { for (pair = 0; pair < 8; pair++) { cellerror = CELLOK; if (cpairs[pair] && cpairs[pair]->expr) { v = (int) eval(cpairs[pair]->expr); cpairs[pair]->fg = v & 7; cpairs[pair]->bg = (v >> 3) & 7; init_pair(pair + 1, cpairs[pair]->fg, cpairs[pair]->bg); } /* Can't see to fix the problem if color 1 has an error, so * turn off color in that case. */ if (pair == 0 && cellerror) { color = 0; attron(COLOR_PAIR(0)); color_set(0, NULL); error("Error in color 1: color turned off"); } } } (void) signal(SIGFPE, doquit); } /* * Evaluate all cells which have expressions and alter their numeric or string * values. Return the number of cells which changed. */ static int RealEvalAll(void) { register int i,j; int chgct = 0; register struct ent *p; if (calc_order == BYROWS) { for (i=0; i<=maxrow; i++) for (j=0; j<=maxcol; j++) if ((p = *ATBL(tbl,i,j)) && p->expr) RealEvalOne(p,i,j,&chgct); } else if (calc_order == BYCOLS) { for (j=0; j<=maxcol; j++) { for (i=0; i<=maxrow; i++) if ((p = *ATBL(tbl,i,j)) && p->expr) RealEvalOne(p,i,j,&chgct); } } else error("Internal error calc_order"); return (chgct); } void RealEvalOne(register struct ent *p, int i, int j, int *chgct) { gmyrow=i; gmycol=j; if (p->flags & IS_STREXPR) { char *v; if (setjmp(fpe_save)) { error("Floating point exception %s", v_name(i, j)); cellerror = CELLERROR; v = ""; } else { cellerror = CELLOK; v = seval(p->expr); } p->cellerror = cellerror; if (!v && !p->label) /* Everything's fine */ return; if (!p->label || !v || strcmp(v, p->label) != 0 || cellerror) { (*chgct)++; p->flags |= IS_CHANGED; changed++; } if (p->label) scxfree(p->label); p->label = v; } else { double v; if (setjmp(fpe_save)) { error("Floating point exception %s", v_name(i, j)); cellerror = CELLERROR; v = (double)0.0; } else { cellerror = CELLOK; v = eval(p->expr); if (cellerror == CELLOK && ! #ifdef HAVE_ISFINITE isfinite( #else finite( #endif v)) cellerror = CELLERROR; } if ((cellerror != p->cellerror) || (v != p->v)) { p->cellerror = cellerror; p->v = v; if (!cellerror) /* don't keep eval'ing an error */ (*chgct)++; p->flags |= IS_CHANGED|IS_VALID; changed++; } } } struct enode * new(int op, struct enode *a1, struct enode *a2) { register struct enode *p; if (freeenodes) { p = freeenodes; freeenodes = p->e.o.left; } else p = scxmalloc(sizeof(struct enode)); p->op = op; p->e.o.left = a1; p->e.o.right = a2; p->e.o.s = NULL; return p; } struct enode * new_var(int op, struct ent_ptr a1) { register struct enode *p; if (freeenodes) { p = freeenodes; freeenodes = p->e.o.left; } else p = scxmalloc(sizeof(struct enode)); p->op = op; p->e.v = a1; return p; } struct enode * new_range(int op, struct range_s a1) { register struct enode *p; if (freeenodes) { p = freeenodes; freeenodes = p->e.o.left; } else p = scxmalloc(sizeof(struct enode)); p->op = op; p->e.r = a1; return p; } struct enode * new_const(int op, double a1) { register struct enode *p; if (freeenodes) { /* reuse an already free'd enode */ p = freeenodes; freeenodes = p->e.o.left; } else p = scxmalloc(sizeof(struct enode)); p->op = op; p->e.k = a1; return p; } struct enode * new_str(char *s) { register struct enode *p; if (freeenodes) { /* reuse an already free'd enode */ p = freeenodes; freeenodes = p->e.o.left; } else p = scxmalloc(sizeof(struct enode)); p->op = O_SCONST; p->e.s = s; return (p); } void copy(struct ent *dv1, struct ent *dv2, struct ent *v1, struct ent *v2) { struct ent *p; static int minsr = -1, minsc = -1; static int maxsr = -1, maxsc = -1; int mindr, mindc; int maxdr, maxdc; int r, c; int deltar, deltac; if (dv1) { mindr = dv1->row; mindc = dv1->col; maxdr = dv2->row; maxdc = dv2->col; if (mindr > maxdr) r = maxdr, maxdr = mindr, mindr = r; if (mindc > maxdc) c = maxdc, maxdc = mindc, mindc = c; } else { if (showrange) { showrange = 0; mindr = showsr < currow ? showsr : currow; mindc = showsc < curcol ? showsc : curcol; maxdr = showsr > currow ? showsr : currow; maxdc = showsc > curcol ? showsc : curcol; } else if (v1) { /* Set up the default source range for the "c." command. */ minsr = maxsr = v1->row; minsc = maxsc = v1->col; return; } else { mindr = maxdr = currow; mindc = maxdc = curcol; } } if (v1) { minsr = v1->row; minsc = v1->col; maxsr = v2->row; maxsc = v2->col; if (minsr > maxsr) r = maxsr, maxsr = minsr, minsr = r; if (minsc > maxsc) c = maxsc, maxsc = minsc, minsc = c; } else if (dv1 == NULL || v2 != NULL) { if (qbuf && delbuf[qbuf]) { delbuf[++dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; } else if (dbidx < 0) return; minsr = maxrow; minsc = maxcol; maxsr = 0; maxsc = 0; for (p = delbuf[dbidx]; p; p = p->next) { if (p->row < minsr) minsr = p->row; if (p->row > maxsr) maxsr = p->row; if (p->col < minsc) minsc = p->col; if (p->col > maxsc) maxsc = p->col; } } else if (showrange && !(showsr == currow && showsc == curcol && mindr == currow && mindc == curcol && maxdr == currow && maxdc == curcol)) { minsr = showsr < currow ? showsr : currow; minsc = showsc < curcol ? showsc : curcol; maxsr = showsr > currow ? showsr : currow; maxsc = showsc > curcol ? showsc : curcol; } else if (minsr == -1) return; checkbounds(&maxdr, &maxdc); if (maxdr - mindr < maxsr - minsr) maxdr = mindr + (maxsr - minsr); if (maxdc - mindc < maxsc - minsc) maxdc = mindc + (maxsc - minsc); if (dv1 && (v1 || !v2)) yank_area(minsr, minsc, maxsr, maxsc); erase_area(mindr, mindc, maxdr, maxdc, 0); sync_refs(); flush_saved(); error("Copying..."); if (!loading) refresh(); p = delbuf[dbidx]; if (minsr == maxsr && minsc == maxsc) { /* Source is a single cell */ for (deltar = mindr - p->row; deltar <= maxdr - p->row; deltar++) for (deltac = mindc - p->col; deltac <= maxdc - p->col; deltac++) copydbuf(deltar, deltac); } else if (minsr == maxsr) { /* Source is a single row */ deltac = mindc - p->col; for (deltar = mindr - p->row; deltar <= maxdr - p->row; deltar++) copydbuf(deltar, deltac); } else if (minsc == maxsc) { /* Source is a single column */ deltar = mindr - p->row; for (deltac = mindc - p->col; deltac <= maxdc - p->col; deltac++) copydbuf(deltar, deltac); } else { /* Everything else */ deltar = mindr - p->row; deltac = mindc - p->col; copydbuf(deltar, deltac); } if (dv1 && (v1 || !v2)) { sync_refs(); flush_saved(); } if (dv1 == NULL) { if (qbuf && delbuf[qbuf]) { delbuf[dbidx] = NULL; delbuffmt[dbidx--] = NULL; } qbuf = 0; } error("Copy done."); } void copydbuf(int deltar, int deltac) { int vr, vc; struct ent *p = delbuf[dbidx]; struct ent *n; while (p) { vr = p->row + deltar; vc = p->col + deltac; n = lookat(vr, vc); if (n->flags&IS_LOCKED) continue; copyent(n, p, deltar, deltac, 0, 0, maxrow, maxcol, 0); p = p->next; } } /* ERASE a Range of cells */ void eraser(struct ent *v1, struct ent *v2) { int i; struct ent *obuf = NULL; if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 1]; delbuf[DELBUFSIZE - 1] = NULL; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 1]; delbuffmt[DELBUFSIZE - 1] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } erase_area(v1->row, v1->col, v2->row, v2->col, 0); sync_refs(); for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; for (i = DELBUFSIZE - 1; i > DELBUFSIZE - 9; i--) { delbuf[i] = delbuf[i-1]; delbuffmt[i] = delbuffmt[i-1]; } delbuf[DELBUFSIZE - 9] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 9] = delbuffmt[dbidx]; FullUpdate++; modflg++; } /* YANK a Range of cells */ void yankr(struct ent *v1, struct ent *v2) { int i, qtmp; struct ent *obuf = NULL; if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[DELBUFSIZE - 10]; delbuf[DELBUFSIZE - 10] = NULL; delbuffmt[dbidx] = delbuffmt[DELBUFSIZE - 10]; delbuffmt[DELBUFSIZE - 10] = NULL; for (i = dbidx + 1; i < DELBUFSIZE; i++) { if (delbuf[i] == delbuf[dbidx]) { delbuf[dbidx] = NULL; delbuffmt[dbidx] = NULL; break; } } flush_saved(); if (qbuf) { if (dbidx < 0) dbidx++; delbuf[dbidx] = delbuf[qbuf]; delbuffmt[dbidx] = delbuffmt[qbuf]; flush_saved(); obuf = delbuf[qbuf]; /* orig. contents of the del. buffer */ } qtmp = qbuf; qbuf = 0; yank_area(v1->row, v1->col, v2->row, v2->col); qbuf = qtmp; for (i = 0; i < DELBUFSIZE; i++) if ((obuf && delbuf[i] == obuf) || (qbuf && i == qbuf)) { delbuf[i] = delbuf[dbidx]; delbuffmt[i] = delbuffmt[dbidx]; } qbuf = 0; delbuf[DELBUFSIZE - 10] = delbuf[dbidx]; delbuffmt[DELBUFSIZE - 10] = delbuffmt[dbidx]; } /* MOVE a Range of cells */ void mover(struct ent *d, struct ent *v1, struct ent *v2) { move_area(d->row, d->col, v1->row, v1->col, v2->row, v2->col); sync_refs(); FullUpdate++; } /* Goto subroutines */ static void g_free(void) { switch (gs.g_type) { case G_STR: case G_NSTR: scxfree(gs.g_s); break; default: break; } gs.g_type = G_NONE; gs.errsearch = 0; } /* repeat the last goto command */ void go_last(void) { int num = 0; switch (gs.g_type) { case G_NONE: error("Nothing to repeat"); break; case G_NUM: num_search(gs.g_n, gs.g_row, gs.g_col, gs.g_lastrow, gs.g_lastcol, gs.errsearch); break; case G_CELL: moveto(gs.g_row, gs.g_col, gs.g_lastrow, gs.g_lastcol, gs.strow, gs.stcol); break; case G_XSTR: num++; case G_NSTR: num++; case G_STR: gs.g_type = G_NONE; /* Don't free the string */ str_search(gs.g_s, gs.g_row, gs.g_col, gs.g_lastrow, gs.g_lastcol, num); break; default: error("go_last: internal error"); } } /* Place the cursor on a given cell. If cornerrow >= 0, place the cell * at row cornerrow and column cornercol in the upper left corner of the * screen if possible. */ void moveto(int row, int col, int lastrow, int lastcol, int cornerrow, int cornercol) { register int i; if (!loading && row != -1 && (row != currow || col != curcol)) remember(0); currow = row; curcol = col; g_free(); gs.g_type = G_CELL; gs.g_row = currow; gs.g_col = curcol; gs.g_lastrow = lastrow; gs.g_lastcol = lastcol; gs.strow = cornerrow; gs.stcol = cornercol; if (cornerrow >= 0) { strow = cornerrow; stcol = cornercol; gs.stflag = 1; } else gs.stflag = 0; for (rowsinrange = 0, i = row; i <= lastrow; i++) { if (row_hidden[i]) continue; rowsinrange++; } for (colsinrange = 0, i = col; i <= lastcol; i++) { if (col_hidden[i]) continue; colsinrange += fwidth[i]; } FullUpdate++; if (loading) { update(1); changed = 0; } else remember(1); } /* * 'goto' either a given number,'error', or 'invalid' starting at currow,curcol */ void num_search(double n, int firstrow, int firstcol, int lastrow, int lastcol, int errsearch) { register struct ent *p; register int r,c; int endr, endc; if (!loading) remember(0); g_free(); gs.g_type = G_NUM; gs.g_n = n; gs.g_row = firstrow; gs.g_col = firstcol; gs.g_lastrow = lastrow; gs.g_lastcol = lastcol; gs.errsearch = errsearch; if (currow >= firstrow && currow <= lastrow && curcol >= firstcol && curcol <= lastcol) { endr = currow; endc = curcol; } else { endr = lastrow; endc = lastcol; } r = endr; c = endc; while (1) { if (c < lastcol) c++; else { if (r < lastrow) { while (++r < lastrow && row_hidden[r]) /* */; c = firstcol; } else { r = firstrow; c = firstcol; } } p = *ATBL(tbl, r, c); if (!col_hidden[c] && p && (p->flags & IS_VALID) && (errsearch || (p->v == n)) && (!errsearch || (p->cellerror == errsearch))) /* CELLERROR vs CELLINVALID */ break; if (r == endr && c == endc) { if (errsearch) { error("no %s cell found", errsearch == CELLERROR ? "ERROR" : "INVALID"); } else { error("Number not found"); } return; } } currow = r; curcol = c; rowsinrange = 1; colsinrange = fwidth[curcol]; if (loading) { update(1); changed = 0; } else remember(1); } /* 'goto' a cell containing a matching string */ void str_search(char *s, int firstrow, int firstcol, int lastrow, int lastcol, int num) { struct ent *p; int r, c; int endr, endc; char *tmp; #if defined(REGCOMP) regex_t preg; int errcode; #endif if (!loading) remember(0); #if defined(REGCOMP) if ((errcode = regcomp(&preg, s, REG_EXTENDED))) { scxfree(s); tmp = scxmalloc(160); regerror(errcode, &preg, tmp, sizeof(tmp)); error(tmp); scxfree(tmp); return; } #endif #if defined(RE_COMP) if ((tmp = re_comp(s)) != NULL) { scxfree(s); error(tmp); return; } #endif #if defined(REGCMP) if ((tmp = regcmp(s, NULL)) == NULL) { scxfree(s); cellerror = CELLERROR; error("Invalid search string"); return; } #endif g_free(); gs.g_type = G_STR + num; gs.g_s = s; gs.g_row = firstrow; gs.g_col = firstcol; gs.g_lastrow = lastrow; gs.g_lastcol = lastcol; if (currow >= firstrow && currow <= lastrow && curcol >= firstcol && curcol <= lastcol) { endr = currow; endc = curcol; } else { endr = lastrow; endc = lastcol; } r = endr; c = endc; while (1) { if (c < lastcol) { c++; } else { if (r < lastrow) { while (++r < lastrow && row_hidden[r]) /* */; c = firstcol; } else { r = firstrow; c = firstcol; } } p = *ATBL(tbl, r, c); if (gs.g_type == G_NSTR) { *line = '\0'; if (p) { if (p->cellerror) snprintf(line, sizeof line, "%s", p->cellerror == CELLERROR ? "ERROR" : "INVALID"); else if (p->flags & IS_VALID) { if (p->format) { if (*(p->format) == ctl('d')) { time_t i = (time_t) (p->v); strftime(line, sizeof(line), (p->format)+1, localtime(&i)); } else format(p->format, precision[c], p->v, line, sizeof(line)); } else engformat(realfmt[c], fwidth[c], precision[c], p->v, line, sizeof(line)); } } } else if (gs.g_type == G_XSTR) { *line = '\0'; if (p && p->expr) { linelim = 0; decompile(p->expr, 0); /* set line to expr */ line[linelim] = '\0'; if (*line == '?') *line = '\0'; } } if (!col_hidden[c]) { if (gs.g_type == G_STR) { if (p && p->label #if defined(REGCOMP) && (regexec(&preg, p->label, 0, NULL, 0) == 0)) #else #if defined(RE_COMP) && (re_exec(p->label) != 0)) #else #if defined(REGCMP) && (regex(tmp, p->label) != (char *)0)) #else && (strcmp(s, p->label) == 0)) #endif #endif #endif break; } else /* gs.g_type != G_STR */ if (*line != '\0' #if defined(REGCOMP) && (regexec(&preg, line, 0, NULL, 0) == 0)) #else #if defined(RE_COMP) && (re_exec(line) != 0)) #else #if defined(REGCMP) && (regex(tmp, line) != (char *)0)) #else && (strcmp(s, line) == 0)) #endif #endif #endif break; } if (r == endr && c == endc) { error("String not found"); #if defined(REGCOMP) regfree(&preg); #endif #if defined(REGCMP) free(tmp); #endif linelim = -1; return; } } linelim = -1; currow = r; curcol = c; rowsinrange = 1; colsinrange = fwidth[curcol]; #if defined(REGCOMP) regfree(&preg); #endif #if defined(REGCMP) free(tmp); #endif if (loading) { update(1); changed = 0; } else remember(1); } /* fill a range with constants */ void fill(struct ent *v1, struct ent *v2, double start, double inc) { int r, c; register struct ent *n; int maxr, maxc; int minr, minc; maxr = v2->row; maxc = v2->col; minr = v1->row; minc = v1->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; checkbounds(&maxr, &maxc); if (minr < 0) minr = 0; if (minc < 0) minc = 0; FullUpdate++; if (calc_order == BYROWS) { for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { n = lookat(r, c); if (n->flags&IS_LOCKED) continue; (void) clearent(n); n->v = start; start += inc; n->flags |= (IS_CHANGED|IS_VALID); n->flags &= ~(IS_CLEARED); } } else if (calc_order == BYCOLS) { for (c = minc; c<=maxc; c++) for (r = minr; r<=maxr; r++) { n = lookat(r, c); (void) clearent(n); n->v = start; start += inc; n->flags |= (IS_CHANGED|IS_VALID); n->flags &= ~(IS_CLEARED); } } else error(" Internal error calc_order"); changed++; } /* lock a range of cells */ void lock_cells(struct ent *v1, struct ent *v2) { int r, c; register struct ent *n; int maxr, maxc; int minr, minc; maxr = v2->row; maxc = v2->col; minr = v1->row; minc = v1->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; checkbounds(&maxr, &maxc); if (minr < 0) minr = 0; if (minc < 0) minc = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { n = lookat(r, c); n->flags |= IS_LOCKED; } } /* unlock a range of cells */ void unlock_cells(struct ent *v1, struct ent *v2) { int r, c; register struct ent *n; int maxr, maxc; int minr, minc; maxr = v2->row; maxc = v2->col; minr = v1->row; minc = v1->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; checkbounds(&maxr, &maxc); if (minr < 0) minr = 0; if (minc < 0) minc = 0; for (r = minr; r<=maxr; r++) for (c = minc; c<=maxc; c++) { n = lookat(r, c); n->flags &= ~IS_LOCKED; } } /* set the numeric part of a cell */ void let(struct ent *v, struct enode *e) { double val; unsigned isconstant = constant(e); if (locked_cell(v->row, v->col)) return; if (v->row == currow && v->col == curcol) cellassign = 1; if (loading && !isconstant) val = (double)0.0; else { exprerr = 0; (void) signal(SIGFPE, eval_fpe); if (setjmp(fpe_save)) { error("Floating point exception in cell %s", v_name(v->row, v->col)); val = (double)0.0; cellerror = CELLERROR; } else { cellerror = CELLOK; val = eval(e); } if (v->cellerror != cellerror) { v->flags |= IS_CHANGED; changed++; modflg++; FullUpdate++; v->cellerror = cellerror; } (void) signal(SIGFPE, doquit); if (exprerr) { efree(e); return; } } if (isconstant) { /* prescale input unless it has a decimal */ if (!loading && !decimal && (prescale < (double)0.9999999)) val *= prescale; decimal = FALSE; v->v = val; if (!(v->flags & IS_STREXPR)) { efree(v->expr); v->expr = (struct enode *)0; } efree(e); } else { efree(v->expr); v->expr = e; v->flags &= ~IS_STREXPR; } changed++; modflg++; v->flags |= (IS_CHANGED|IS_VALID); if (!loading) { int i; for (i = 36; i > 28; i--) { savedrow[i] = savedrow[i-1]; savedcol[i] = savedcol[i-1]; savedstrow[i] = savedstrow[i-1]; savedstcol[i] = savedstcol[i-1]; } savedrow[28] = v->row; savedcol[28] = v->col; savedstrow[28] = savedstrow[27]; savedstcol[28] = savedstcol[27]; } } void slet(struct ent *v, struct enode *se, int flushdir) { char *p; if (locked_cell(v->row, v->col)) return; if (v->row == currow && v->col == curcol) cellassign = 1; exprerr = 0; (void) signal(SIGFPE, eval_fpe); if (setjmp(fpe_save)) { error ("Floating point exception in cell %s", v_name(v->row, v->col)); cellerror = CELLERROR; p = ""; } else { cellerror = CELLOK; p = seval(se); } if (v->cellerror != cellerror) { v->flags |= IS_CHANGED; changed++; modflg++; FullUpdate++; v->cellerror = cellerror; } (void) signal(SIGFPE, doquit); if (exprerr) { efree(se); return; } if (!loading) { int i; for (i = 36; i > 28; i--) { savedrow[i] = savedrow[i-1]; savedcol[i] = savedcol[i-1]; savedstrow[i] = savedstrow[i-1]; savedstcol[i] = savedstcol[i-1]; } savedrow[28] = v->row; savedcol[28] = v->col; savedstrow[28] = savedstrow[27]; savedstcol[28] = savedstcol[27]; } if (constant(se)) { label(v, p, flushdir); if (p) scxfree(p); efree(se); if (v->flags & IS_STREXPR) { efree(v->expr); v->expr = (struct enode *)0; v->flags &= ~IS_STREXPR; } return; } efree(v->expr); v->expr = se; v->flags |= (IS_CHANGED|IS_STREXPR); if (flushdir < 0) v->flags |= IS_LEFTFLUSH; if (flushdir == 0) v->flags |= IS_LABEL; else v->flags &= ~IS_LABEL; FullUpdate++; changed++; modflg++; } void format_cell(struct ent *v1, struct ent *v2, char *s) { int r, c; register struct ent *n; int maxr, maxc; int minr, minc; size_t l; maxr = v2->row; maxc = v2->col; minr = v1->row; minc = v1->col; if (minr>maxr) r = maxr, maxr = minr, minr = r; if (minc>maxc) c = maxc, maxc = minc, minc = c; checkbounds(&maxr, &maxc); if (minr < 0) minr = 0; if (minc < 0) minc = 0; FullUpdate++; modflg++; for (r = minr; r <= maxr; r++) for (c = minc; c <= maxc; c++) { n = lookat(r, c); if (locked_cell(n->row, n->col)) continue; if (n->format) scxfree(n->format); n->format = 0; if (s && *s != '\0') { l = strlen(s) + 1; n->format = scxmalloc(l); strlcpy(n->format, s, l); } n->flags |= IS_CHANGED; } } void hide_row(int a) { if (a < 0) { error("Invalid Range"); return; } if (a >= maxrows-1) { if (!growtbl(GROWROW, a+1, 0)) { error("You can't hide the last row"); return; } } FullUpdate++; row_hidden[a] = TRUE; } void hide_col(int a) { if (a < 0) { error("Invalid Range"); return; } if (a >= maxcols-1) { if ((a >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, a+1)) { error("You can't hide the last col"); return; } } FullUpdate++; col_hidden[a] = TRUE; } void clearent(struct ent *v) { if (!v) return; label(v,"",-1); v->v = (double)0; if (v->expr) efree(v->expr); v->expr = NULL; if (v->format) scxfree(v->format); v->format = NULL; v->flags = (IS_CHANGED|IS_CLEARED); changed++; modflg++; } /* * Say if an expression is a constant (return 1) or not. */ int constant(register struct enode *e) { return ( e == NULL || e->op == O_CONST || e->op == O_SCONST || (e->op == 'm' && constant(e->e.o.left)) || ( e->op != O_VAR && !(e->op & REDUCE) && constant(e->e.o.left) && constant(e->e.o.right) && e->op != EXT /* functions look like constants but aren't */ && e->op != NVAL && e->op != SVAL && e->op != NOW && e->op != MYROW && e->op != MYCOL && e->op != LASTROW && e->op != LASTCOL && e->op != NUMITER && e->op != FILENAME && optimize ) ); } void efree(struct enode *e) { if (e) { if (e->op != O_VAR && e->op != O_CONST && e->op != O_SCONST && !(e->op & REDUCE)) { efree(e->e.o.left); efree(e->e.o.right); } if (e->op == O_SCONST && e->e.s) scxfree(e->e.s); else if (e->op == EXT && e->e.o.s) scxfree(e->e.o.s); e->e.o.left = freeenodes; freeenodes = e; } } void label(register struct ent *v, register char *s, int flushdir) { if (v) { if (flushdir==0 && v->flags&IS_VALID) { register struct ent *tv; if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&IS_VALID)==0) v = tv, flushdir = 1; else if (((tv=lookat(v->row,v->col+1))->flags&IS_VALID)==0) v = tv, flushdir = -1; else flushdir = -1; } if (v->label) scxfree((char *)(v->label)); if (s && s[0]) { size_t l = strlen(s) + 1; v->label = scxmalloc(l); strlcpy(v->label, s, l); } else v->label = (char *)0; if (flushdir<0) v->flags |= IS_LEFTFLUSH; else v->flags &= ~IS_LEFTFLUSH; if (flushdir==0) v->flags |= IS_LABEL; else v->flags &= ~IS_LABEL; FullUpdate++; modflg++; } } static void decodev(struct ent_ptr v) { struct range *r; if (!v.vp || v.vp->flags & IS_DELETED) snprintf(line + linelim, sizeof(line) - linelim, "@ERR"); else if (!find_range((char *)0, 0, v.vp, v.vp, &r) && !r->r_is_range) snprintf(line + linelim, sizeof(line) - linelim, "%s", r->r_name); else { snprintf(line + linelim, sizeof(line) - linelim, "%s%s%s%d", v.vf & FIX_COL ? "$" : "", coltoa(v.vp->col), v.vf & FIX_ROW ? "$" : "", v.vp->row); } linelim += strlen(line+linelim); } char * coltoa(int col) { static char rname[3]; register char *p = rname; if (col > 25) { *p++ = col/26 + 'A' - 1; col %= 26; } *p++ = col+'A'; *p = '\0'; return (rname); } /* * To make list elements come out in the same order * they were entered, we must do a depth-first eval * of the ELIST tree */ static void decompile_list(struct enode *p) { if (!p) return; decompile_list(p->e.o.left); /* depth first */ decompile(p->e.o.right, 0); line[linelim++] = ','; } void decompile(register struct enode *e, int priority) { register char *s; if (e) { int mypriority; switch (e->op) { default: mypriority = 99; break; case ';': mypriority = 1; break; case '?': mypriority = 2; break; case ':': mypriority = 3; break; case '|': mypriority = 4; break; case '&': mypriority = 5; break; case '<': case '=': case '>': mypriority = 6; break; case '+': case '-': case '#': mypriority = 8; break; case '*': case '/': case '%': mypriority = 10; break; case '^': mypriority = 12; break; } if (mypriorityop) { case 'f': for (s="@fixed "; (line[linelim++] = *s++); ) ; linelim--; decompile(e->e.o.left, 30); break; case 'F': for (s="(@fixed)"; (line[linelim++] = *s++); ) ; linelim--; decompile(e->e.o.left, 30); break; case 'm': line[linelim++] = '-'; decompile(e->e.o.left, 30); break; case '!': line[linelim++] = '!'; decompile(e->e.o.left, 30); break; case O_VAR: decodev(e->e.v); break; case O_CONST: snprintf(line + linelim, sizeof(line) - linelim, "%.15g", e->e.k); linelim += strlen(line+linelim); break; case O_SCONST: snprintf(line + linelim, sizeof(line) - linelim, "\"%s\"", e->e.s); linelim += strlen(line+linelim); break; case SUM : index_arg("@sum", e); break; case PROD : index_arg("@prod", e); break; case AVG : index_arg("@avg", e); break; case COUNT : index_arg("@count", e); break; case STDDEV : index_arg("@stddev", e); break; case MAX : index_arg("@max", e); break; case MIN : index_arg("@min", e); break; case REDUCE | 'R': range_arg("@rows(", e); break; case REDUCE | 'C': range_arg("@cols(", e); break; case ABS: one_arg("@abs(", e); break; case ACOS: one_arg("@acos(", e); break; case ASIN: one_arg("@asin(", e); break; case ATAN: one_arg("@atan(", e); break; case ATAN2: two_arg("@atan2(", e); break; case CEIL: one_arg("@ceil(", e); break; case COS: one_arg("@cos(", e); break; case EXP: one_arg("@exp(", e); break; case FABS: one_arg("@fabs(", e); break; case FLOOR: one_arg("@floor(", e); break; case HYPOT: two_arg("@hypot(", e); break; case LOG: one_arg("@ln(", e); break; case LOG10: one_arg("@log(", e); break; case POW: two_arg("@pow(", e); break; case SIN: one_arg("@sin(", e); break; case SQRT: one_arg("@sqrt(", e); break; case TAN: one_arg("@tan(", e); break; case DTR: one_arg("@dtr(", e); break; case RTD: one_arg("@rtd(", e); break; case RND: one_arg("@rnd(", e); break; case ROUND: two_arg("@round(", e); break; case HOUR: one_arg("@hour(", e); break; case MINUTE: one_arg("@minute(", e); break; case SECOND: one_arg("@second(", e); break; case MONTH: one_arg("@month(", e); break; case DAY: one_arg("@day(", e); break; case YEAR: one_arg("@year(", e); break; case NOW: for (s = "@now"; (line[linelim++] = *s++); ) ; linelim--; break; case DATE: if (e->e.o.right) two_arg("@date(", e); else one_arg("@date(", e); break; case FMT: two_arg("@fmt(", e); break; case UPPER: one_arg("@upper(", e); break; case LOWER: one_arg("@lower(", e); break; case CAPITAL: one_arg("@capital(", e); break; case DTS: three_arg("@dts(", e); break; case TTS: three_arg("@tts(", e); break; case STON: one_arg("@ston(", e); break; case EQS: two_arg("@eqs(", e); break; case LMAX: list_arg("@max(", e); break; case LMIN: list_arg("@min(", e); break; case FV: three_arg("@fv(", e); break; case PV: three_arg("@pv(", e); break; case PMT: three_arg("@pmt(", e); break; case NVAL: two_arg("@nval(", e); break; case SVAL: two_arg("@sval(", e); break; case EXT: two_arg("@ext(", e); break; case SUBSTR: three_arg("@substr(", e); break; case STINDEX: index_arg("@stindex", e); break; case INDEX: index_arg("@index", e); break; case LOOKUP: index_arg("@lookup", e); break; case HLOOKUP: two_arg_index("@hlookup", e); break; case VLOOKUP: two_arg_index("@vlookup", e); break; case IF: three_arg("@if(", e); break; case MYROW: for (s = "@myrow"; (line[linelim++] = *s++); ) ; linelim--; break; case MYCOL: for (s = "@mycol"; (line[linelim++] = *s++); ) ; linelim--; break; case LASTROW: for (s = "@lastrow"; (line[linelim++] = *s++); ) ; linelim--; break; case LASTCOL: for (s = "@lastcol"; (line[linelim++] = *s++); ) ; linelim--; break; case COLTOA: one_arg( "@coltoa(", e); break; case FILENAME: one_arg( "@filename(", e); break; case NUMITER: for (s = "@numiter"; (line[linelim++] = *s++); ) ; linelim--; break; case ERR_: for (s = "@err"; (line[linelim++] = *s++); ) ; linelim--; break; case PI_: for (s = "@pi"; (line[linelim++] = *s++); ) ; linelim--; break; case BLACK: for (s = "@black"; (line[linelim++] = *s++); ) ; linelim--; break; case RED: for (s = "@red"; (line[linelim++] = *s++); ) ; linelim--; break; case GREEN: for (s = "@green"; (line[linelim++] = *s++); ) ; linelim--; break; case YELLOW: for (s = "@yellow"; (line[linelim++] = *s++); ) ; linelim--; break; case BLUE: for (s = "@blue"; (line[linelim++] = *s++); ) ; linelim--; break; case MAGENTA: for (s = "@magenta"; (line[linelim++] = *s++); ) ; linelim--; break; case CYAN: for (s = "@cyan"; (line[linelim++] = *s++); ) ; linelim--; break; case WHITE: for (s = "@white"; (line[linelim++] = *s++); ) ; linelim--; break; default: decompile(e->e.o.left, mypriority); line[linelim++] = e->op; decompile(e->e.o.right, mypriority+1); break; } if (myprioritye.o.right && e->e.o.right->op == ',') { two_arg_index(s, e); return; } for (; (line[linelim++] = *s++); ); linelim--; range_arg("(", e->e.o.left); linelim--; if (e->e.o.right) { line[linelim++] = ','; decompile(e->e.o.right, 0); } line[linelim++] = ')'; } void two_arg_index(char *s, struct enode *e) { for (; (line[linelim++] = *s++); ); linelim--; range_arg("(", e->e.o.left); linelim--; line[linelim++] = ','; decompile(e->e.o.right->e.o.left, 0); line[linelim++] = ','; decompile(e->e.o.right->e.o.right, 0); line[linelim++] = ')'; } void list_arg(char *s, struct enode *e) { for (; (line[linelim++] = *s++); ); linelim--; decompile(e->e.o.right, 0); line[linelim++] = ','; decompile_list(e->e.o.left); line[linelim - 1] = ')'; } void one_arg(char *s, struct enode *e) { for (; (line[linelim++] = *s++); ); linelim--; decompile(e->e.o.left, 0); line[linelim++] = ')'; } void two_arg(char *s, struct enode *e) { for (; (line[linelim++] = *s++); ); linelim--; decompile(e->e.o.left, 0); line[linelim++] = ','; decompile (e->e.o.right, 0); line[linelim++] = ')'; } void three_arg(char *s, struct enode *e) { for (; (line[linelim++] = *s++); ); linelim--; decompile(e->e.o.left, 0); line[linelim++] = ','; decompile(e->e.o.right->e.o.left, 0); line[linelim++] = ','; decompile (e->e.o.right->e.o.right, 0); line[linelim++] = ')'; } void range_arg(char *s, struct enode *e) { struct range *r; for (; (line[linelim++] = *s++); ); linelim--; if (!find_range((char *)0, 0, e->e.r.left.vp, e->e.r.right.vp, &r) && r->r_is_range) { snprintf(line + linelim, sizeof(line) - linelim, "%s", r->r_name); linelim += strlen(line+linelim); } else { decodev(e->e.r.left); line[linelim++] = ':'; decodev(e->e.r.right); } line[linelim++] = ')'; } void editfmt(int row, int col) { register struct ent *p; p = lookat(row, col); if (p->format) { snprintf(line, sizeof line, "fmt %s \"%s\"", v_name(row, col), p->format); linelim = strlen(line); } } void editv(int row, int col) { register struct ent *p; p = lookat(row, col); snprintf(line, sizeof line, "let %s = ", v_name(row, col)); linelim = strlen(line); if (p->flags & IS_VALID) { if (p->flags & IS_STREXPR || p->expr == NULL) { snprintf(line + linelim, sizeof(line) - linelim, "%.15g", p->v); linelim = strlen(line); } else editexp(row, col); } } void editexp(int row, int col) { register struct ent *p; p = lookat(row, col); decompile(p->expr, 0); line[linelim] = '\0'; } void edits(int row, int col) { register struct ent *p; p = lookat(row, col); if (p->flags & IS_LABEL) snprintf(line, sizeof line, "label %s = ", v_name(row, col)); else snprintf(line, sizeof line, "%sstring %s = ", ((p->flags & IS_LEFTFLUSH) ? "left" : "right"), v_name(row, col)); linelim = strlen(line); if (p->flags & IS_STREXPR && p->expr) { editexp(row, col); } else if (p->label) { snprintf(line + linelim, sizeof(line) - linelim, "\"%s\"", p->label); linelim += strlen(line+linelim); } else { snprintf(line + linelim, sizeof(line) - linelim, "\""); linelim += 1; } } #ifdef RINT /* round-to-even, also known as ``banker's rounding''. With round-to-even, a number exactly halfway between two values is rounded to whichever is even; e.g. rnd(0.5)=0, rnd(1.5)=2, rnd(2.5)=2, rnd(3.5)=4. This is the default rounding mode for IEEE floating point, for good reason: it has better numeric properties. For example, if X+Y is an integer, then X+Y = rnd(X)+rnd(Y) with round-to-even, but not always with sc's rounding (which is round-to-positive-infinity). I ran into this problem when trying to split interest in an account to two people fairly. */ double rint(double d) { /* as sent */ double fl = floor(d), fr = d-fl; return fr<0.5 || fr==0.5 && fl==floor(fl/2)*2 ? fl : ceil(d); } #endif sc-7.16_1.1.2/lex.c000066400000000000000000000361241326046243500135420ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Lexical analyser * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * More mods by Alan Silverstein, 3/88, see list of changes. * $Revision: 7.16 $ * */ #include #include #include #include #if defined(BSD42) || defined(BSD43) #include #endif #ifdef USE_IEEEFP_H # include #endif #include #include #include #include #include #include #include "compat.h" #include "sc.h" static void fpe_trap(int); #ifdef VMS # include "gram_tab.h" typedef union { int ival; double fval; struct ent *ent; struct enode *enode; char *sval; struct range_s rval; } YYSTYPE; extern YYSTYPE yylval; extern int VMS_read_raw; /*sigh*/ #else /* VMS */ # if defined(MSDOS) # include "y_tab.h" # else # include "y.tab.h" # endif /* MSDOS */ #endif /* VMS */ #ifdef hpux extern YYSTYPE yylval; #endif /* hpux */ jmp_buf wakeup; jmp_buf fpe_buf; bool decimal = FALSE; static void fpe_trap(int signo) { (void)signo; #if defined(i386) && !defined(M_XENIX) asm(" fnclex"); asm(" fwait"); #else # ifdef IEEE_MATH (void)fpsetsticky((fp_except)0); /* Clear exception */ # endif /* IEEE_MATH */ # ifdef PC _fpreset(); # endif #endif longjmp(fpe_buf, 1); } struct key { char *key; int val; }; struct key experres[] = { #include "experres.h" { 0, 0 } }; struct key statres[] = { #include "statres.h" { 0, 0 } }; int yylex(void) { char *p = line + linelim; int ret = -1; static int isfunc = 0; static bool isgoto = 0; static bool colstate = 0; static int dateflag; static char *tokenst = NULL; static size_t tokenl; while (isspace((int)*p)) p++; if (*p == '\0') { isfunc = isgoto = 0; ret = -1; } else if (isalpha((int)*p) || (*p == '_')) { register char *la; /* lookahead pointer */ register struct key *tblp; if (!tokenst) { tokenst = p; tokenl = 0; } /* * This picks up either 1 or 2 alpha characters (a column) or * tokens made up of alphanumeric chars and '_' (a function or * token or command or a range name) */ while (isalpha((int)*p) && isascii((int)*p)) { p++; tokenl++; } la = p; while (isdigit((int)*la) || (*la == '$')) la++; /* * A COL is 1 or 2 char alpha with nothing but digits following * (no alpha or '_') */ if (!isdigit((int)*tokenst) && tokenl && tokenl <= 2 && (colstate || (isdigit((int)*(la-1)) && !(isalpha((int)*la) || (*la == '_'))))) { ret = COL; yylval.ival = atocol(tokenst, tokenl); } else { while (isalpha((int)*p) || (*p == '_') || isdigit((int)*p)) { p++; tokenl++; } ret = WORD; if (!linelim || isfunc) { if (isfunc) isfunc--; for (tblp = linelim ? experres : statres; tblp->key; tblp++) if (((tblp->key[0] ^ tokenst[0]) & 0x5F) == 0) { /* Commenting the following line makes the search slower */ /* but avoids access outside valid memory. A BST would */ /* be the better alternative. */ /* && tblp->key[tokenl] == 0) { */ unsigned int i = 1; while (i < tokenl && ((tokenst[i] ^ tblp->key[i]) & 0x5F) == 0) i++; if (i >= tokenl) { ret = tblp->val; colstate = (ret <= S_FORMAT); if (isgoto) { isfunc = isgoto = 0; if (ret != K_ERROR && ret != K_INVALID) ret = WORD; } break; } } } if (ret == WORD) { struct range *r; char *path; if (!find_range(tokenst, tokenl, NULL, NULL, &r)) { yylval.rval.left = r->r_left; yylval.rval.right = r->r_right; if (r->r_is_range) ret = RANGE; else ret = VAR; } else if ((path = scxmalloc(PATHLEN)) && plugin_exists(tokenst, tokenl, path)) { strlcat(path, p, PATHLEN); yylval.sval = path; ret = PLUGIN; } else { scxfree(path); linelim = p-line; yyerror("Unintelligible word"); } } } } else if ((*p == '.') || isdigit((int)*p)) { #ifdef SIGVOID void (*sig_save)(); #else int (*sig_save)(); #endif double v = 0.0; int temp; char *nstart = p; sig_save = signal(SIGFPE, fpe_trap); if (setjmp(fpe_buf)) { (void) signal(SIGFPE, sig_save); yylval.fval = v; error("Floating point exception\n"); isfunc = isgoto = 0; tokenst = NULL; return FNUMBER; } if (*p=='.' && dateflag) { /* .'s in dates are returned as tokens. */ ret = *p++; dateflag--; } else { if (*p != '.') { tokenst = p; tokenl = 0; do { v = v*10.0 + (double) ((unsigned) *p - '0'); tokenl++; } while (isdigit((int)*++p)); if (dateflag) { ret = NUMBER; yylval.ival = (int)v; /* * If a string of digits is followed by two .'s separated by * one or two digits, assume this is a date and return the * .'s as tokens instead of interpreting them as decimal * points. dateflag counts the .'s as they're returned. */ } else if (*p=='.' && isdigit((int)*(p+1)) && (*(p+2)=='.' || (isdigit((int)*(p+2)) && *(p+3)=='.'))) { ret = NUMBER; yylval.ival = (int)v; dateflag = 2; } else if (*p == 'e' || *p == 'E') { while (isdigit((int)*++p)) /* */; if (isalpha((int)*p) || *p == '_') { linelim = p - line; return (yylex()); } else ret = FNUMBER; } else if (isalpha((int)*p) || *p == '_') { linelim = p - line; return (yylex()); } } if ((!dateflag && *p=='.') || ret == FNUMBER) { ret = FNUMBER; yylval.fval = strtod(nstart, &p); if (! #ifdef HAVE_ISFINITE isfinite( #else finite( #endif yylval.fval)) ret = K_ERR; else decimal = TRUE; } else { /* A NUMBER must hold at least MAXROW and MAXCOL */ /* This is consistent with a short row and col in struct ent */ if (v > (double)32767 || v < (double)-32768) { ret = FNUMBER; yylval.fval = v; } else { temp = (int)v; if((double)temp != v) { ret = FNUMBER; yylval.fval = v; } else { ret = NUMBER; yylval.ival = temp; } } } } (void) signal(SIGFPE, sig_save); } else if (*p=='"') { char *ptr; ptr = p+1; /* "string" or "string\"quoted\"" */ while (*ptr && ((*ptr != '"') || (*(ptr-1) == '\\'))) ptr++; ptr = scxmalloc(ptr-p); yylval.sval = ptr; p++; while (*p && ((*p != '"') || (*(p-1) == '\\' && *(p+1) != '\0' && *(p+1) != '\n'))) *ptr++ = *p++; *ptr = '\0'; if (*p) p++; ret = STRING; } else if (*p=='[') { while (*p && *p!=']') p++; if (*p) p++; linelim = p-line; tokenst = NULL; return yylex(); } else ret = *p++; linelim = p-line; if (!isfunc) isfunc = ((ret == '@') + (ret == S_GOTO) - (ret == S_SET)); if (ret == S_GOTO) isgoto = TRUE; tokenst = NULL; return ret; } /* * This is a very simpleminded test for plugins: does the file merely exist * in the plugin directories. Perhaps should test for it being executable */ int plugin_exists(char *name, size_t len, char *path) { #ifndef MSDOS struct stat sb; static char *homedir; if ((homedir = getenv("HOME"))) { if (strlcpy(path, homedir, len) >= len || strlcat(path, "/.sc/plugins/", len) >= len || strlcat(path, name, len) >= len) return 0; if (!stat(path, &sb)) return 1; } if (strlcpy(path, LIBDIR, len) >= len || strlcat(path, "/plugins/", len) >= len || strlcat(path, name, len) >= len) return 0; if (!stat(path, &sb)) return 1; #endif return 0; } /* * Given a token string starting with a symbolic column name and its valid * length, convert column name ("A"-"Z" or "AA"-"ZZ") to a column number (0-N). * Never mind if the column number is illegal (too high). The procedure's name * and function are the inverse of coltoa(). * * Case-insensitivity is done crudely, by ignoring the 040 bit. */ int atocol(char *string, int len) { register int col; col = (toupper((int)string[0])) - 'A'; if (len == 2) /* has second char */ col = ((col + 1) * 26) + ((toupper((int)string[1])) - 'A'); return (col); } #ifdef SIMPLE void initkbd(void) {} void kbd_again(void) {} void resetkbd(void) {} # ifndef VMS int nmgetch(void) { return (getchar()); } # else /* VMS */ int nmgetch(void) /* This is not perfect, it doesn't move the cursor when goraw changes over to deraw, but it works well enough since the whole sc package is incredibly stable (loop constantly positions cursor). Question, why didn't the VMS people just implement cbreak? NOTE: During testing it was discovered that the DEBUGGER and curses and this method of reading would collide (the screen was not updated when continuing from screen mode in the debugger). */ { short c; static int key_id=0; int status; # define VMScheck(a) {if (~(status = (a)) & 1) VMS_MSG (status);} if (VMS_read_raw) { VMScheck(smg$read_keystroke (&stdkb->_id, &c, 0, 0, 0)); } else c = getchar(); switch (c) { case SMG$K_TRM_LEFT: c = KEY_LEFT; break; case SMG$K_TRM_RIGHT: c = KEY_RIGHT; break; case SMG$K_TRM_UP: c = ctl('p'); break; case SMG$K_TRM_DOWN: c = ctl('n'); break; default: c = c & A_CHARTEXT; } return (c); } VMS_MSG (int status) /* Routine to put out the VMS operating system error (if one occurs). */ { # include char errstr[81], buf[120]; $DESCRIPTOR(errdesc, errstr); short length; # define err_out(msg) fprintf (stderr,msg) /* Check for no error or standard error */ if (~status & 1) { status = status & 0x8000 ? status & 0xFFFFFFF : status & 0xFFFF; if (SYS$GETMSG(status, &length, &errdesc, 1, 0) == SS$_NORMAL) { errstr[length] = '\0'; snprintf(buf, sizeof buf, "<0x%x> %s", status, errdesc.dsc$a_pointer); err_out(buf); } else err_out("System error"); } } # endif /* VMS */ #else /*SIMPLE*/ # if defined(BSD42) || defined (SYSIII) || defined(BSD43) # define N_KEY 4 struct key_map { char *k_str; int k_val; char k_index; }; struct key_map km[N_KEY]; char keyarea[N_KEY*30]; char *ks; char ks_buf[20]; char *ke; char ke_buf[20]; # ifdef TIOCSLTC struct ltchars old_chars, new_chars; # endif char dont_use[] = { ctl('['), ctl('a'), ctl('b'), ctl('c'), ctl('e'), ctl('f'), ctl('g'), ctl('h'), ctl('i'), ctl('j'), ctl('l'), ctl('m'), ctl('n'), ctl('p'), ctl('q'), ctl('r'), ctl('s'), ctl('t'), ctl('u'), ctl('v'), ctl('w'), ctl('x'), ctl('z'), 0 }; int charout(int c) { return putchar(c); } void initkbd(void) { register struct key_map *kp; register i,j; char *p = keyarea; char *ktmp; static char buf[1024]; /* Why do I have to do this again? */ if (!(ktmp = getenv("TERM"))) { (void) fprintf(stderr, "TERM environment variable not set\n"); exit (1); } if (tgetent(buf, ktmp) <= 0) return; km[0].k_str = tgetstr("kl", &p); km[0].k_val = KEY_LEFT; km[1].k_str = tgetstr("kr", &p); km[1].k_val = KEY_RIGHT; km[2].k_str = tgetstr("ku", &p); km[2].k_val = ctl('p'); km[3].k_str = tgetstr("kd", &p); km[3].k_val = ctl('n'); ktmp = tgetstr("ks",&p); if (ktmp) { strlcpy(ks_buf, ktmp, sizeof ks_buf); ks = ks_buf; tputs(ks, 1, charout); } ktmp = tgetstr("ke",&p); if (ktmp) { strlcpy(ke_buf, ktmp, sizeof ke_buf); ke = ke_buf; } /* Unmap arrow keys which conflict with our ctl keys */ /* Ignore unset, longer than length 1, and 1-1 mapped keys */ for (i = 0; i < N_KEY; i++) { kp = &km[i]; if (kp->k_str && (kp->k_str[1] == 0) && (kp->k_str[0] != kp->k_val)) for (j = 0; dont_use[j] != 0; j++) if (kp->k_str[0] == dont_use[j]) { kp->k_str = (char *)0; break; } } # ifdef TIOCSLTC (void)ioctl(fileno(stdin), TIOCGLTC, (char *)&old_chars); new_chars = old_chars; if (old_chars.t_lnextc == ctl('v')) new_chars.t_lnextc = -1; if (old_chars.t_rprntc == ctl('r')) new_chars.t_rprntc = -1; (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&new_chars); # endif } void kbd_again(void) { if (ks) tputs(ks, 1, charout); # ifdef TIOCSLTC (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&new_chars); # endif } void resetkbd(void) { if (ke) tputs(ke, 1, charout); # ifdef TIOCSLTC (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&old_chars); # endif } int nmgetch(void) { register int c; register struct key_map *kp; register struct key_map *biggest; register int i; int almost; int maybe; static char dumpbuf[10]; static char *dumpindex; # ifdef SIGVOID void time_out(int); # else int time_out(int); # endif if (dumpindex && *dumpindex) return (*dumpindex++); c = getchar(); biggest = 0; almost = 0; for (kp = &km[0]; kp < &km[N_KEY]; kp++) { if (!kp->k_str) continue; if (c == kp->k_str[kp->k_index]) { almost = 1; kp->k_index++; if (kp->k_str[kp->k_index] == 0) { c = kp->k_val; for (kp = &km[0]; kp < &km[N_KEY]; kp++) kp->k_index = 0; return (c); } } if (!biggest && kp->k_index) biggest = kp; else if (kp->k_index && biggest->k_index < kp->k_index) biggest = kp; } if (almost) { (void) signal(SIGALRM, time_out); (void) alarm(1); if (setjmp(wakeup) == 0) { maybe = nmgetch(); (void) alarm(0); return (maybe); } } if (biggest) { for (i = 0; ik_index; i++) dumpbuf[i] = biggest->k_str[i]; if (!almost) dumpbuf[i++] = c; dumpbuf[i] = '\0'; dumpindex = &dumpbuf[1]; for (kp = &km[0]; kp < &km[N_KEY]; kp++) kp->k_index = 0; return (dumpbuf[0]); } return(c); } # endif /* if defined(BSD42) || defined (SYSIII) || defined(BSD43) */ void initkbd(void) { keypad(stdscr, TRUE); #ifndef NONOTIMEOUT notimeout(stdscr,TRUE); #endif } void kbd_again(void) { keypad(stdscr, TRUE); #ifndef NONOTIMEOUT notimeout(stdscr,TRUE); #endif } void resetkbd(void) { keypad(stdscr, FALSE); #ifndef NONOTIMEOUT notimeout(stdscr, FALSE); #endif } int nmgetch(void) { register int c; c = getch(); switch (c) { # ifdef KEY_SELECT case KEY_SELECT: c = 'm'; break; # endif # ifdef KEY_C1 /* This stuff works for a wyse wy75 in ANSI mode under 5.3. Good luck. */ /* It is supposed to map the curses keypad back to the numeric equiv. */ /* I had to disable this to make programmable function keys work. I'm * not familiar with the wyse wy75 terminal. Does anyone know how to * make this work without causing problems with programmable function * keys on everything else? - CRM case KEY_C1: c = '0'; break; case KEY_A1: c = '1'; break; case KEY_B2: c = '2'; break; case KEY_A3: c = '3'; break; case KEY_F(5): c = '4'; break; case KEY_F(6): c = '5'; break; case KEY_F(7): c = '6'; break; case KEY_F(9): c = '7'; break; case KEY_F(10): c = '8'; break; case KEY_F0: c = '9'; break; case KEY_C3: c = '.'; break; case KEY_ENTER: c = ctl('m'); break; * * */ # endif default: break; } return (c); } #endif /* SIMPLE */ #ifdef SIGVOID void #else int #endif time_out(int signo) { (void)signo; longjmp(wakeup, 1); } sc-7.16_1.1.2/pipe.c000066400000000000000000000170311326046243500137030ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Routines for piping data to and from an external macro program * * Chuck Martin * Original Version Created: June, 2000 * * $Revision: 7.16 $ */ #include #include #include #include #include "compat.h" #include "sc.h" void getnum(int r0, int c0, int rn, int cn, int fd) { struct ent **pp; struct ent *p; int r, c; for (r = r0; r <= rn; r++) { for (c = c0, pp = ATBL(tbl, r, c); c <= cn; pp++, c++) { *line = '\0'; p = *pp; if (p) { if (p->cellerror) snprintf(line, sizeof line, "%s", (*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"); else if (p->flags & IS_VALID) snprintf(line, sizeof line, "%.15g", p->v); } if (c < cn) strlcat(line, "\t", sizeof line); else strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); if (brokenpipe) { linelim = -1; return; } } } linelim = -1; } void fgetnum(int r0, int c0, int rn, int cn, int fd) { struct ent **pp; struct ent *p; int r, c; for (r = r0; r <= rn; r++) { for (c = c0, pp = ATBL(tbl, r, c); c <= cn; pp++, c++) { *line = '\0'; p = *pp; if (p) { if (p->cellerror) snprintf(line, sizeof line, "%s", p->cellerror == CELLERROR ? "ERROR" : "INVALID"); else if (p->flags & IS_VALID) { if (p->format) { if (*(p->format) == ctl('d')) { time_t i = (time_t) (p->v); strftime(line, sizeof(line), (p->format)+1, localtime(&i)); } else format(p->format, precision[c], p->v, line, sizeof(line)); } else engformat(realfmt[c], fwidth[c], precision[c], p->v, line, sizeof(line)); } } if (c < cn) strlcat(line, "\t", sizeof line); else strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); if (brokenpipe) { linelim = -1; return; } } } linelim = -1; } void getstring(int r0, int c0, int rn, int cn, int fd) { struct ent **pp; int r, c; for (r = r0; r <= rn; r++) { for (c = c0, pp = ATBL(tbl, r, c); c <= cn; pp++, c++) { *line = '\0'; if (*pp && (*pp)->label) snprintf(line, sizeof line, "%s", (*pp)->label); if (c < cn) strlcat(line, "\t", sizeof line); else strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); if (brokenpipe) { linelim = -1; return; } } } linelim = -1; } void getexp(int r0, int c0, int rn, int cn, int fd) { struct ent **pp; struct ent *p; int r, c; for (r = r0; r <= rn; r++) { for (c = c0, pp = ATBL(tbl, r, c); c <= cn; pp++, c++) { *line = '\0'; p = *pp; if (p && p->expr) { linelim = 0; decompile(p->expr, 0); /* set line to expr */ line[linelim] = '\0'; if (*line == '?') *line = '\0'; } if (c < cn) strlcat(line, "\t", sizeof line); else strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); if (brokenpipe) { linelim = -1; return; } } } linelim = -1; } void getformat(int col, int fd) { snprintf(line, sizeof line, "%d %d %d\n", fwidth[col], precision[col], realfmt[col]); write(fd, line, strlen(line)); linelim = -1; } void getfmt(int r0, int c0, int rn, int cn, int fd) { struct ent **pp; int r, c; for (r = r0; r <= rn; r++) { for (c = c0, pp = ATBL(tbl, r, c); c <= cn; pp++, c++) { *line = '\0'; if (*pp && (*pp)->format) snprintf(line, sizeof line, "%s", (*pp)->format); if (c < cn) strlcat(line, "\t", sizeof line); else strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); if (brokenpipe) { linelim = -1; return; } } } linelim = -1; } void getframe(int fd) { struct frange *fr; size_t l; *line = '\0'; if ((fr = find_frange(currow, curcol))) { snprintf(line, sizeof line, "%s", r_name(fr->or_left->row, fr->or_left->col, fr->or_right->row, fr->or_right->col)); strlcat(line, " ", sizeof line); l = strlen(line); snprintf(line + l, sizeof(line) - l, "%s", r_name(fr->ir_left->row, fr->ir_left->col, fr->ir_right->row, fr->ir_right->col)); } strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); linelim = -1; } void getrange(char *name, int fd) { struct range *r; char *p; *line = '\0'; if (!find_range(name, strlen(name), (struct ent *)0, (struct ent *)0, &r)) { snprintf(line, sizeof line, "%s%s%s%d", r->r_left.vf & FIX_COL ? "$" : "", coltoa(r->r_left.vp->col), r->r_left.vf & FIX_ROW ? "$" : "", r->r_left.vp->row); if (r->r_is_range) { p = line; while (*p) p++; snprintf(p, sizeof(line) - (p - line), ":%s%s%s%d", r->r_right.vf & FIX_COL ? "$" : "", coltoa(r->r_right.vp->col), r->r_right.vf & FIX_ROW ? "$" : "", r->r_right.vp->row); } /************************************************/ /* */ /* if(r->r_is_range) */ /* sprintf(line,"%d:%d:%d:%d", */ /* r->r_left.vp->col, */ /* r->r_left.vp->row, */ /* r->r_right.vp->col, */ /* r->r_right.vp->row); */ /* else */ /* sprintf(line,"%d:%d", */ /* r->r_left.vp->col, */ /* r->r_left.vp->row); */ /* */ /************************************************/ } strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); linelim = -1; } void doeval(struct enode *e, char *fmt, int row, int col, int fd) { double v; gmyrow = row; gmycol = col; v = eval(e); if (fmt) { if (*fmt == ctl('d')) { time_t tv = v; strftime(line, FBUFLEN, fmt + 1, localtime(&tv)); } else format(fmt, precision[col], v, line, FBUFLEN); } else snprintf(line, sizeof line, "%.15g", v); strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); linelim = -1; efree(e); if (fmt) scxfree(fmt); } void doseval(struct enode *e, int row, int col, int fd) { char *s; gmyrow = row; gmycol = col; s = seval(e); if (s) write(fd, s, strlen(s)); write(fd, "\n", 1); linelim = -1; efree(e); if (s) scxfree(s); } void doquery(char *s, char *data, int fd) { goraw(); query(s, data); deraw(0); if (linelim >= 0) { write(fd, line, strlen(line)); write(fd, "\n", 1); } line[0] = '\0'; linelim = -1; CLEAR_LINE; update(0); if (s) scxfree(s); } void dogetkey(void) { int c, len; goraw(); c = nmgetch(); deraw(0); if (c < 256) { snprintf(line, sizeof line, "%c", c); len = 1; #ifdef HAVE_CURSES_KEYNAME } else if (c >= KEY_MIN && c <= KEY_MAX) { int i, j; line[0] = '\0'; snprintf(line + 1, sizeof(line) - 1, "%s\n", keyname(c)); for (i = 1, j = 5; line[j-1]; ) { if (line[j] == '(' || line[j] == ')') j++; else line[i++] = line[j++]; } len = strlen(line + 1) + 1; #endif } else { line[0] = '0'; snprintf(line + 1, sizeof(line) - 1, "UNKNOWN KEY"); len = strlen(line + 1) + 1; } write(macrofd, line, len); } void dostat(int fd) { *line = '\0'; if (modflg) snprintf(line, sizeof line, "m"); if (isatty(STDIN_FILENO)) strlcat(line, "i", sizeof line); if (isatty(STDOUT_FILENO)) strlcat(line, "o", sizeof line); strlcat(line, "\n", sizeof line); write(fd, line, strlen(line)); linelim = -1; } sc-7.16_1.1.2/psc.c000066400000000000000000000164471326046243500135450ustar00rootroot00000000000000/* Sc parse routine * * usage psc options * options: * -L Left justify strings. Default is right justify. * -r Assemble data into rows first, not columns. * -R n Increment by n between rows * -C n Increment by n between columns * -n n Length of the row (column) should be n. * -s v Top left location in the spreadsheet should be v; eg, k5 * -d c Use c as the delimiter between the fields. * -k Keep all delimiters - Default is strip multiple delimiters to 1. * -f suppress 'format' lines in output * -S Use strings vs numbers for numbers * -P Use numbers only when there is no [-+eE] (plain numbers only) * * Author: Robert Bond * Adjustments: Jeff Buhrt, Eric Putz and Chuck Martin */ #include "version.c" #include #include #include #include #ifdef HAVE_STDBOOL_H # include #else # if !HAVE__BOOL # define _Bool int # endif # define bool _Bool # define true 1 # define false 0 #endif #include #include #include "sc.h" #define END 0 #define NUM 1 #define ALPHA 2 #define SPACE 3 #define EOL 4 char *coltoa(int col); char *progname; int getrow(char *p); int getcol(char *p); static int scan(void); int *fwidth; int *precision; int maxcols; int *realfmt; static int curlen; int curcol; static int coff; int currow; static int roff; static int first; static int effr, effc; /* option flags reset */ static int colfirst = FALSE; static int leftadj = FALSE; static int r0 = 0; static int c0 = 0; static int rinc = 1; static int cinc = 1; static int len = 20000; static char delim1 = ' '; static char delim2 = '\t'; static int strip_delim = TRUE; static int drop_format = FALSE; static int strnums = FALSE; static int plainnums = FALSE; static char token[1000]; int main(int argc, char **argv) { int c; int i, j; register char *p; progname = argv[0]; while ((c = getopt(argc, argv, "rfLks:R:C:n:d:SPv")) != EOF) { switch (c) { case 'r': colfirst = TRUE; break; case 'L': leftadj = TRUE; break; case 's': c0 = getcol(optarg); r0 = getrow(optarg); break; case 'R': rinc = atoi(optarg); break; case 'C': cinc = atoi(optarg); break; case 'n': len = atoi(optarg); break; case 'd': delim1 = optarg[0]; delim2 = '\0'; break; case 'k': strip_delim = FALSE; break; case 'f': drop_format = TRUE; break; case 'S': strnums = TRUE; break; case 'P': plainnums = TRUE; break; case 'v': fprintf(stderr,"%s: %s\n", progname, rev); return 0; default: exit(1); } } if (optind < argc) { fprintf(stderr, "%s: %d more argument(s) than expected\n", progname, argc - optind); exit(1); } /* setup the spreadsheet arrays */ if (!growtbl(GROWNEW, 0, 0)) exit(1); curlen = 0; curcol = c0; coff = 0; currow = r0; roff = 0; first = TRUE; while (1) { effr = currow+roff; effc = curcol+coff; switch (scan()) { case END: if (drop_format) exit(0); for (i = 0; i= maxcols - 1) { if (!growtbl(GROWCOL, 0, effc)) { (void) fprintf(stderr, "Invalid column used: %s\n", coltoa(effc)); continue; } } i = 0; j = 0; p = token; while (*p && *p != '.') { p++; i++; } if (*p) { p++; i++; } while (*p) { p++; i++; j++; } { int ow, nw; ow = fwidth[effc] - precision[effc]; if (precision[effc] < j) precision[effc] = j; if (fwidth[effc] < i) fwidth[effc] = i; /* now make sure: * 1234.567890 (format 11 6) * 1234567.890 (format 11 3) * both show (format 14 6) * (really it uses 15 6 to separate columns) */ if ((nw = i - j) > ow) fwidth[effc] += nw - (fwidth[effc] - precision[effc]); } break; case ALPHA: first = FALSE; if (leftadj) (void) printf("leftstring %s%d = \"%s\"\n", coltoa(effc),effr,token); else (void) printf("rightstring %s%d = \"%s\"\n",coltoa(effc),effr,token); if (effc >= maxcols - 1) { if (!growtbl(GROWCOL, 0, effc)) { (void) fprintf(stderr, "Invalid column used: %s\n", coltoa(effc)); continue; } } i = strlen(token); if (i > fwidth[effc]) fwidth[effc] = i; break; case SPACE: if (first && strip_delim) break; if (colfirst) roff++; else coff++; break; case EOL: curlen++; roff = 0; coff = 0; first = TRUE; if (colfirst) { if (curlen >= len) { curcol = c0; currow += rinc; curlen = 0; } else { curcol += cinc; } } else { if (curlen >= len) { currow = r0; curcol += cinc; curlen = 0; } else { currow += rinc; } } break; } } } static int scan(void) { register int c; register char *p; register int founddigit; p = token; c = getchar(); if (c == EOF) return (END); if (c == '\n') return (EOL); if (c == delim1 || c == delim2) { if (strip_delim) { while ((c = getchar()) && (c == delim1 || c == delim2)) ; (void)ungetc(c, stdin); } return (SPACE); } if (c == '\"') { while ((c = getchar()) && c != '\"' && c != '\n' && c != EOF) *p++ = c; if (c != '\"') (void)ungetc(c, stdin); *p = '\0'; return (ALPHA); } while (c != delim1 && c != delim2 && c!= '\n' && c != EOF) { *p++ = c; c = getchar(); } *p = '\0'; (void)ungetc(c, stdin); p = token; c = *p; founddigit = FALSE; /* * str_nums always returns numbers as strings * plainnums returns 'numbers' with [-+eE] in them as strings * lastprtnum makes sure a number ends in one of [0-9eE.] */ if (!strnums && (isdigit(c) || c == '.' || c == '-' || c == '+')) { int lastprtnum = FALSE; while (isdigit(c) || c == '.' || (!plainnums && (c == '-' || c == '+' || c == 'e' || c == 'E'))) { if (isdigit(c)) lastprtnum = founddigit = TRUE; else if (!(c == '.' || c == 'e' || c == 'E')) lastprtnum = FALSE; c = *p++; } if (c == '\0' && founddigit && lastprtnum) return (NUM); else return (ALPHA); } return (ALPHA); } /* turns [A-Z][A-Z] into a number */ int getcol(char *p) { int col; col = 0; if (!p) return (0); while (*p && !isalpha((int)*p)) p++; if (!*p) return (0); col = (toupper((int)*p) - 'A'); if (isalpha((int)*++p)) col = (col + 1)*26 + (toupper((int)*p) - 'A'); return (col); } /* given a string turn it into a row number */ int getrow(char *p) { int row; row = 0; if (!p) return (0); while (*p && !isdigit((int)*p)) p++; if (!*p) return (0); while (*p && isdigit((int)*p)) { row = row * 10 + *p - '0'; p++; } return (row); } /* turns a column number into [A-Z][A-Z] */ char * coltoa(int col) { static char rname[3]; register char *p = rname; if (col < 0 || col > 27*26) /* A-Z, AA-ZZ */ (void) fprintf(stderr,"coltoa: invalid col: %d", col); if (col > 25) { *p++ = col/26 + 'A' - 1; col %= 26; } *p++ = col+'A'; *p = '\0'; return (rname); } sc-7.16_1.1.2/psc.doc000066400000000000000000000051031326046243500140530ustar00rootroot00000000000000.\" $Revision: 7.16 $ .TH PPNAME 1 "19 September 2002" "PPNAME #REVISION#" .SH NAME ppname \- prepare pname files .SH SYNOPSIS .B ppname .RB [ -fLkrSPv ] .RB [ -s .IB cell ] .RB [ -R .IB n ] .RB [ -C .IB n ] .RB [ -n .IB n ] .RB [ -d .IB c ] .\" ========== .SH DESCRIPTION .I Ppname is used to prepare data for input to the spreadsheet calculator .I pname(1). It accepts normal ascii data on standard input. Standard output is a .I pname file. With no options, .I ppname starts the spreadsheet in cell A0. Strings are right justified. All data on a line is entered on the same row; new input lines cause the output row number to increment by one. The default delimiters are tab and space. The column formats are set to one larger than the number of columns required to hold the largest value in the column. .\" ---------- .SH OPTIONS .\" ---------- .TP .B \-f Omit column width calculations. This option is for preparing data to be merged with an existing spreadsheet. If the option is not specified, the column widths calculated for the data read by .I ppname will override those already set in the existing spreadsheet. .\" ---------- .TP .B \-L Left justify strings. .\" ---------- .TP .B \-k Keep all delimiters. This option causes the output cell to change on each new delimiter encountered in the input stream. The default action is to condense multiple delimiters to one, so that the cell only changes once per input data item. .\" ---------- .TP .B \-r Output the data by row first then column. For input consisting of a single column, this option will result in output of one row with multiple columns instead of a single column spreadsheet. .\" ---------- .TP .BI \-s " cell" Start the top left corner of the spreadsheet in .I cell. For example, .I "-s B33" will arrange the output data so that the spreadsheet starts in column B, row 33. .\" ---------- .TP .BI \-R " n" Increment by .I n on each new output row. .\" ---------- .TP .BI \-C " n" Increment by .I n on each new output column. .\" ---------- .TP .BI \-n " n" Output .I n rows before advancing to the next column. This option is used when the input is arranged in a single column and the spreadsheet is to have multiple columns, each of which is to be length .I n. .\" ---------- .TP .BI \-d " c" Use the single character .I c as the delimiter between input fields. .\" ---------- .TP .B \-P Plain numbers only. A field is a number only when there is no imbedded [-+eE]. .\" ---------- .TP .B \-S All numbers are strings. .\" ---------- .TP .B \-v Print the version of ppname .\" ---------- .SH SEE ALSO pname(1) .SH AUTHOR Robert Bond sc-7.16_1.1.2/range.c000066400000000000000000000234471326046243500140520ustar00rootroot00000000000000 /* SC A Spreadsheet Calculator * Range Manipulations * * Robert Bond, 4/87 * * $Revision: 7.16 $ */ #include #include #include #include #include #include #include "compat.h" #include "sc.h" static struct range *rng_base; void sync_enode(struct enode *e); void fix_enode(struct enode *e, int row1, int col1, int row2, int col2, int delta1, int delta2); void add_range(char *name, struct ent_ptr left, struct ent_ptr right, int is_range) { struct range *r; register char *p; int minr, minc, maxr, maxc; int minrf, mincf, maxrf, maxcf; register struct ent *rcp; struct range *prev = 0; if (left.vp->row < right.vp->row) { minr = left.vp->row; minrf = left.vf & FIX_ROW; maxr = right.vp->row; maxrf = right.vf & FIX_ROW; } else { minr = right.vp->row; minrf = right.vf & FIX_ROW; maxr = left.vp->row; maxrf = right.vf & FIX_ROW; } if (left.vp->col < right.vp->col) { minc = left.vp->col; mincf = left.vf & FIX_COL; maxc = right.vp->col; maxcf = right.vf & FIX_COL; } else { minc = right.vp->col; mincf = right.vf & FIX_COL; maxc = left.vp->col; maxcf = left.vf & FIX_COL; } left.vp = lookat(minr, minc); left.vf = minrf | mincf; right.vp = lookat(maxr, maxc); right.vf = maxrf | maxcf; if (!find_range(name, strlen(name), (struct ent *)0, (struct ent *)0, &prev)) { error("Error: range name \"%s\" already defined", name); scxfree(name); return; } for (p = name; *p; p++) if (!(isalpha((int)*p) || isdigit((int)*p) || *p == '_')) { error("Invalid range name \"%s\" - illegal combination", name); scxfree(name); return; } p = name; if (isdigit((int)*p) || (isalpha((int)*p++) && (isdigit((int)*p) || (isalpha((int)*p++) && isdigit((int)*p))))) { if (*name == '0' && (name[1] == 'x' || name[1] == 'X')) { ++p; while (isxdigit((int)*++p)) /* */; if (*p == 'p' || *p == 'P') while (isxdigit((int)*++p)) /* */; } else { while (isdigit((int)*++p)) /* */; if (isdigit((int)*name) && (*p == 'e' || *p == 'E')) while (isdigit((int)*++p)) /* */; } if (!(*p)) { error("Invalid range name \"%s\" - ambiguous", name); scxfree(name); return; } } if (autolabel && minc>0 && !is_range) { rcp = lookat(minr, minc-1); if (rcp->label==0 && rcp->expr==0 && rcp->v==0) label(rcp, name, 0); } r = scxmalloc(sizeof(struct range)); r->r_name = name; r->r_left = left; r->r_right = right; r->r_is_range = is_range; if (prev) { r->r_next = prev->r_next; r->r_prev = prev; prev->r_next = r; if (r->r_next) r->r_next->r_prev = r; } else { r->r_next = rng_base; r->r_prev = (struct range *)0; if (rng_base) rng_base->r_prev = r; rng_base = r; } modflg++; } void del_range(struct ent *left, struct ent *right) { struct range *r; int minr, minc, maxr, maxc; minr = left->row < right->row ? left->row : right->row; minc = left->col < right->col ? left->col : right->col; maxr = left->row > right->row ? left->row : right->row; maxc = left->col > right->col ? left->col : right->col; left = lookat(minr, minc); right = lookat(maxr, maxc); if (find_range((char *)0, 0, left, right, &r)) return; if (r->r_next) r->r_next->r_prev = r->r_prev; if (r->r_prev) r->r_prev->r_next = r->r_next; else rng_base = r->r_next; scxfree((char *)(r->r_name)); scxfree((char *)r); modflg++; } void clean_range(void) { register struct range *r; register struct range *nextr; r = rng_base; rng_base = (struct range *)0; while (r) { nextr = r->r_next; scxfree((char *)(r->r_name)); scxfree((char *)r); r = nextr; } } /* Match on name or lmatch, rmatch */ int find_range(char *name, int len, struct ent *lmatch, struct ent *rmatch, struct range **rng) { struct range *r; int cmp; int exact = TRUE; if (len < 0) { exact = FALSE; len = -len; } if (name) { for (r = rng_base; r; r = r->r_next) { if ((cmp = strncmp(name, r->r_name, len)) > 0) return (cmp); *rng = r; if (cmp == 0) if (!exact || strlen(r->r_name) == (size_t)len) return (cmp); } return (-1); } for (r = rng_base; r; r = r->r_next) { if ((lmatch == r->r_left.vp) && (rmatch == r->r_right.vp)) { *rng = r; return (0); } } return (-1); } void sync_ranges(void) { int i, j; struct range *r; struct ent *p; for (r = rng_base; r; r = r->r_next) { r->r_left.vp = lookat(r->r_left.vp->row, r->r_left.vp->col); r->r_right.vp = lookat(r->r_right.vp->row, r->r_right.vp->col); } for (i=0; i<=maxrow; i++) for (j=0; j<=maxcol; j++) if ((p = *ATBL(tbl,i,j)) && p->expr) sync_enode(p->expr); sync_franges(); sync_cranges(); } void sync_enode(struct enode *e) { if (e) { if ((e->op & REDUCE)) { e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col); e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col); } else if (e->op != O_VAR && e->op !=O_CONST && e->op != O_SCONST) { sync_enode(e->e.o.left); sync_enode(e->e.o.right); } } } void write_ranges(FILE *f) { register struct range *r; register struct range *nextr; for (r = nextr = rng_base; nextr; r = nextr, nextr = r->r_next) /* */ ; while (r) { (void) fprintf(f, "define \"%s\" %s%s%s%d", r->r_name, r->r_left.vf & FIX_COL ? "$":"", coltoa(r->r_left.vp->col), r->r_left.vf & FIX_ROW ? "$":"", r->r_left.vp->row); if (r->r_is_range) (void) fprintf(f, ":%s%s%s%d\n", r->r_right.vf & FIX_COL ? "$":"", coltoa(r->r_right.vp->col), r->r_right.vf & FIX_ROW ? "$":"", r->r_right.vp->row); else (void) fprintf(f, "\n"); r = r->r_prev; } } void list_ranges(FILE *f) { register struct range *r; register struct range *nextr; if (!are_ranges()) { fprintf(f, " No ranges defined"); return; } (void) fprintf(f, " %-30s %s\n","Name","Definition"); if (!brokenpipe) (void) fprintf(f, " %-30s %s\n","----","----------"); for (r = nextr = rng_base; nextr; r = nextr, nextr = r->r_next) /* */ ; while (r) { (void) fprintf(f, " %-30s %s%s%s%d", r->r_name, r->r_left.vf & FIX_COL ? "$":"", coltoa(r->r_left.vp->col), r->r_left.vf & FIX_ROW ? "$":"", r->r_left.vp->row); if (brokenpipe) return; if (r->r_is_range) (void) fprintf(f, ":%s%s%s%d\n", r->r_right.vf & FIX_COL ? "$":"", coltoa(r->r_right.vp->col), r->r_right.vf & FIX_ROW ? "$":"", r->r_right.vp->row); else (void) fprintf(f, "\n"); if (brokenpipe) return; r = r->r_prev; } } char * v_name(int row, int col) { struct ent *v; struct range *r; static char buf[20]; v = lookat(row, col); if (!find_range((char *)0, 0, v, v, &r)) { return (r->r_name); } else { snprintf(buf, sizeof buf, "%s%d", coltoa(col), row); return (buf); } } char * r_name(int r1, int c1, int r2, int c2) { struct ent *v1, *v2; struct range *r; static char buf[100]; v1 = lookat(r1, c1); v2 = lookat(r2, c2); if (!find_range((char *)0, 0, v1, v2, &r)) { return (r->r_name); } else { size_t l; snprintf(buf, sizeof buf, "%s", v_name(r1, c1)); l = strlen(buf); snprintf(buf + l, sizeof(buf) - l, ":%s", v_name(r2, c2)); return (buf); } } int are_ranges(void) { return (rng_base != 0); } void fix_ranges(int row1, int col1, int row2, int col2, int delta1, int delta2) { int r1, r2, c1, c2, i, j; struct range *r; struct frange *fr; struct ent *p; fr = find_frange(currow, curcol); /* First we fix all of the named ranges. */ if (rng_base) for (r = rng_base; r; r = r->r_next) { r1 = r->r_left.vp->row; c1 = r->r_left.vp->col; r2 = r->r_right.vp->row; c2 = r->r_right.vp->col; if (!(fr && (c1 < fr->or_left->col || c1 > fr->or_right->col))) { if (r1 >= row1 && r1 <= row2) r1 = row2 - delta1; if (c1 >= col1 && c1 <= col2) c1 = col2 - delta1; } if (!(fr && (c2 < fr->or_left->col || c2 > fr->or_right->col))) { if (r2 >= row1 && r2 <= row2) r2 = row1 + delta2; if (c2 >= col1 && c2 <= col2) c2 = col1 + delta2; } r->r_left.vp = lookat(r1, c1); r->r_right.vp = lookat(r2, c2); } /* Next, we go through all valid cells with expressions and fix any ranges * that need fixing. */ for (i=0; i<=maxrow; i++) for (j=0; j<=maxcol; j++) if ((p = *ATBL(tbl,i,j)) && p->expr) fix_enode(p->expr, row1, col2, row2, col2, delta1, delta2); fix_frames(row1, col1, row2, col2, delta1, delta2); fix_colors(row1, col1, row2, col2, delta1, delta2); } void fix_enode(struct enode *e, int row1, int col1, int row2, int col2, int delta1, int delta2) { if (e) { if ((e->op & REDUCE)) { int r, c; int r1, c1, r2, c2; struct frange *fr; fr = find_frange(currow, curcol); r1 = e->e.r.left.vp->row; c1 = e->e.r.left.vp->col; r2 = e->e.r.right.vp->row; c2 = e->e.r.right.vp->col; if (r1>r2) r = r2, r2 = r1, r1 = r; if (c1>c2) c = c2, c2 = c1, c1 = c; if (!(fr && (c1 < fr->or_left->col || c1 > fr->or_right->col))) { if (r1 != r2 && r1 >= row1 && r1 <= row2) r1 = row2 - delta1; if (c1 != c2 && c1 >= col1 && c1 <= col2) c1 = col2 - delta1; } if (!(fr && (c2 < fr->or_left->col || c2 > fr->or_right->col))) { if (r1 != r2 && r2 >= row1 && r2 <= row2) r2 = row1 + delta2; if (c1 != c2 && c2 >= col1 && c2 <= col2) c2 = col1 + delta2; } e->e.r.left.vp = lookat(r1, c1); e->e.r.right.vp = lookat(r2, c2); } else if (e->op != O_VAR && e->op !=O_CONST && e->op != O_SCONST) { fix_enode(e->e.o.left, row1, col1, row2, col2, delta1, delta2); fix_enode(e->e.o.right, row1, col1, row2, col2, delta1, delta2); } } } sc-7.16_1.1.2/sc-7.16.lsm000066400000000000000000000011741326046243500143160ustar00rootroot00000000000000Begin4 Title: sc Spreadsheet Calculator Version: 7.16 Entered-date: 2002-9-19 Description: sc is a free curses-based spreadsheet program that uses key bindings similar to vi and less. Keywords: spreadsheet calculator console textmode curses ncurses Author: Too numerous to mention. Maintained-by: nrocinu@myrealbox.com (Chuck Martin) Primary-site: ibiblio.org /pub/Linux/apps/financial/spreadsheet Alternate-site: Original-site: Platforms: Pre-7.x versions presumably worked on any Unix or Unix clone, as well as VMS and DOS. Hopefully, this is still true, but it's only been tested on Linux. Copying-policy: Public domain End sc-7.16_1.1.2/sc.c000066400000000000000000001506171326046243500133630ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Main driver * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * More mods by Alan Silverstein, 3-4/88, see list of changes. * $Revision: 7.16 $ * */ #include #include #include #include #include #include #include #ifndef MSDOS #include #endif #include #include #include #include #include "compat.h" #include "sc.h" #ifndef SAVENAME #define SAVENAME "SC.SAVE" /* file name to use for emergency saves */ #endif /* SAVENAME */ static void settcattr(void); static void scroll_down(void); static void scroll_up(int); /* Globals defined in sc.h */ struct ent ***tbl; int arg = 1; int strow = 0, stcol = 0; int currow = 0, curcol = 0; int savedrow[37], savedcol[37]; int savedstrow[37], savedstcol[37]; int FullUpdate = 0; int maxrow, maxcol; int maxrows, maxcols; int *fwidth; int *precision; int *realfmt; char *col_hidden; char *row_hidden; char line[FBUFLEN]; int changed; struct ent *delbuf[DELBUFSIZE]; char *delbuffmt[DELBUFSIZE]; int dbidx; int qbuf; /* buffer no. specified by " command */ int modflg; int cellassign; int numeric; char *mdir; char *autorun; int skipautorun; char *fkey[FKEYS]; char *scext; char *ascext; char *tbl0ext; char *tblext; char *latexext; char *slatexext; char *texext; int scrc = 0; int showsc, showsr; /* Starting cell for highlighted range */ int usecurses = TRUE; /* Use curses unless piping/redirection or using -q */ int brokenpipe = FALSE; /* Set to true if SIGPIPE is received */ #ifdef RIGHT_CBUG int wasforw = FALSE; #endif char curfile[PATHLEN]; char revmsg[80]; /* numeric separators, country-dependent if locale support enabled: */ char dpoint = '.'; /* decimal point */ char thsep = ','; /* thousands separator */ ssize_t linelim = -1; int showtop = 1; /* Causes current cell value display in top line */ int showcell = 1; /* Causes current cell to be highlighted */ int showrange = 0; /* Causes ranges to be highlighted */ int showneed = 0; /* Causes cells needing values to be highlighted */ int showexpr = 0; /* Causes cell exprs to be displayed, highlighted */ int shownote = 0; /* Causes cells with attached notes to be highlighted */ int braille = 0; /* Be nice to users of braille displays */ int braillealt = 0; /* Alternate mode for braille users */ int autocalc = 1; /* 1 to calculate after each update */ int autolabel = 1; /* If room, causes label to be created after a define */ int autoinsert = 0; /* Causes rows to be inserted if craction is non-zero and the last cell in a row/column of the scrolling portion of a framed range has been filled */ int autowrap = 0; /* Causes cursor to move to next row/column if craction is non-zero and the last cell in a row/column of the scrolling portion of a framed range has been filled */ int calc_order = BYROWS; int optimize = 0; /* Causes numeric expressions to be optimized */ int tbl_style = 0; /* headers for T command output */ int rndtoeven = 0; int color = 0; /* Use color */ int colorneg = 0; /* Increment color number for cells with negative numbers */ int colorerr = 0; /* Color cells with errors with color 3 */ int numeric_field = 0; /* Started the line editing with a number */ int craction = 0; /* 1 for down, 2 for right */ int pagesize = 0; /* If nonzero, use instead of 1/2 screen height */ int dobackups; /* Copy current database file to backup file */ /* before overwriting */ int rowlimit = -1; int collimit = -1; int rowsinrange = 1; int colsinrange = DEFWIDTH; /* a linked list of free [struct ent]'s, uses .next as the pointer */ struct ent *freeents = NULL; #ifdef VMS int VMS_read_raw = 0; #endif #ifdef NCURSES_MOUSE_VERSION static int mouse_sel_cell(int); MEVENT mevent; #endif /* return a pointer to a cell's [struct ent *], creating if needed */ struct ent * lookat(int row, int col) { register struct ent **pp; checkbounds(&row, &col); pp = ATBL(tbl, row, col); if (*pp == NULL) { if (freeents != NULL) { *pp = freeents; (*pp)->flags &= ~IS_CLEARED; (*pp)->flags |= MAY_SYNC; freeents = freeents->next; } else *pp = scxmalloc(sizeof(struct ent)); if (row > maxrow) maxrow = row; if (col > maxcol) maxcol = col; (*pp)->label = (char *)0; (*pp)->row = row; (*pp)->col = col; (*pp)->nrow = -1; (*pp)->ncol = -1; (*pp)->flags = MAY_SYNC; (*pp)->expr = (struct enode *)0; (*pp)->v = (double) 0.0; (*pp)->format = (char *)0; (*pp)->cellerror = CELLOK; (*pp)->next = NULL; } return (*pp); } /* * This structure is used to keep ent structs around before they * are deleted to allow the sync_refs routine a chance to fix the * variable references. * We also use it as a last-deleted buffer for the 'p' command. */ void free_ent(register struct ent *p, int unlock) { p->next = delbuf[dbidx]; delbuf[dbidx] = p; p->flags |= IS_DELETED; if (unlock) p->flags &= ~IS_LOCKED; } /* free deleted cells */ void flush_saved(void) { register struct ent *p; register struct ent *q; if (dbidx < 0) return; if ((p = delbuf[dbidx])) { scxfree(delbuffmt[dbidx]); delbuffmt[dbidx] = NULL; } while (p) { (void) clearent(p); q = p->next; p->next = freeents; /* put this ent on the front of freeents */ freeents = p; p = q; } delbuf[dbidx--] = NULL; } char *progname; int Vopt; #ifdef TRACE FILE *ftrace; #endif int main (int argc, char **argv) { int inloop = 1; register int c; int edistate = -1; int narg; int nedistate; int running; char *revi; int anychanged = FALSE; int tempx, tempy; /* Temp versions of curx, cury */ /* * Keep command line options around until the file is read so the * command line overrides file options */ int mopt = 0; int oopt = 0; int nopt = 0; int copt = 0; int ropt = 0; int Copt = 0; int Ropt = 0; int eopt = 0; int popt = 0; int qopt = 0; int Mopt = 0; Vopt = 0; #ifdef MSDOS if ((revi = strrchr(argv[0], '\\')) != NULL) #else #ifdef VMS if ((revi = strrchr(argv[0], ']')) != NULL) #else if ((revi = strrchr(argv[0], '/')) != NULL) #endif #endif progname = revi+1; else progname = argv[0]; #ifdef TRACE if (!(ftrace = fopen(TRACE, "w"))) { fprintf(stderr, "%s: fopen(%s, 'w') failed: %s\n", progname, TRACE, strerror(errno)); exit(1); } #endif while ((c = getopt(argc, argv, "axmoncrCReP:W:vqM")) != EOF) { switch (c) { case 'a': skipautorun = 1; break; case 'x': #if defined(VMS) || defined(MSDOS) || !defined(CRYPT_PATH) (void) fprintf(stderr, "Crypt not available\n"); exit (1); #else Crypt = 1; #endif break; case 'm': mopt = 1; break; case 'o': oopt = 1; break; case 'n': nopt = 1; break; case 'c': copt = 1; break; case 'r': ropt = 1; break; case 'C': Copt = 1; craction = CRCOLS; break; case 'R': Ropt = 1; craction = CRROWS; break; case 'e': rndtoeven = 1; eopt = 1; break; case 'P': case 'W': popt = 1; case 'v': break; case 'q': qopt = 1; break; case 'M': Mopt = 1; break; default: exit (1); } } if (!isatty(STDOUT_FILENO) || popt || qopt) usecurses = FALSE; startdisp(); signals(); settcattr(); read_hist(); /* setup the spreadsheet arrays, initscr() will get the screen size */ if (!growtbl(GROWNEW, 0, 0)) { stopdisp(); exit (1); } /* * Build revision message for later use: */ if (popt) *revmsg = '\0'; else { strlcpy(revmsg, progname, sizeof revmsg); for (revi = rev; (*revi++) != ':'; ); /* copy after colon */ strlcat(revmsg, revi, sizeof revmsg); revmsg[strlen(revmsg) - 2] = 0; /* erase last character */ strlcat(revmsg, ": Type '?' for help.", sizeof revmsg); } #ifdef MSDOS if (optind < argc) #else if (optind < argc && !strcmp(argv[optind], "--")) optind++; if (optind < argc && argv[optind][0] != '|' && strcmp(argv[optind], "-")) #endif /* MSDOS */ strlcpy(curfile, argv[optind], sizeof curfile); for (dbidx = DELBUFSIZE - 1; dbidx >= 0; ) { delbuf[dbidx] = NULL; delbuffmt[dbidx--] = NULL; } if (usecurses && has_colors()) initcolor(0); if (optind < argc) { if (!readfile(argv[optind], 1) && (optind == argc - 1)) error("New file: \"%s\"", curfile); EvalAll(); optind++; } else erasedb(); while (optind < argc) { (void) readfile(argv[optind], 0); optind++; } savedrow[0] = currow; savedcol[0] = curcol; savedstrow[0] = strow; savedstcol[0] = stcol; EvalAll(); if (!(popt || isatty(STDIN_FILENO))) (void) readfile("-", 0); if (qopt) { stopdisp(); exit (0); } clearok(stdscr, TRUE); EvalAll(); if (mopt) autocalc = 0; if (oopt) optimize = 1; if (nopt) numeric = 1; if (copt) calc_order = BYCOLS; if (ropt) calc_order = BYROWS; if (Copt) craction = CRCOLS; if (Ropt) craction = CRROWS; if (eopt) rndtoeven = 1; if (Mopt) mouseon(); if (popt) { char *redraw = NULL; int o; #ifdef BSD43 optreset = 1; #endif optind = 1; stopdisp(); while ((o = getopt(argc, argv, "axmoncrCReP:W:vq")) != EOF) { switch (o) { case 'v': Vopt = 1; break; case 'P': if (*optarg == '/') { int in, out; in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO); freopen("/dev/tty", "r", stdin); freopen("/dev/tty", "w", stdout); usecurses = TRUE; startdisp(); if (has_colors()) { initcolor(0); bkgd(COLOR_PAIR(1) | ' '); } clearok(stdscr, TRUE); FullUpdate++; linelim = 0; *line = '\0'; if (mode_ind != 'v') write_line(ctl('v')); error("Select range:"); update(1); while (!linelim) { int c_; switch (c_ = nmgetch()) { case '.': case ':': case ctl('i'): if (!showrange) { write_line(c_); break; } /* else drop through */ case ctl('m'): strlcpy(line, "put ", sizeof line); linelim = 4; write_line('.'); if (showrange) write_line('.'); strlcat(line, optarg, sizeof line); break; case ESC: case ctl('g'): case 'q': linelim = -1; break; case ctl('l'): FullUpdate++; clearok(stdscr, 1); break; default: write_line(c_); break; } /* goto switches to insert mode when done, so we * have to switch back. */ if (mode_ind == 'i') write_line(ctl('v')); CLEAR_LINE; update(1); } stopdisp(); dup2(in, STDIN_FILENO); dup2(out, STDOUT_FILENO); close(in); close(out); redraw = "recalc\nredraw\n"; } else { strlcpy(line, "put ", sizeof line); linelim = 4; strlcat(line, optarg, sizeof line); } if (linelim > 0) { linelim = 0; yyparse(); } Vopt = 0; break; case 'W': strlcpy(line, "write ", sizeof line); strlcat(line, optarg, sizeof line); linelim = 0; yyparse(); break; default: break; } } if (redraw) fputs(redraw, stdout); exit (0); } if (!isatty(STDOUT_FILENO)) { stopdisp(); write_fd(stdout, 0, 0, maxrow, maxcol); exit (0); } modflg = 0; cellassign = 0; #ifdef VENIX setbuf(stdin, NULL); #endif while (inloop) { running = 1; while (running) { nedistate = -1; narg = 1; if (edistate < 0 && linelim < 0 && autocalc && (changed || FullUpdate)) { EvalAll(); if (changed) /* if EvalAll changed or was before */ anychanged = TRUE; changed = 0; } else /* any cells change? */ if (changed) anychanged = TRUE; update(anychanged); anychanged = FALSE; #ifndef SYSV3 /* HP/Ux 3.1 this may not be wanted */ (void) refresh(); /* 5.3 does a refresh in getch */ #endif c = nmgetch(); getyx(stdscr, tempy, tempx); (void) move(1, 0); (void) clrtoeol(); (void) move(tempy, tempx); seenerr = 0; showneed = 0; /* reset after each update */ showexpr = 0; shownote = 0; /* * there seems to be some question about what to do w/ the iscntrl * some BSD systems are reportedly broken as well */ /* if ((c < ' ') || ( c == DEL )) how about international here ? PB */ #if pyr if(iscntrl(c) || (c >= 011 && c <= 015)) /* iscntrl broken in OSx4.1 */ #else if ((isascii(c) && (iscntrl(c) || (c == 020))) || /* iscntrl broken in OSx4.1 */ c == KEY_END || c == KEY_BACKSPACE) #endif switch(c) { #ifdef SIGTSTP case ctl('z'): (void) deraw(1); (void) kill(0, SIGTSTP); /* Nail process group */ /* the pc stops here */ (void) goraw(); break; #endif case ctl('r'): showneed = 1; case ctl('l'): FullUpdate++; (void) clearok(stdscr,1); break; case ctl('x'): FullUpdate++; showexpr = 1; (void) clearok(stdscr,1); break; default: error ("No such command (^%c)", c + 0100); break; case ctl('b'): { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; backrow(arg * ps); strow = strow - (arg * ps); if (strow < 0) strow = 0; FullUpdate++; } break; case ctl('c'): running = 0; break; case KEY_END: case ctl('e'): if (linelim < 0 || mode_ind == 'v') { switch (c = nmgetch()) { case KEY_UP: case ctl('p'): case 'k': doend(-1, 0); break; case KEY_DOWN: case ctl('n'): case 'j': doend( 1, 0); break; case KEY_LEFT: case KEY_BACKSPACE: case ctl('h'): case 'h': doend( 0,-1); break; case KEY_RIGHT: case ' ': case ctl('i'): case 'l': doend( 0, 1); break; case ctl('e'): case ctl('y'): while (c == ctl('e') || c == ctl('y')) { int x = arg; while (arg) { if (c == ctl('e')) { scroll_down(); } else { scroll_up(x); } arg--; } FullUpdate++; update(0); arg++; c = nmgetch(); } ungetch(c); break; case ESC: case ctl('g'): break; default: error("Invalid ^E command"); break; } } else write_line(ctl('e')); break; case ctl('y'): while (c == ctl('e') || c == ctl('y')) { int x = arg; while (arg) { if (c == ctl('e')) { scroll_down(); } else { scroll_up(x); } arg--; } FullUpdate++; update(0); arg++; c = nmgetch(); } ungetch(c); break; case ctl('f'): { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; forwrow(arg * ps); strow = strow + (arg * ps); FullUpdate++; } break; case ctl('g'): showrange = 0; linelim = -1; (void) move(1, 0); (void) clrtoeol(); break; case ESC: /* ctl('[') */ write_line(ESC); break; case ctl('d'): write_line(ctl('d')); break; case KEY_BACKSPACE: case DEL: case ctl('h'): if (linelim < 0) { /* not editing line */ backcol(arg); /* treat like ^B */ break; } write_line(ctl('h')); break; case ctl('i'): /* tab */ if (linelim < 0) { /* not editing line */ forwcol(arg); break; } write_line(ctl('i')); break; case ctl('m'): case ctl('j'): write_line(ctl('m')); break; case ctl('n'): c = craction; if (numeric_field) { craction = 0; write_line(ctl('m')); numeric_field = 0; } craction = c; if (linelim < 0) { forwrow(arg); break; } write_line(ctl('n')); break; case ctl('p'): c = craction; if (numeric_field) { craction = 0; write_line(ctl('m')); numeric_field = 0; } craction = c; if (linelim < 0) { backrow(arg); break; } write_line(ctl('p')); break; case ctl('q'): break; /* ignore flow control */ case ctl('s'): break; /* ignore flow control */ case ctl('t'): #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) error( "Toggle: a:auto,c:cell,e:ext funcs,n:numeric,t:top,x:encrypt,$:pre-scale,"); #else /* no encryption available */ error( "Toggle: a:auto,c:cell,e:ext funcs,n:numeric,t:top,$:pre-scale,"); #endif if (braille) move(1, 0); (void) refresh(); switch (nmgetch()) { case 'a': case 'A': case 'm': case 'M': autocalc ^= 1; error("Automatic recalculation %sabled.", autocalc ? "en":"dis"); break; case 'o': case 'O': optimize ^= 1; error("%sptimize expressions upon entry.", optimize ? "O":"Do not o"); break; case 'n': numeric = (!numeric); error("Numeric input %sabled.", numeric ? "en" : "dis"); break; case 't': case 'T': showtop = (!showtop); error("Top line %sabled.", showtop ? "en" : "dis"); break; case 'c': showcell = (!showcell); repaint(lastmx, lastmy, fwidth[lastcol], 0, 0); error("Cell highlighting %sabled.", showcell ? "en" : "dis"); --modflg; /* negate the modflg++ */ break; case 'b': braille ^= 1; error("Braille enhancement %sabled.", braille ? "en" : "dis"); --modflg; /* negate the modflg++ */ break; case 's': cslop ^= 1; error("Color slop %sabled.", cslop ? "en" : "dis"); break; case 'C': color = !color; if (has_colors()) { if (color) { attron(COLOR_PAIR(1)); bkgd(COLOR_PAIR(1) | ' '); } else { attron(COLOR_PAIR(0)); bkgd(COLOR_PAIR(0) | ' '); } } error("Color %sabled.", color ? "en" : "dis"); break; case 'N': colorneg = !colorneg; error("Color changing of negative numbers %sabled.", colorneg ? "en" : "dis"); break; case 'E': colorerr = !colorerr; error("Color changing of cells with errors %sabled.", colorerr ? "en" : "dis"); break; case 'x': case 'X': #if defined(VMS) || defined(MSDOS) || !defined(CRYPT_PATH) error("Encryption not available."); #else Crypt = (! Crypt); error("Encryption %sabled.", Crypt? "en" : "dis"); #endif break; case 'l': case 'L': autolabel = (!autolabel); error("Autolabel %sabled.", autolabel? "en" : "dis"); break; case '$': if (prescale == 1.0) { error("Prescale enabled."); prescale = 0.01; } else { prescale = 1.0; error("Prescale disabled."); } break; case 'e': extfunc = (!extfunc); error("External functions %sabled.", extfunc? "en" : "dis"); break; case ESC: case ctl('g'): CLEAR_LINE; --modflg; /* negate the modflg++ */ break; case 'r': case 'R': error("Which direction after return key?"); switch(nmgetch()) { case ctl('m'): craction = 0; error("No action after new line"); break; case 'j': case ctl('n'): case KEY_DOWN: craction = CRROWS; error("Down row after new line"); break; case 'l': case ' ': case KEY_RIGHT: craction = CRCOLS; error("Right column after new line"); break; case ESC: case ctl('g'): CLEAR_LINE; break; default: error("Not a valid direction"); } break; case 'i': case 'I': autoinsert = (!autoinsert); error("Autoinsert %sabled.", autoinsert? "en" : "dis"); break; case 'w': case 'W': autowrap = (!autowrap); error("Autowrap %sabled.", autowrap? "en" : "dis"); break; case 'z': case 'Z': rowlimit = currow; collimit = curcol; error("Row and column limits set"); break; default: error("Invalid toggle command"); --modflg; /* negate the modflg++ */ } FullUpdate++; modflg++; break; case ctl('u'): narg = arg * 4; nedistate = 1; break; case ctl('v'): /* switch to navigate mode, or if already * * in navigate mode, insert variable name */ if (linelim >= 0) write_line(ctl('v')); break; case ctl('w'): /* insert variable expression */ if (linelim >= 0) { static char *temp = NULL, *temp1 = NULL; static unsigned templen = 0; int templim; /* scxrealloc will scxmalloc if needed */ if (strlen(line)+1 > templen) { templen = strlen(line)+40; temp = scxrealloc(temp, templen); temp1= scxrealloc(temp1, templen); } strlcpy(temp, line, templen); templim = linelim; linelim = 0; /* reset line to empty */ editexp(currow,curcol); strlcpy(temp1, line, templen); strlcpy(line, temp, sizeof line); linelim = templim; ins_string(temp1); } break; case ctl('a'): if (linelim >= 0) write_line(c); else { remember(0); currow = 0; curcol = 0; rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); FullUpdate++; } break; case '\035': /* ^] */ if (linelim >= 0) write_line(c); break; } /* End of the control char switch stmt */ else if (isascii(c) && isdigit(c) && ((!numeric && linelim < 0) || (linelim >= 0 && (mode_ind == 'e' || mode_ind == 'v')) || edistate >= 0)) { /* we got a leading number */ if (edistate != 0) { /* First char of the count */ if (c == '0') { /* just a '0' goes to left col */ if (linelim >= 0) write_line(c); else leftlimit(); } else { nedistate = 0; narg = c - '0'; } } else { /* Succeeding count chars */ nedistate = 0; narg = arg * 10 + (c - '0'); } } else if (c == KEY_F(1) && !fkey[c - KEY_F0 - 1]) { deraw(1); system("man sc"); goraw(); clear(); } else if (linelim >= 0) { /* Editing line */ switch (c) { case ')': case ',': if (showrange) showdr(); break; default: break; } write_line(c); } else if (!numeric && ( c == '+' || c == '-' )) { /* increment/decrement ops */ register struct ent *p = *ATBL(tbl, currow, curcol); if (!p || !(p->flags & IS_VALID)) { if (c == '+') { editv(currow, curcol); linelim = strlen(line); insert_mode(); write_line(ctl('v')); } continue; } if (p->expr && !(p->flags & IS_STREXPR)) { error("Can't increment/decrement a formula\n"); continue; } FullUpdate++; modflg++; if (c == '+') p->v += (double) arg; else p->v -= (double) arg; } else if (c > KEY_F0 && c <= KEY_F(FKEYS)) { /* a function key was pressed */ if (fkey[c - KEY_F0 - 1]) { char *tpp; insert_mode(); strlcpy(line, fkey[c - KEY_F0 - 1], sizeof line); linelim = 0; for (tpp = line; *tpp != '\0'; tpp++) if (*tpp == '\\' && *(tpp + 1) == '"') memmove(tpp, tpp + 1, strlen(tpp)); for (tpp = line; *tpp != '\0'; tpp++) { char mycell[9]; size_t l; l = strlcpy(mycell, coltoa(curcol), sizeof mycell); snprintf(mycell + l, sizeof(mycell) - l, "%d", currow); if (*tpp == '$' && *(tpp + 1) == '$') { memmove(tpp + strlen(mycell), tpp + 2, strlen(tpp + 1)); memcpy(tpp, mycell, strlen(mycell)); tpp += strlen(mycell); } } write_line(ctl('m')); } } else /* switch on a normal command character */ switch (c) { case ':': if (linelim >= 0) write_line(':'); break; /* Be nice to vi users */ case '@': EvalAll(); changed = 0; anychanged = TRUE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': if (locked_cell(currow, curcol)) break; /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; numeric_field = 1; snprintf(line, sizeof line, "let %s = %c", v_name(currow, curcol), c); linelim = strlen(line); insert_mode(); break; case '+': case '-': if (!locked_cell(currow, curcol)) { struct ent *p = lookat(currow, curcol); /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; numeric_field = 1; editv(currow, curcol); linelim = strlen(line); insert_mode(); if (c == '-' || p->flags & IS_VALID) write_line(c); else write_line(ctl('v')); } break; case '=': if (locked_cell(currow, curcol)) break; /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; snprintf(line, sizeof line, "let %s = ", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); break; case '!': doshell(); break; /* * Range commands: */ case 'r': error( "Range: x:erase v:value c:copy f:fill d:def l:lock U:unlock S:show u:undef F:fmt"); if (braille) move(1, 0); (void) refresh(); c = nmgetch(); CLEAR_LINE; switch (c) { case 'l': snprintf(line, sizeof line, "lock [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'U': snprintf(line, sizeof line, "unlock [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'c': snprintf(line, sizeof line, "copy [dest_range src_range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'm': snprintf(line, sizeof line, "move [destination src_range] %s ", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); write_line(ctl('v')); break; case 'x': snprintf(line, sizeof line, "erase [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'y': snprintf(line, sizeof line, "yank [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'v': snprintf(line, sizeof line, "value [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'f': snprintf(line, sizeof line, "fill [range start inc] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'd': snprintf(line, sizeof line, "define [string range] \""); linelim = strlen(line); insert_mode(); break; case 'u': snprintf(line, sizeof line, "undefine [range] "); linelim = strlen(line); insert_mode(); break; case 'r': error("frame (top/bottom/left/right/all/unframe)"); if (braille) move(1, 0); refresh(); linelim = 0; c = nmgetch(); CLEAR_LINE; switch (c) { case 't': snprintf(line, sizeof line, "frametop [ rows] "); break; case 'b': snprintf(line, sizeof line, "framebottom [ rows] "); break; case 'l': snprintf(line, sizeof line, "frameleft [ cols] "); break; case 'r': snprintf(line, sizeof line, "frameright [ cols] "); break; case 'a': snprintf(line, sizeof line, "frame [ inrange] "); break; case 'u': snprintf(line, sizeof line, "unframe [] "); break; case ESC: case ctl('g'): linelim = -1; break; default: error("Invalid frame command"); linelim = -1; break; } if (linelim == 0) { linelim = strlen(line); insert_mode(); } if (c == 'a' || c == 'u') startshow(); break; case 's': snprintf(line, sizeof line, "sort [range \"criteria\"] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'C': snprintf(line, sizeof line, "color [range color#] "); linelim = strlen(line); insert_mode(); startshow(); break; case 'S': /* Show color definitions and various types of * ranges */ if (!are_ranges() && !are_frames() && !are_colors()) { error("Nothing to show"); } else { FILE *f; int pid; char px[MAXCMD]; char *pager; strlcpy(px, "| ", sizeof px); if (!(pager = getenv("PAGER"))) pager = DFLT_PAGER; strlcat(px, pager, sizeof px); f = openfile(px, sizeof px, &pid, NULL); if (!f) { error("Can't open pipe to %s", pager); break; } fprintf(f, "Named Ranges:\n=============\n\n"); if (!brokenpipe) list_ranges(f); if (!brokenpipe) fprintf(f, "\n\nFrames:\n=======\n\n"); if (!brokenpipe) list_frames(f); if (!brokenpipe) fprintf(f, "\n\nColors:\n=======\n\n"); if (!brokenpipe) list_colors(f); closefile(f, pid, 0); } break; case 'F': snprintf(line, sizeof line, "fmt [range \"format\"] "); linelim = strlen(line); insert_mode(); startshow(); break; case '{': snprintf(line, sizeof line, "leftjustify [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case '}': snprintf(line, sizeof line, "rightjustify [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case '|': snprintf(line, sizeof line, "center [range] "); linelim = strlen(line); insert_mode(); startshow(); break; case ESC: case ctl('g'): break; default: error("Invalid region command"); break; } break; case '~': snprintf(line, sizeof line, "abbrev \""); linelim = strlen(line); insert_mode(); break; case '"': error("Select buffer (a-z or 0-9):"); if ((c=nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; } else if (c >= '0' && c <= '9') { qbuf = c - '0' + (DELBUFSIZE - 10); CLEAR_LINE; } else if (c >= 'a' && c <= 'z') { qbuf = c - 'a' + (DELBUFSIZE - 36); CLEAR_LINE; } else if (c == '"') { qbuf = 0; CLEAR_LINE; } else error("Invalid buffer"); break; /* * Row/column commands: */ case KEY_IC: case 'i': case 'o': case 'a': case 'd': case 'y': case 'p': case 'v': case 's': case 'Z': { int rcqual; if (!(rcqual = get_rcqual(c))) { error("Invalid row/column command"); break; } CLEAR_LINE; /* clear line */ if (rcqual == ESC || rcqual == ctl('g')) break; switch (c) { case 'i': if (rcqual == 'r') insertrow(arg, 0); else insertcol(arg, 0); break; case 'o': if (rcqual == 'r') insertrow(arg, 1); else insertcol(arg, 1); break; case 'a': if (rcqual == 'r') while (arg--) duprow(); else while (arg--) dupcol(); break; case 'd': if (rcqual == 'r') deleterow(arg); else closecol(arg); break; case 'y': if (rcqual == 'r') yankrow(arg); else yankcol(arg); break; case 'p': if (rcqual == '.') { snprintf(line, sizeof line, "pullcopy "); linelim = strlen(line); insert_mode(); startshow(); break; } while (arg--) pullcells(rcqual); break; /* * turn an area starting at currow/curcol into * constants vs expressions - not reversable */ case 'v': if (rcqual == 'r') { struct frange *fr; if ((fr = find_frange(currow, curcol))) valueize_area(currow, fr->or_left->col, currow + arg - 1, fr->or_right->col); else valueize_area(currow, 0, currow + arg - 1, maxcol); } else valueize_area(0, curcol, maxrow, curcol + arg - 1); modflg++; break; case 'Z': switch (rcqual) { case 'r': hiderow(arg); break; case 'c': hidecol(arg); break; case 'Z': if (modflg && curfile[0]) { writefile(curfile, 0, 0, maxrow, maxcol); running = 0; } else if (modflg) { error("No file name."); } else running = 0; break; } break; case 's': /* special case; no repeat count */ if (rcqual == 'r') rowshow_op(); else colshow_op(); break; } break; } case '$': rightlimit(); break; case '#': gotobottom(); break; case 'w': { register struct ent *p; while (--arg>=0) { do { if (curcol < maxcols - 1) curcol++; else { if (currow < maxrows - 1) { while(++currow < maxrows - 1 && row_hidden[currow]) ; curcol = 0; } else { error("At end of table"); break; } } } while(col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } rowsinrange = 1; colsinrange = fwidth[curcol]; break; } case 'b': { register struct ent *p; while (--arg>=0) { do { if (curcol) curcol--; else { if (currow) { while(--currow && row_hidden[currow]) ; curcol = maxcols - 1; } else { error("At start of table"); break; } } } while (col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } rowsinrange = 1; colsinrange = fwidth[curcol]; break; } case '^': gototop(); break; #ifdef KEY_HELP case KEY_HELP: #endif case '?': help(); break; case '\\': if (!locked_cell(currow, curcol)) { /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; snprintf(line, sizeof line, "label %s = \"", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); } break; case '<': if (!locked_cell(currow, curcol)) { /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; snprintf(line, sizeof line, "leftstring %s = \"", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); } break; case '>': if (!locked_cell(currow, curcol)) { /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; snprintf(line, sizeof line, "rightstring %s = \"", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); } break; case '{': { struct ent *p = *ATBL(tbl, currow, curcol); if (p && p->label) ljustify(currow, curcol, currow, curcol); else error("Nothing to justify"); break; } case '}': { struct ent *p = *ATBL(tbl, currow, curcol); if (p && p->label) rjustify(currow, curcol, currow, curcol); else error("Nothing to justify"); break; } case '|': { struct ent *p = *ATBL(tbl, currow, curcol); if (p && p->label) center(currow, curcol, currow, curcol); else error("Nothing to center"); break; } case 'e': if (!locked_cell(currow, curcol)) { struct ent *p = lookat(currow, curcol); /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; editv(currow, curcol); if (!(p->flags & IS_VALID)) { linelim = strlen(line); insert_mode(); } else edit_mode(); } break; case 'E': if (!locked_cell(currow, curcol)) { /* set mark 0 */ savedrow[27] = currow; savedcol[27] = curcol; savedstrow[27] = strow; savedstcol[27] = stcol; edits(currow, curcol); edit_mode(); } break; case 'f': formatcol(arg); break; case 'F': { register struct ent *p = *ATBL(tbl, currow, curcol); if (p && p->format) { snprintf(line, sizeof line, "fmt [format] %s \"%s", v_name(currow, curcol), p->format); edit_mode(); linelim = strlen(line) - 1; } else { snprintf(line, sizeof line, "fmt [format] %s \"", v_name(currow, curcol)); insert_mode(); linelim = strlen(line); } break; } case 'C': { if (braille) { braillealt ^= 1; break; } error("Color number to set (1-8)?"); if ((c=nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; break; } if ((c -= ('1' - 1)) < 1 || c > 8) { error("Invalid color number."); break; } CLEAR_LINE; snprintf(line, sizeof line, "color %d = ", c); linelim = strlen(line); if (cpairs[c-1] && cpairs[c-1]->expr) { decompile(cpairs[c-1]->expr, 0); line[linelim] = '\0'; edit_mode(); } else { insert_mode(); } break; } #ifdef KEY_FIND case KEY_FIND: #endif case 'g': snprintf(line, sizeof line, "goto [v] "); linelim = strlen(line); insert_mode(); break; case 'n': go_last(); break; case 'P': snprintf(line, sizeof line, "put [\"dest\" range] \""); /* See the comments under "case 'W':" below for an explanation of the * logic here. */ curfile[strlen(curfile) + 1] = '\0'; if (strrchr(curfile, '.') != NULL) { size_t l; if (!strcmp((strrchr(curfile, '.')), ".sc")) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + 3; strlcpy(curfile + l, ".\0", sizeof(curfile) - l); } else if (scext != NULL && !strcmp((strrchr(curfile, '.') + 1), scext)) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + strlen(scext) + 1; strlcpy(curfile + l, ".\0", sizeof(curfile) - l); } } if (*curfile) error("Default path is \"%s.%s\"", curfile, scext == NULL ? "sc" : scext); c = *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)); *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)) = '\0'; curfile[strlen(curfile)] = c; linelim = strlen(line); insert_mode(); break; case 'M': snprintf(line, sizeof line, "merge [\"source\"] \""); linelim = strlen(line); insert_mode(); break; case 'R': if (mdir) snprintf(line, sizeof line, "merge [\"macro_file\"] \"%s", mdir); else snprintf(line, sizeof line, "merge [\"macro_file\"] \""); linelim = strlen(line); insert_mode(); break; case 'D': (void) snprintf(line, sizeof line, "mdir [\"macro_directory\"] \""); linelim = strlen(line); insert_mode(); break; case 'A': if (autorun) snprintf(line, sizeof line,"autorun [\"macro_file\"] \"%s", autorun); else snprintf(line, sizeof line, "autorun [\"macro_file\"] \""); linelim = strlen(line); insert_mode(); break; case 'G': snprintf(line, sizeof line, "get [\"source\"] \""); if (*curfile) error("Default file is \"%s\"", curfile); linelim = strlen(line); insert_mode(); break; case 'W': snprintf(line, sizeof line, "write [\"dest\" range] \""); /* First, append an extra null byte to curfile. Then, if curfile ends in * ".sc" (or '.' followed by the string in scext), move the '.' to the * end and replace it with a null byte. This results in two consecutive * null-terminated strings, the first being curfile with the ".sc" (or '.' * and scext) removed, if present, and the second being either "sc." (or * scext and '.') or "", depending on whether the ".sc" (or '.' and scext) * was present or not. */ curfile[strlen(curfile) + 1] = '\0'; if (strrchr(curfile, '.') != NULL) { size_t l; if (!strcmp((strrchr(curfile, '.')), ".sc")) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + 3; strlcpy(curfile + l, ".\0", sizeof(curfile) - l); } else if (scext != NULL && !strcmp((strrchr(curfile, '.') + 1), scext)) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + strlen(scext) + 1; strlcpy(curfile + l, ".\0", sizeof(curfile) - l); } } /* Now append ".asc" (or '.' and the value of ascext) to the possibly * truncated curfile. */ if (*curfile) error("Default file is \"%s.%s\"", curfile, ascext == NULL ? "asc" : ascext); /* Now swap the '.' and null bytes again. If there is no '.', swap a * null byte with itself. This may seem convoluted, but it works well, * and obviates the need for a 1024 byte temporary buffer. - CRM */ c = *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)); *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)) = '\0'; curfile[strlen(curfile)] = c; linelim = strlen(line); insert_mode(); break; case 'S': /* set options */ snprintf(line, sizeof line, "set "); error("Options:byrows,bycols,iterations=n,tblstyle=(0|tbl|latex|slatex|tex|frame),"); linelim = strlen(line); insert_mode(); break; case 'T': /* tbl output */ snprintf(line, sizeof line, "tbl [\"dest\" range] \""); /* See the comments under "case 'W':" above for an explanation of the * logic here. */ curfile[strlen(curfile) + 1] = '\0'; if (strrchr(curfile, '.') != NULL) { size_t l; if (!strcmp((strrchr(curfile, '.')), ".sc")) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + 3; strlcpy(curfile + l, ".\0", sizeof(curfile) - l); } else if (scext != NULL && !strcmp((strrchr(curfile, '.') + 1), scext)) { *strrchr(curfile, '.') = '\0'; l = strlen(curfile) + strlen(scext) + 1; strlcpy(curfile + l, ".\0", sizeof(curfile) - 1); } } if (*curfile && tbl_style == 0) { error("Default file is \"%s.%s\"", curfile, tbl0ext == NULL ? "cln" : tbl0ext); } else if (*curfile && tbl_style == TBL) { error("Default file is \"%s.%s\"", curfile, tblext == NULL ? "tbl" : tblext); } else if (*curfile && tbl_style == LATEX) { error("Default file is \"%s.%s\"", curfile, latexext == NULL ? "lat" : latexext); } else if (*curfile && tbl_style == SLATEX) { error("Default file is \"%s.%s\"", curfile, slatexext == NULL ? "stx" : slatexext); } else if (*curfile && tbl_style == TEX) { error("Default file is \"%s.%s\"", curfile, texext == NULL ? "tex" : texext); } c = *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)); *(curfile + strlen(curfile) + strlen(curfile + strlen(curfile) + 1)) = '\0'; curfile[strlen(curfile)] = c; linelim = strlen(line); insert_mode(); break; #ifdef KEY_DC case KEY_DC: #endif case 'x': if (calc_order == BYROWS) eraser(lookat(currow, curcol), lookat(currow, curcol + arg - 1)); else eraser(lookat(currow, curcol), lookat(currow + arg - 1, curcol)); break; case 'Q': case 'q': running = 0; break; case KEY_LEFT: case 'h': backcol(arg); break; case KEY_DOWN: case 'j': forwrow(arg); break; case KEY_UP: case 'k': backrow(arg); break; case 'H': backcol(curcol - stcol + 2); break; #ifdef KEY_NPAGE case KEY_NPAGE: /* next page */ #endif case 'J': { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; forwrow(arg * ps); strow = strow + (arg * ps); FullUpdate++; } break; #ifdef KEY_PPAGE case KEY_PPAGE: /* previous page */ #endif case 'K': { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; backrow(arg * ps); strow = strow - (arg * ps); if (strow < 0) strow = 0; FullUpdate++; } break; #ifdef KEY_HOME case KEY_HOME: gohome(); break; #endif case 'L': forwcol(lcols - (curcol - stcol) + 1); break; case KEY_RIGHT: case ' ': case 'l': forwcol(arg); break; case 'm': markcell(); break; case 'c': error("Copy marked cell:"); if ((c = nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; break; } if (c == '.') { copy(NULL, NULL, lookat(currow, curcol), NULL); snprintf(line, sizeof line, "copy [dest_range src_range] "); linelim = strlen(line); insert_mode(); startshow(); break; } if (c == '`' || c == '\'') c = 0; else if (!(((c -= ('a' - 1)) > 0 && c < 27) || ((c += ('a' - '0' + 26)) > 26 && c < 37))) { error("Invalid mark (must be a-z, 0-9, ` or \')"); break; } if (savedrow[c] == -1) { error("Mark not set"); break; } CLEAR_LINE; { struct ent *p = *ATBL(tbl, savedrow[c], savedcol[c]); int c1; struct ent *n; for (c1 = curcol; arg-- && c1 < maxcols; c1++) { if ((n = *ATBL(tbl, currow, c1))) { if (n->flags & IS_LOCKED) continue; if (!p) { (void) clearent(n); continue; } } else { if (!p) break; n = lookat(currow, c1); } copyent(n, p, currow - savedrow[c], c1 - savedcol[c], 0, 0, maxrow, maxcol, 0); n->flags |= IS_CHANGED; } FullUpdate++; modflg++; break; } case '`': case '\'': dotick(c); break; case '*': { register struct ent *p; error("Note: Add/Delete/Show/*(go to note)?"); if ((c = nmgetch()) == ESC || c == ctl('g')) { CLEAR_LINE; break; } if (c == 'a' || c == 'A') { snprintf(line, sizeof line, "addnote [target range] %s ", v_name(currow, curcol)); linelim = strlen(line); insert_mode(); write_line(ctl('v')); CLEAR_LINE; FullUpdate++; break; } if (c == 'd' || c == 'D') { p = lookat(currow, curcol); p->nrow = p->ncol = -1; p->flags |= IS_CHANGED; CLEAR_LINE; modflg++; FullUpdate++; break; } if (c == 's' || c == 'S') { FullUpdate++; shownote = 1; clearok(stdscr,1); error("Highlighted cells have attached notes."); break; } if (c == '*') { gotonote(); CLEAR_LINE; break; } error("Invalid command"); break; } case 'z': switch (c = nmgetch()) { case ctl('m'): strow = currow; FullUpdate++; (void) clearok(stdscr,1); break; case '.': strow = -1; FullUpdate++; (void) clearok(stdscr,1); break; case '|': stcol = -1; FullUpdate++; (void) clearok(stdscr,1); break; case 'c': /* Force centering of current cell (or range, if * we've just jumped to a new range with the goto * command). */ strow = -1; stcol = -1; FullUpdate++; (void) clearok(stdscr,1); break; default: break; } break; #ifdef KEY_RESIZE case KEY_RESIZE: #ifndef SIGWINCH winchg(); #endif break; #endif #ifdef NCURSES_MOUSE_VERSION case KEY_MOUSE: if (getmouse(&mevent) != OK) break; if (mevent.bstate & BUTTON1_CLICKED) { mouse_sel_cell(0); update(0); } else if (mevent.bstate & BUTTON1_PRESSED) { mouse_sel_cell(1); } else if (mevent.bstate & BUTTON1_RELEASED) { if (!mouse_sel_cell(2)) update(0); } # if NCURSES_MOUSE_VERSION >= 2 else if (mevent.bstate & BUTTON4_PRESSED) { scroll_up(1); FullUpdate++; update(0); } else if (mevent.bstate & BUTTON5_PRESSED) { scroll_down(); FullUpdate++; update(0); } # endif break; #endif default: if ((toascii(c)) != c) { error ("Weird character, decimal %d\n", (int) c); } else { error ("No such command (%c)", c); } break; } edistate = nedistate; arg = narg; } /* while (running) */ inloop = modcheck(" before exiting"); } /* while (inloop) */ stopdisp(); write_hist(); #ifdef VMS /* Until VMS "fixes" exit we should say 1 here */ exit (1); #else exit (0); #endif /*NOTREACHED*/ } /* set the calculation order */ void setorder(int i) { if ((i == BYROWS) || (i == BYCOLS)) calc_order = i; } void setauto(int i) { autocalc = i; } void signals(void) { (void) signal(SIGINT, doquit); #if !defined(MSDOS) (void) signal(SIGQUIT, dump_me); (void) signal(SIGPIPE, nopipe); (void) signal(SIGALRM, time_out); #ifndef __DJGPP__ (void) signal(SIGBUS, doquit); #endif #endif (void) signal(SIGTERM, doquit); (void) signal(SIGFPE, doquit); #ifdef SIGWINCH (void) signal(SIGWINCH, winchg); #endif } #ifdef SIGVOID void #else int #endif nopipe(int i) { (void)i; brokenpipe = TRUE; } #ifdef SIGVOID void #else int #endif winchg(int i) { (void)i; stopdisp(); startdisp(); /* * I'm not sure why a refresh() needs to be done both before and after * the clearok() and update(), but without doing it this way, a screen * (or window) that grows bigger will leave the added space blank. - CRM */ refresh(); FullUpdate++; (void) clearok(stdscr, TRUE); update(1); refresh(); #ifdef SIGWINCH (void) signal(SIGWINCH, winchg); #endif } #ifdef SIGVOID void #else int #endif doquit(int i) { (void)i; if (usecurses) { diesave(); stopdisp(); } write_hist(); exit (1); } #ifdef SIGVOID void #else int #endif dump_me(int i) { (void)i; if (usecurses) diesave(); deraw(1); abort(); } /* try to save the current spreadsheet if we can */ void diesave(void) { char path[PATHLEN]; if (modcheck(" before Spreadsheet dies") == 1) { snprintf(path, sizeof path, "~/%s", SAVENAME); if (writefile(path, 0, 0, maxrow, maxcol) < 0) { snprintf(path, sizeof path, "/tmp/%s", SAVENAME); if (writefile(path, 0, 0, maxrow, maxcol) < 0) error("Couldn't save current spreadsheet, Sorry"); } } } /* check if tbl was modified and ask to save */ int modcheck(char *endstr) { if (modflg && curfile[0]) { int yn_ans; char lin[100]; snprintf(lin, sizeof lin, "File \"%s\" is modified, save%s? ",curfile,endstr); if ((yn_ans = yn_ask(lin)) < 0) return(1); else if (yn_ans == 1) { if (writefile(curfile, 0, 0, maxrow, maxcol) < 0) return (1); } } else if (modflg) { int yn_ans; if ((yn_ans = yn_ask("Do you want a chance to save the data? ")) < 0) return(1); else return(yn_ans); } return(0); } /* Returns 1 if cell is locked, 0 otherwise */ int locked_cell(int r, int c) { struct ent *p = *ATBL(tbl, r, c); if (p && (p->flags & IS_LOCKED)) { error("Cell %s%d is locked", coltoa(c), r) ; return(1); } return(0); } /* Check if area contains locked cells */ int any_locked_cells(int r1, int c1, int r2, int c2) { int r, c; struct ent *p ; for (r=r1; r<=r2; r++) for (c=c1; c<=c2; c++) { p = *ATBL(tbl, r, c); if (p && (p->flags&IS_LOCKED)) return(1); } return(0); } static void settcattr(void) { #ifdef VDSUSP static struct termios tty; # ifdef _PC_VDISABLE static long vdis; if ((vdis = fpathconf(STDIN_FILENO, _PC_VDISABLE)) == -1) { fprintf(stderr, "fpathconf(STDIN, _PC_VDISABLE) failed: %s\n", strerror(errno)); vdis = 255; } # else # define vdis 255 # endif if (tcgetattr(STDIN_FILENO, &tty) == -1) { fprintf(stderr, "tcgetattr STDIN failed: %s\n", strerror(errno)); return; } tty.c_cc[VDSUSP] = vdis; if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tty) == -1) { fprintf(stderr, "tcsetattr STDIN failed: %s\n", strerror(errno)); return; } #endif /* VDSUSP */ } void mouseon(void) { #ifdef NCURSES_MOUSE_VERSION mousemask(BUTTON1_CLICKED # if NCURSES_MOUSE_VERSION >= 2 | BUTTON4_PRESSED | BUTTON5_PRESSED # endif , NULL); # if NCURSES_MOUSE_VERSION < 2 error("Warning: NCURSES_MOUSE_VERSION < 2"); # endif #else error("Error: NCURSES_MOUSE_VERSION undefined"); #endif } void mouseoff(void) { #if NCURSES_MOUSE_VERSION >= 2 mousemask(0, NULL); #endif } #ifdef NCURSES_MOUSE_VERSION int mouse_sel_cell(int mode) { /* 0: set, 1: save, 2: cmp and set */ int i, y, x, tx, ty; static int x1, y1; if ((y = mevent.y - RESROW) < 0 || (x = mevent.x - rescol) < 0) return 1; for (ty = strow, i = y; ; ty++) { if (row_hidden[ty]) continue; if (--i < 0) break; } for (tx = stcol, i = x; ; tx++) { if (col_hidden[tx]) continue; if ((i -= fwidth[tx]) < 0) break; } switch (mode) { case 1: y1 = ty; x1 = tx; break; case 2: if (y1 != ty || x1 != tx) break; default: currow = ty; curcol = tx; return 0; } return 1; } #endif static void scroll_down(void) { strow++; while (row_hidden[strow]) strow++; if (currow < strow) currow = strow; } static void scroll_up(int x) { if (strow) strow--; while (strow && row_hidden[strow]) strow--; forwrow(x); if (currow >= lastendrow) backrow(1); backrow(x); } sc-7.16_1.1.2/sc.doc000066400000000000000000003712701326046243500137060ustar00rootroot00000000000000.\" Warning: The string "pname" is converted to the true program name .\" by the makefile, throughout this document. .\" .\" Warning: The order of presentation of commands largely matches the .\" help screen built into the program. .\" .\" Conventions: .\" - pname italicized and never uppercased (it's a proper name). .\" - Refer to lists of commands in the same order as introduced. .\" - Command and function names bold when introduced, italicized in all .\" other places if possible, or in \(lq \(rq if not. .\" - Cell names italicized except when used in expressions; row numbers .\" and column names not italicized. .\" - Use \(lq \(rq rather than `` '' or " " except referring to literal .\" input or output. .\" - TPs use default indent except for function names, then 18. .\" - Smallify uppercase strings. .\" - Avoid passive voice and third person. .\" $Revision: 7.16 $ .\" .TH PNAME 1 "April 14, 2016" "PNAME #REVISION#" .SH NAME pname \- spreadsheet calculator .SH SYNOPSIS .SY pname .OP \-aCceMmnoqRrvx .RB [ \-P .IR range [/ address ] | .RI / address ] .OP \-W range .RI [ file .IR .\|.\|. ] .YS .\" ========== .SH DESCRIPTION The spreadsheet calculator .B pname is based on rectangular tables much like a financial spreadsheet. When invoked, it first looks for a file in the user's home directory called .scrc and if found, reads that file into memory. If that file contains the command .RB \(lq set\ scrc \(rq, .B pname looks for a file called .scrc in the current directory, and if found, reads that file into memory, too. Next, it reads the options from the command line, and finally, it reads in the file or files specified on the command line and presents the data in a table organized as rows and columns of cells. If invoked without a .I file argument, the table is initially empty, unless it is running in a pipeline, in which case it will read its data from the standard input. If more than one file is specified, all files except the first one will be merged. The default filename for saving a file with the .B Put command will be the same as the first file specified, and the other files will be treated as macros. If you want to use advanced macros from the command line, the .RB \(oq | \(cq must be quoted to prevent it from being expanded by the shell. .LP Options begin with .RB \(oq \- \(cq. However, an argument of a single .RB \(oq \- \(cq will be interpreted to mean that spreadsheet data will be taken from the standard input. This is useful for including .B pname in a pipeline if the system supports pipes. However, if standard input is not a terminal, the .RB \(oq \- \(cq is only necessary if there are multiple files and standard input is not the last to be read, since standard input is automatically read in after all other files in such cases if it is not specified explicitly, or if there are no other filenames on the command line. If .B pname is included in a pipeline, and a filename of .RB \(oq \- \(cq is not specified, the standard input will be merged in after all of the other named files have been processed. .LP The first argument not beginning with a .RB \(oq \- \(cq, or a single .RB \(oq \- \(cq by itself, and any subsequent arguments will all be interpreted as filenames (a filename of .RB \(oq \- \(cq meaning standard input as noted above). In addition, an argument of .RB \(lq \-\- \(rq may be used to signify that all subsequent arguments should be treated as filenames even if they begin with a .RB \(oq \- \(cq, but unlike .RB \(oq \- \(cq, .RB \(lq \-\- \(rq won't be treated as a filename itself. .LP Each cell may have associated with it a numeric value, a label string, and/or an expression (formula) which evaluates to a numeric value or label string, often based on other cell values. .LP For an online tutorial, type the command: .IP pname #LIBDIR#/tutorial.pname .LP To generate a quick reference card in PDF format, type the command (for traditional .BR troff ): .IP pnameqref | troff \-ms | dpost | ps2pdf \- .IR filename .pdf .LP Or for using .BR groff : .IP pnameqref | troff \-Tpdf \-ms | gropdf > .IR filename .pdf .\" ---------- .SH OPTIONS .\" ---------- .TP .B \-a Do not run the autorun macro, if one is specified in the file. .\" ---------- .TP .B \-c Start the program with the recalculation being done in column order. .\" ---------- .TP .B \-e Start the program with round-to-even (banker's rounding) enabled. .\" ---------- .TP .B \-m Start the program with automatic recalculation disabled. The spreadsheet will be recalculated only when the .RB \(lq @ \(rq command is used. .\" ---------- .TP .B \-n Start the program in quick numeric entry mode (\TQuickNumericEntryModesee below\T). .\" ---------- .TP .B \-o Start the program with automatic optimization of expressions enabled. .\" ---------- .TP .B \-q Quit after loading all files, but before becoming interactive. This is useful in shell scripts for getting information from a file, for example, or using .B pname as a non-interactive calculator using the .B eval command. .\" ---------- .TP .B \-r Start the program with the recalculation being done in row order (default option). .\" ---------- .TP .B \AFl-v\-v When piping data out using the .B \-P option (below), change all expressions to values. The .B \-v option must precede the .B \-P option to have an effect. If the .B \-P option is used more than once, there must be a separate .B \-v option for each instance of the .B \-P option. .\" ---------- .TP .B \AFl-x\-x Cause the .B \TGetGet\T and .B \TPutPut\T commands (see below) to encrypt and decrypt data files. .\" ---------- .TP .B \-C Start the program with automatic newline action set to increment the column (\TNewlineActionsee below\T). .\" ---------- .TP .B \-M Process mouse events (see options .BR \Tset_mousemouse / !mouse\T ). This option overrides a .RB \(lq "set !mouse" \(rq command in .BR .scrc . It is used by .BR xsc (1). .TP \fB\-P\fI range\fR[/\fIaddress\fR] .PD 0 .TP \fB\-P\fR /\fIaddress\fR .PD Pipe a range to standard output. The output is similar to that of the .B \TPutPut\T command (below), except that only cell data and formatting information for cells in the range are output, without all of the colors, range definitions, column formatting, etc. The optional .I /address is used to adjust all addresses in the range to a new starting point. This is useful for copying data from one file to another, especially when used in conjunction with the .B \TFl-v\-v\T option (above), using something like .B "merge ""|sc \-v \-Prange/address filename""" (note the pipe symbol). This option may be used more than once to specify multiple ranges. Note, however, that the .B \-v option must precede the .B \-P option on the command line, and there must be a separate .B \-v option for each instance of the .B \-P option. Any instance of .B \-P not preceded by its own .B \-v option will output unevaluated expressions. A range of .RB \(lq % \(rq may be used to refer to the entire spreadsheet. If the range is left out, as shown in the second form above, .B pname will be started interactively in navigate mode, allowing you to navigate the spreadsheet and highlight the range you want to output. Pressing , ^G, or q will terminate without outputting any data. .\" ---------- .TP .B \-R Start the program with automatic newline action set to increment the row (\TNewlineActionsee below\T). .\" ---------- .TP .B \-W Pipe a range to standard output. The output is identical to that of the .B \TWriteWrite\T command (below). This option may be used more than once to specify multiple ranges. A range of .RB \(lq % \(rq may be used to refer to the entire spreadsheet. .\" ---------- .PP All of these options can be changed with the .B \TToggleOptions^T\T and .B \TSetOptionsS\T commands (see below) while .B pname is running. Options specified when .B pname is invoked override options saved in the data file. .\" ========== .SS "Personal Initialization File" .\" ---------- When .B pname first starts, it looks for a file in the user's home directory called .scrc and if found, loads it into memory. The format of this file is the same as any other .B pname file, but should be reserved for setting certain defaults. Any options set which have equivalent command line options may be overridden by the command line. If that file contains the command .RB \(lq set\ scrc \(rq, .B pname will then look for a file called .scrc in the current directory, and if found, load that file into memory, too (this is analogous to the \(lqset exrc\(rq command used by vi/ex). These \(lqdotfiles\(rq may be created by any text editor. Several commands exist specifically for setting default file name extensions in the .scrc file, although they may also be used from macros, ordinary spreadsheet files, or from within .B pname at the command line. They will not, however, be saved along with the file. The extensions should be quoted, and should not include the preceding \(oq.\(cq (e.g., .B scext\ "sc" will add the extension .B .sc ). These commands are: .\" ---------- .TP .B scext This is the default extension for normal .B pname files (those created with the .B Put command). If this command is not used, all .B pname files will be saved without an extension, and any existing extension will not be removed. Setting this option causes all .B pname files to be saved with the specified extension added, unless it is already present. If the file name already has an extension of .B .sc, it will first be removed. Any other extension will not be removed. .\" ---------- .TP .B ascext This is the default extension for plain text files created with the .B Write command. The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .asc will be used. .\" ---------- .TP .B tbl0ext This is the default extension for files created with the .B Tbl command if tblstyle is set to 0 (default). The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .cln will be used. .\" ---------- .TP .B tblext This is the default extension for files created with the .B Tbl command if tblstyle is set to tbl. The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .tbl will be used. .\" ---------- .TP .B latexext This is the default extension for files created with the .B Tbl command if tblstyle is set to latex. The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .lat will be used. .\" ---------- .TP .B slatexext This is the default extension for files created with the .B Tbl command if tblstyle is set to slatex. The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .stx will be used. .\" ---------- .TP .B texext This is the default extension for files created with the .B Tbl command if tblstyle is set to tex. The file name will first be checked to see if it already has an extension of either .B .sc or the extension specified with .B scext above, and if either one exists, it will first be removed before adding the new extension. If this option is not set, a default of .B .tex will be used. .TP .BI "histfile \(dq" filename \(dq Set history filename or disable saving the history. The default filename is .RB \(lq ~/.sc_history \(rq. If the filename begins with .RB \(oq ~/ \(cq the tilde is expanded with the contents of the environment variable .BR $HOME . If it begins with .RB \(oq ~ \(cq followed by a user name, this sequence is expanded with the home directory of that user found in the password database. The command .RB \(lq "histfile \(dq\(dq" \(rq (empty .IR filename ) disables using a history file. .\" ========== .SS "General Information" .\" ---------- The screen is divided into four regions. The top line is for entering commands and displaying cell values. The second line is for messages from .BR pname . The third line and the first four columns show the column and row numbers, from which are derived cell addresses, e.g. .B A0 for the cell in column A, row 0. Note that column names are case-insensitive: you can enter .B A0 or .BR a0 . .\" ---------- .PP The rest of the screen forms a window looking at a portion of the table. The total number of display rows and columns available, hence the number of table rows and columns displayed, is set by .BR curses (3) and may be overridden by setting the .SM LINES and .SM COLUMNS environment variables, respectively. .\" ---------- .PP The screen has two cursors: a cell cursor, indicated by either a highlighted cell or a \(lq<\(rq on the screen, and a character cursor, indicated by the terminal's hardware cursor. .\" ---------- .PP If a cell's numeric value is wider than the column width (see the .B \TFCmdf\T command), the cell is filled with asterisks. If a cell's label string is wider than the column width, it is truncated at the start of the next non-blank cell in the row, if any. .\" ---------- .PP Cursor control commands and row and column commands can be prefixed by a numeric argument which indicates how many times the command is to be executed. You can type .B ^U before a repeat count if quick numeric entry mode is enabled. .\" ========== .SS "Changing Options" .\" ---------- \0 \" exactly one blank line (hard to get) .PD 0 .TP .BI \AToggleOptions^T o Toggle options. This command allows you to switch the state of one option selected by .IR o . A small menu lists the choices for .I o when you type .BR ^T . Unless otherwise noted, the options selected are saved when the data and formulas are saved so that you will have the same setup next time you enter the spreadsheet. .PD .RS .\" ---------- .TP .B a Automatic Recalculation. When set, each change in the spreadsheet causes the entire spreadsheet be recalculated. Normally this is not noticeable, but for very large spreadsheets, it may be faster to clear automatic recalculation mode and update the spreadsheet via explicit \(lq@\(rq commands. Default is automatic recalculation on. .\" ---------- .TP .B b Braille enhancement mode. See the \Tbraillebraille section\T under the .B Set command below for a complete description of how to use this mode. This option is not saved when saving a file, to allow blind and sighted users to easily share files. It is intended for use in a user's .B .scrc file. .\" ---------- .TP .B c Current cell highlighting. If enabled, the current cell is highlighted (using the terminal's standout mode, if available) and the cell pointer \(lq<\(rq is turned off. This is enabled by default. .\" ---------- .TP .B e External function execution. When disabled, external functions (see .BR \T@ext@ext ()\T below) are not called. This saves a lot of time at each screen update. External functions are disabled by default. If disabled, and external functions are used anywhere, a warning is printed each time the screen is updated, and the result of .BR @ext () is the value from the previous call, if any, or a null string. .\" ---------- .TP .B i Automatic insertion of rows/columns. If this is enabled and craction is set to move the cell cursor either down or to the right after entering data into a cell, and the last cell in a row/column in the scrolling portion of a framed range was just filled, causing the cell cursor to move outside of this range, a new column/row will be inserted, thus enlarging the range and allowing you to continue entering data into the row/column without overwriting the frame (which may contain expressions of some sort, such as totals). If autowrap is also enabled, it will take precedence, and a new row/column will only be inserted after entering data in the very last cell (bottom right corner) of the scrolling range. The default is no automatic insertion. .\" ---------- .TP .B w Automatic wrap to next row/column. If this is enabled and craction is set to move the cell cursor either down or to the right after entering data into a cell, and the last cell in a row/column in the scrolling portion of a framed range was just filled, causing the cell cursor to move outside of this range, the cell cursor will move to the first cell in the next row/column in this range. If this would also take the cursor out of the scrolling portion of the range, the cursor will remain in last edited cell instead, unless autoinsert is also enabled, in which case a new row/column will be added so that the cursor can wrap. The default is no autowrap. .\" ---------- .TP .B l Autolabeling. If enabled, using the define command (rd) causes a label to be automatically generated in the cell to the left of the defined cell. This is only done if the cell to the left is empty. Default is enabled. .\" ---------- .TP .B \AQuickNumericEntryModen Quick numeric entry. If enabled, a typed digit is assumed to be the start of a numeric value for the current cell, not a repeat count, unless preceded by .BR ^U . Also, the \(oq+\(cq and \(oq\-\(cq keys will enter insert mode and append a \(oq+\(cq or \(oq\-\(cq to the existing contents of the cell, allowing the user to easily add to or subtract from the current numeric contents of the cell. The cursor controls .RB ( ^P , .BR ^N , or any of the arrow keys) in this mode will end a numeric entry if the entry was started by pressing \(oq+\(cq, \(oq\-\(cq, or a digit. Switching from insert mode to edit mode will cause the cursor controls to revert to their normal functions. .\" ---------- .TP .B o Automatic optimization of expressions. If this is enabled, expressions which evaluate to a constant are automatically optimized upon entry. For example, if you enter @pow(2,32) into a cell, the value 4294967296 will be stored in that cell, whereas if optimization is turned off, the calculated value will be displayed, but the actual expression will be stored in the cell instead. This allows you to edit the expression instead of re-entering it from scratch when you just want to make a minor change. Default is automatic optimization off. .\" ---------- .TP .B t Top line display. If enabled, the name and value of the current cell is displayed on the top line. If there is an associated label string, the first character of the string value is \(lq|\(rq for a \TEnterCenterStringcentered string\T, \(lq<\(rq for a \TEnterLeftStringleftstring\T or \(lq>\(rq for a \TEnterRightStringrightstring\T (see below), followed by "\fIstring\fP" for a constant string or .RI { expr } for a string expression. A constant string may be preceeded with a backslash (\(oq\\\(cq). In this case the constant string will be used as a \(lqwheel\(rq to fill a column, e.g. "\\-" for a line in a column, and "\\Yeh\ " for "Yeh\ Yeh\ Ye". If the cell has a numeric value, it follows as .RI [ value ], which may be a constant or expression. .\" ---------- .TP .B $ Dollar prescale. If enabled, all numeric .B constants (not expressions) which you enter are multipled by 0.01 so you don't have to keep typing the decimal point if you enter lots of dollar figures. .\" ---------- .TP .B r \ANewlineActionNewline action. This is a 3-way toggle which determines which direction to move after pressing the RETURN key to enter data into a cell. It has the same effect as using the .B set (S) command to set the value of craction. After selecting this option, you will be prompted for the direction you want to move. Valid directions are down (craction=1) and to the right (craction=2). Pressing j, ^N, or the cursor-down key will cause the cursor to move down a cell each time you press the RETURN key and pressing l, the cursor-right key, or the space bar will cause the cursor to move one cell to the right. Pressing the RETURN key at the prompt selects no action (craction=0, which means that the cursor will remain in the current cell). No action is the default unless .B pname is started with either the \-R or \-C option. This option is ignored if the cell into which data is being entered is not the current cell. .\" ---------- .TP .B s Enable/disable color slop. If a cell's label string is wider than the column width, it will slop over into the next cell to the right if that cell is empty. However, if that cell is in a different color range than the first, this slopover will be disabled, regardless of whether the colors assigned to the two ranges are different or not. If cslop is enabled, strings may slop over even if the next cell is in a different color range, carrying their color with them, which may cause a ragged boundary between the ranges, but may allow the strings to be seen in their entirety. Cslop is disabled by default. .\" ---------- .TP .B x Encryption. See the .B \TFl-x\-x\T option. .\" ---------- .TP .B z Set newline action limits. This option sets limits to the newline action option above. When this option is invoked, the row and column of the current cell are remembered. If a later newline action would take the current cell to the right of the remembered column, then the current cell is instead moved to the first column of the next row. If a newline action would take the current cell below the remembered row, then the current cell is instead moved to the top row of the next column. .\" ---------- .TP .B C Color. This option enables color, and must be set before any other color options, such as colorneg (color negative numbers) or colorerr (color cells with errors), will have an effect. On a slow connection, turning off color can noticeably speed up screen updates. .\" ---------- .TP .B E Color cells with errors. Setting this option will cause all cells with expressions which evaluate to ERROR or INVALID to be set to color 3. Color must be enabled for this option to take effect. .\" ---------- .TP .B N Color negative numbers. When this option is set, all cells containing negative numbers will have their color number incremented by one. Cells with color 8 will cycle back to color 1. Color must be enabled for this option to take effect. .PP The quick numeric entry, newline action and set newline action limits options can be combined to allow very quick entry of large amounts of data. If all the data to be entered is in a single row or column then setting the quick numeric entry and the appropriate newline action will allow the numbers to be entered without any explicit commands to position the current cell or enter a number. .PP If the data entry involves several entries in each row for many rows, then setting the quick numeric entry option, setting the newline action to move right after each entry and setting the newline action limits on the last column on which data should be entered will allow the data to entered quickly. An alternative to setting newline action limits is to enclose the range for entry in a frame (see \(lq\TFramedRangesFramed Ranges\T\(rq below), and setting the autowrap option. Setting autoinsert will insert new rows as needed if the frame includes data at the bottom. If necessary, columns which do not need data to be entered can be hidden with the .B z command. Similar arrangements can be made for entering several rows of data in each column. .RE .\" ---------- \0 \" exactly one blank line (hard to get) .PD 0 .TP .B \ASetOptionsS Set options. This command allows you to set various options. A small menu lists the options that cannot be changed through .B ^T above. .PD .RS .\" ---------- .TP .BR byrows / bycols Specify the order cell evaluation when updating. These options also affect the order in which cells are filled (see .BR \TrfCmdrf\T ) and whether a row or column is cleared by an .B x command. .\" ---------- .TP .BI iterations= n Set the maximum number of recalculations before the screen is displayed again. .I Iterations is set to 10 by default. .\" ---------- .TP .BI tblstyle= s Control the output of the .B T command. .I s can be: .B 0 (default) to give colon delimited fields, with no .B tbl control lines; .B tbl to give colon delimited fields, with .BR tbl (1) control lines; .B latex to give a .B LaTeX tabular environment; .B slatex to give a .B SLaTeX (Scandinavian LaTeX) tabular environment; .B tex to give a .B TeX simple tabbed alignment with ampersands as delimiters; and .B frame to give a tblstyle output for FrameMaker. .\" ---------- .TP .BI pagesize= n Set the page size for the PageUp, PageDown, J, and K commands. If set to 0, the default is to move up or down half the number of rows displayed on the screen, or if the current cell is in a framed range, half the number of displayed rows in the scrolling region of that range. .\" ---------- .PP Other .B Set options are normally used only in .B pname data files since they are available through .BR ^T . You can also use them interactively. .\" ---------- .TP .BR autocalc / !autocalc Set/clear auto recalculation mode. .\" ---------- .TP .BR autoinsert / !autoinsert Set/clear automatic insertion mode. .\" ---------- .TP .BR autowrap / !autowrap Set/clear autowrap mode. .\" ---------- .TP .BR optimize / !optimize Set/clear auto optimize mode. .\" ---------- .TP .BR numeric / !numeric Set/clear numeric mode. .\" ---------- .TP .BR prescale / !prescale Set/clear numeric prescale mode. .\" ---------- .TP .BR extfun / !extfun Enable/disable external functions. .\" ---------- .TP .BR toprow / !toprow Set/clear top row display mode. .\" ---------- .TP .BR rndtoeven / !rndtoeven Default: *.5 will be rounded up to the next integer; doing a 'set rndtoeven' will cause it to be rounded to the closest even number instead (aka banker's rounding). Round-to-even has advantages over the default rounding for some applications. For example, if X+Y is an integer, then X+Y = rnd(X)+rnd(Y) with round-to-even, but not always with the defaulting rounding method. This could be an advantage, for example, when trying to split an odd amount of money evenly between two people (it would determine who gets the extra penny). Note: rndtoeven only effects the @rnd and @round functions. It has no effect on how a number is rounded to fit the display format of a cell. .\" ---------- .TP .BI craction= n Set the newline action. .I n can be: .B 0 (default) to give no action; .B 1 to move down after each entry; or .B 2 to move right after each entry. .TP .BI rowlimit= n Set the remembered limit for the maximum row below which the current cell will be moved to the top of the next column if the newline action is set to move the current cell down. .I n can be .B \-1 (default) to disable this facility. .\" ---------- .TP .BI collimit= n Set the remembered limit for the maximum column to the right of which the current cell will be moved to the left of the next row if the newline action is set to move the current cell right. .I n can be .B \-1 (default) to disable this facility. .\" ---------- .TP .BR color / !color Enable color. This option must be set for any other color options, such as colorneg or colorerr, to take effect. On a slow connection, turning off color can noticeably speed up screen updates. .\" ---------- .TP .BR colorneg / !colorneg Color negative numbers. When this option is set, all cells containing negative numbers will have their color number increased by one. Cells with color 8 will cycle back to color 1. Color must be enabled for this option to take effect. .\" ---------- .TP .BR colorerr / !colorerr Color cells with errors. Setting this option will cause all cells with expressions which evaluate to ERROR or INVALID to be set to color 3. Color must be enabled for this option to take effect. .\" ---------- .TP .BR cslop / !cslop Enable color slop. If a cell's label string is wider than the column width, it will slop over into the next cell to the right if that cell is empty. However, if that cell is in a different color range than the first, this slop over will be disabled, regardless of whether the colors assigned to the two ranges are different or not. If cslop is enabled, strings may slop over even if the next cell is in a different color range, carrying their color with them, which may cause a ragged boundary between the ranges, but may allow the strings to be seen in their entirety. Cslop is disabled by default. .PP The following .B Set options are considered personal preferences, or are terminal dependent, and are therefore not saved when saving a file, but are instead intended for use in a user's .scrc file. .\" ---------- .TP .BR \Abraillebraille / !braille Set/clear braille enhancement mode. When braille enhancement mode is set, the cursor behaves in a manner that makes the use of .B pname much easier when using a braille display. In spite of its name, this mode also works well with screen readers such as SpeakUp, and can even be used by sighted users to make cutting and pasting using the .I screen program much easier. There are actually two different braille modes. When the braille option is set, the .B C command, which is normally used to set colors, will instead change from one braille mode to the other. If it is desired to set/change colors so you can share files with others not using a braille display, braille mode will have to be switched off temporarily, and then switched back on after the color operation is done. When the braille option is set, the default braille mode will cause the cursor to be positioned at the left edge of the current cell, while the alternate braille mode will cause the cursor to be placed at the beginning of the top line, which will contain information such as the current cell address, contents of the cell, and column formatting information. The column names will also be moved to the left edge of their respective columns in order to remain aligned with the cursor as it moves up and down the column. In either mode, the cursor will be placed in the top line when editing a line, except when switching to navigate mode, in which case the cursor will be placed in either the current cell (default braille mode) or the second line, where the cell address or default range will be displayed (alternate braille mode). Whenever a message is displayed on the second line, such as an error message or prompt for further information, both modes will cause the cursor to be placed at the beginning of that message. After this message goes away, the cursor will revert to its former behavior. The easiest way to make this message go away without effecting anything, except in the cases where it is asking the user for more information, is to press .BR CC , which effectively changes modes twice, with a net effect of leaving .B pname in the original mode. .\" ---------- .TP .BR locale / !locale If locale support is compiled into .BR pname , this option will cause certain locale-dependent behaviors, such as the display of numbers and the determination of word boundaries for some operations in edit mode. Note that if this option is set and the environment variable LC_ALL is unrecognized, unset, or set to either \(lqPOSIX\(rq or \(lqC\(rq, commas in format commands will be ignored. .\" ---------- .TP .BR cellcur / !cellcur Set/clear current cell highlighting mode. This option is included here because it is likely to be terminal dependent and/or a user preference, and therefore is not saved when saving a file. .\" ---------- .TP .B scrc It tells .B pname to also read the file .scrc in the current directory when starting. Settings in this file will override those in $HOME/.scrc but may themselves be overridden by command line options. Setting this could be a potential security risk, since starting .B pname with an unknown .scrc could potentially execute arbitrary commands. This risk is probably very slight, since a spreadsheet program is not likely to be run in just any directory, and should .B never be run as root. .TP .BR backup / !backup Before the database is written to an existing file a backup of that file can be saved as \fIfilename\fP\fB~\fP. This is enabled/disabled by this option. The default is .B not to do backups. .TP .BR \Aset_mousemouse / !mouse Process mouse events. By default mouse support is disabled for compatibility reasons with traditional .BR sc . If mouse events are enabled, the shift key must be pressed to use the mouse \(oqcut and paste\(cq feature. Currently selecting cells with the left mouse button, vertical scrolling using the mouse wheel (requires ncurses version \(>= 6), and selecting the position in the edit line with the left mosue button is implemented. .RE .\" ========== .SS "Cursor Control Commands" .\" ---------- \0 \" exactly one blank line (hard to get) .PD 0 .TP .B ^A Go to cell .B A0 (same as ). .PD .\" ---------- .TP .B ^P Move the cell cursor up to the previous row. .\" ---------- .TP .B ^N Move the cell cursor down to the next row. .\" ---------- .TP .B ^H Move the cell cursor backward one column. .\" ---------- .TP Move the cell cursor forward one column. When in navigate mode, if a range is highlighted, insert the highlighted range into the command line, followed by a space, while remaining in navigate mode. This is useful when entering copy, move, or frame commands, for example, which accept more than one range argument. .\" ---------- .TP .B "h, j, k, l" These are alternate, .BR vi -compatible cell cursor controls (left, down, up, right). Space is just like l (right). .TP .B "H, J, K, L" These move the cursor by half pages (left, down, up, right). If .I pagesize is nonzero, up/down paging will be by .I pagesize rows, instead. .\" ---------- .TP .BR ^F ", " ^B Same as J and K above. .\" ---------- .TP , Same as J and K above. .\" ---------- .TP If the character cursor is on the top line, tries to complete a range name if the character immediately preceding it is alphanumeric or \(lq_\(rq, and starts a range if not (see below). Otherwise, move the cell cursor forward one column. .\" ---------- .TP Go to cell .BR A0 . .\" ---------- .TP Same as \TCTRL-E^E\T (see below). .\" ---------- .TP The terminal's arrow keys provide another alternate set of cell cursor controls if they exist and are supported in the appropriate .B termcap entry. Some terminals have arrow keys which conflict with other control key codes. For example, a terminal might send .B ^H when the back arrow key is pressed. In these cases, the conflicting arrow key performs the same function as the key combination it mimics. .\" ---------- .TP .B ^ Move the cell cursor up to row 0 of the current column. .\" ---------- .TP .B # Move the cell cursor down to the last valid row of the current column. .\" ---------- .TP .B 0 Move the cell cursor backward to column A of the current row. This command must be prefixed with .B ^U if quick numeric entry mode is enabled. .\" ---------- .TP .B $ Move the cell cursor forward to the last valid column of the current row. .\" ---------- .TP .B b Scan the cursor backward (left and up) to the previous valid cell. .\" ---------- .TP .B w Scan the cursor forward (right and down) to the next valid cell. .\" ---------- .TP .B g Go to a cell. .B pname prompts for a cell's name, a regular expression surrounded by quotes, or a number. If a cell's name such as .B ae122 or the name of a defined range is given, the cell cursor goes directly to that cell. If a quoted regular expression such as " .B Tax Table " or " .B ^Jan [0-9]*$ " is given, .B pname searches for a cell containing a string matching the regular expression. See .BR regex (3) or .BR ed (1) for more details on the form of regular expressions. .IP You can also search formatted numbers or expressions using regular expressions by preceding the opening quotes of the regular expression with a \(lq#\(rq (for formatted numbers) or a \(lq%\(rq (for expressions). These are handy for searching for dates within a specified range or cells which reference a given cell, for example, although they are somewhat slower than searching through ordinary strings, since all numbers must be formatted or expressions decompiled on the fly during the search. .IP If a number is given, .B pname will search for a cell containing that number. Searches for either strings or numbers proceed forward from the current cell, wrapping back to a0 at the end of the table, and terminate at the current cell if the string or number is not found. You may also go to a cell with an ERROR (divide by zero, etc. in this cell) or INVALID (references a cell containing an ERROR). .B g\ error will take you to the next ERROR, while .B g\ invalid take you to the next INVALID. The last .B g command is saved, and can be re-issued by entering .BR g . You can also repeat the last search by pressing .BR n . .IP An optional second argument is available whose meaning depends on whether you're doing a search or jumping to a specific cell. When doing a search, the second argument specifies a range to search. When jumping to a specific cell, the second argument specifies which cell should be in the upper lefthand corner of the screen, if possible, which allows you to position the destination cell where you want it on the screen. .\" ---------- .TP .BI \ACTRL-E^E d Go to end of range. Follow .B ^E by a direction indicator such as .B ^P or .BR j . If the cell cursor starts on a non-blank cell, it goes in the indicated direction until the last non-blank adjacent cell. If the cell cursor starts on a blank cell, it goes in the indicated direction until the first non-blank cell. This command is useful when specifying ranges of adjacent cells (see below), especially when the range is bigger than the visible window. .IP If .B ^E is pressed twice in succession, or if it is pressed after another .B ^E or a .BR ^Y , it will cause the screen to scroll up without moving the cell cursor, unless the cell cursor is already at the top of the screen, in which case, it will remain at the top of the visible screen. .\" ---------- .TP .B ^Y Causes the screen to scroll down without moving the cell cursor, unless the cell cursor is already at the bottom of the screen, in which case, it will remain at the bottom of the visible screen. .\" ---------- .TP .BI m x Mark the current cell. .B pname will prompt for a lowercase letter to be used as a mark specifier. Marked cells may be used as the source for the .B c (copy a marked cell) command, or as the target of a .B ` or .B ' (go to marked cell) command. In addition to cells marked with lowercase letters, .B pname also automatically marks the last nine cells that have been edited with the numbers 1-9, and the current cell being edited with the number 0. When not editing a cell, marks 0 and 1 usually refer to the same cell, unless the last edit was begun in one cell, but the cell address was changed before pressing the RETURN key, or the last edit was aborted prematurely. .\" ---------- .TP .BI ` x Jump to a previously marked cell. If the target cell is currently on the screen, .B pname will simply jump to the target cell, making it current. Otherwise, .B pname will attempt to center the cell on the screen, if possible. As a special case, following the ` with another ` will return you to the cell you were in before the last g, ', `, *, or ^E (or END key) was used to jump to another cell. .\" ---------- .TP .BI ' x Jump to a previously marked cell. .B ' works just like .B ` , except that .B ' will attempt to restore the marked cell to the same position on the screen as when it was marked. It does this by remembering which cell was in the upper left hand corner of the screen at the time the mark was set, and restoring that cell to its original position. As a special case, following the ' with another ' will return you to the cell you were in before the last g, ', `, *, or ^E (or END key) was used to jump to another cell, and will also try to position that cell on the screen in the same position as when you left it. .\" ---------- .TP .BR z Move the current row to the top of the screen. If the current row is in a framed range, move the current row to the top of the scrolling region. .\" ---------- .TP .B z. Move the current row to the center of the screen. .\" ---------- .TP .B z| Move the current column to the center of the screen. .\" ---------- .TP .B zc Center the current cell both horizontally and vertically. .\" ========== .SS "Cell Entry and Editing Commands" .\" ---------- Cells can contain both a numeric value and a string value. Either value can be the result of an expression, but not both at once, i.e. each cell can have only one expression associated with it. Entering a valid numeric expression alters the cell's previous numeric value, if any, and replaces the cell's previous string expression, if any, leaving only the previously computed constant label string. Likewise, entering a valid string expression alters the cell's the previous label string, if any, and replaces the cell's previous numeric expression, if any, leaving only the previously computed constant numeric value. .TP .B = Enter a numeric constant or expression into the current cell. .B pname prompts for the expression on the top line. The usual way to enter a number into a cell is to type \(lq=\(rq, then enter the number in response to the prompt on the top line. The quick numeric entry option, enabled through the .B \-n option or .B ^T command, shows the prompt when you enter the first digit of a number (you can skip typing \(lq=\(rq). If you want to begin entering an expression in the current cell, but you want to start out in navigate mode (e.g. to enter cell addresses, or sums of ranges using \(lq@sum\(rq), use the \(lq+\(rq command instead (see below). .\" ---------- .TP .B \AEnterLeftString< Enter a label string into the current cell to be flushed left against the left edge of the cell. .\" ---------- .TP .B \AEnterCenterString\(rs Enter a label string into the current cell to be centered in the column. .\" ---------- .TP .B \AEnterRightString> Enter a label string into the current cell to be flushed right against the right edge of the cell. .\" ---------- .TP .B { Left justify the string in the current cell. .\" ---------- .TP .B | Center the string in the current cell. .\" ---------- .TP .B } Right justify the string in the current cell. .\" ---------- .TP .B F Enter a format string into the current cell. This format string overrides the precision specified with the .B f command unless .B & is present in the fractional part of the format string (see below). The format only applies to numeric values. There are two types of format strings allowed: standard numeric and date. (Note: these format strings may also be used with the .B f command to create user-defined format types.) The following characters can be used to build a standard numeric format string: .RS .TP .BR # Digit placeholder. If the number has fewer digits on either side of the decimal point than there are \(oq#\(cq characters in the format, the extra \(oq#\(cq characters are ignored. The number is rounded to the number of digit placeholders as there are to the right of the decimal point. If there are more digits in the number than there are digit placeholders on the left side of the decimal point, then those digits are displayed. .TP .BR 0 Digit placeholder. Same as for \(oq#\(cq except that the number is padded with zeroes on either side of the decimal point. The number of zeroes used in padding is determined by the number of digit placeholders after the \(oq0\(cq for digits on the left side of the decimal point and by the number of digit placeholders before the \(oq0\(cq for digits on the right side of the decimal point. .TP .BR . Decimal point. Determines how many digits are placed on the right and left sides of the decimal point in the number. If .B locale is set, the decimal point for the user's current locale will be used when formatting a number. Note that numbers smaller than 1 will begin with a decimal point if the left side of the decimal point contains only a \(oq#\(cq digit placeholder. Use a \(oq0\(cq placeholder to get a leading zero in decimal formats. .TP .BR % Percentage. For each \(oq%\(cq character in the format, the actual number gets multiplied by 100 (only for purposes of formatting \(em the original number is left unmodified) and the \(oq%\(cq character is placed in the same position as it is in the format. .TP .BR , Thousands separator. The presence of a \(oq,\(cq in the format (multiple commas are treated as one) will cause the number to be formatted with a \(oq,\(cq separating each set of three digits in the integer part of the number with numbering beginning from the right end of the integer. If .B locale is set, the thousands separator for the user's current locale will be used in place of the comma. If the environment variable LC_ALL is unset, unrecognized, or is set to \(lqPOSIX\(rq or \(lqC\(rq, any commas in the format string will be ignored. .TP .BR & Precision. When this character is present in the fractional part of the number, it is equivalent to a number of 0's equal to the precision specified in the column format command. For example, if the precision is 3, \(oq&\(cq is equivalent to `000'. .TP .BR \e Quote. This character causes the next character to be inserted into the formatted string directly with no special interpretation. .TP .BR E\-\ E+\ e\-\ e+ Scientific format. Causes the number to be formatted in scientific notation. The case of the \(oqE\(cq or \(oqe\(cq given is preserved. If the format uses a \(oq+\(cq, then the sign is always given for the exponent value. If the format uses a \(oq\-\(cq, then the sign is only given when the exponent value is negative. Note that if there is no digit placeholder following the \(oq+\(cq or \(oq\-\(cq, then that part of the formatted number is left out. In general, there should be one or more digit placeholders after the \(oq+\(cq or \(oq\-\(cq. .TP .BR ; Format selector. Use this character to separate the format into two distinct formats. The format to the left of the \(oq;\(cq character will be used if the number given is zero or positive. The format to the right of the \(oq;\(cq character is used if the number given is negative. .RE .IP Some example formats are integer (\(lq0\(rq or \(lq#\(rq), fixed (\(lq0.00\(rq), percentage (\(lq0%\(rq or \(lq0.00%\(rq), scientific (\(lq0.00E+00\(rq), and currency (\(lq$#,0.00;($#,0.00)\(rq). .IP Date format strings are identified by the presence of a ^D in the first postition. If this is present, the remainder of the string is passed to the strftime() function, and therefore uses the same conversion specifiers as strftime(). For more information on conversion specifiers for date format strings, see the man page for strftime(3). .\" ---------- .PP Strings you enter must start with ". You can leave off the trailing " and .B pname will add it for you. You can also enter a string expression by backspacing over the opening " in the prompt. .\" ---------- .TP .B e Edit the value associated with the current cell. This is identical to \(lq=\(rq except that the command line starts out containing the old numeric value or expression associated with the cell. The editing in this mode is vi-like. .RS .TP .BR ^H Move back a character .TP .BR "^V, v" Enter navigate mode. This mode allows you to navigate the spreadsheet while editing a command. When in navigate mode, .B v will insert the numeric value of the current cell, if any, into the command line, instead, while .B ^V will return to the previous mode (like the ESCAPE key). .TP .BR ^W Insert the expression attached to the current cell into the command line. If there is none, the result is \(lq?\(rq. This only works while in navigate mode. .TP .BR ^A In navigate mode, go to cell .BR A0 . When not in navigate mode, jump to the beginning of the line instead. .TP .BR ^E Jump to the end of the line. Unlike \(lq$\(rq (below), this can also be used from insert mode. .TP If the character immediately preceding the cursor is alphanumeric or \(lq_\(rq, tries to find a match in the list of range names, and if one is found, the name will be completed on the command line. If there are multiple matches, pressing repeatedly without any other intervening keys will cycle through all of the valid matches. If the character immediately preceding the cursor is not alphanumeric or \(lq_\(rq, defines a range of cells via the cursor control commands or the arrow keys. Pressing automatically switches .B pname to navigate mode if you haven't already done so using the .B ^V command, and the range is highlighted, starting at the cell where you typed , and continuing through the current cell. Pressing again causes the highlighted range to be inserted into the command line, the highlighting to be turned off, and the previous mode to be restored. This is most useful for defining ranges to functions such as .BR @sum (). Pressing \(lq)\(rq acts just like typing the key the second time and adds the closing \(lq)\(rq. Note that when you give a range command, if the first argument to the command is a range, you don't need to press the first to begin defining a range starting with the current cell. .TP .B : Synonym for , when in navigate mode. .TP .B "\(oq \(cq" In navigate mode, go to marked cell. .TP .B * In navigate mode, go to note linked to current cell. .TP .B + Forward through history (same as j) .TP .B \- Backward through history (same as k) .TP Done editing .TP Save. When in navigate mode, insert the name of the current cell (the one at the cell cursor) into the command line. This is useful when entering expressions which refer to other cells in the table. .TP .B $ Goto last column .TP .B % Goto matching parenthesis .TP .B . Insert current dot buffer. When in navigate mode, this is a synonym for : or . .TP .BR ; Repeat the last f, F, t, or T command. .TP .BR , Repeat the last f, F, t, or T command, but in the reverse direction. .TP .BR ~ Change the case of the character under the cursor. .TP .BR / Search backwards for a string in the history .TS ll. edit the string you typed search \fB^H\fP backspace .TE .TP .B ? Search forward for a string in the history (see \(lq/\(rq above) .TP .B 0 Goto column 0 .TP .B B Move back a word. Like .BR b , except words are space delimited only. .TP .B C Change to end of line (delete first, then enter insert mode) .TP .B D Delete to end of line .TP .B F Find the next char typed, moving backwards in the line .TP .B G Go to the end of history, i.e., to the line being currently entered .TP .B I Insert at column 0; revert back to edit mode .TP .B N Repeat the last search in the opposite direction .TP .B P Insert the most recently deleted text before the cursor .TP .B R Replace mode; revert back to edit mode .TP .B T Goto a char, moving backwards in the line .TP .B W Forward a word. Like .BR w , except words are space delimited only. .TP .B X Delete the char to the left .TP .B a Append after cursor; revert back to edit mode .TP .B b Move back a word .TP .B c Change mode; revert back to edit mode. In navigate mode, insert color range which includes the current cell. .TP .BR d Delete ... .TS ll. \fB0\fP delete to beginning of line \fB$\fP delete to end of line \fBb\fP back word \fBe\fP delete to end of word \fBf\fP forward (right) \fBh\fP back char \fBl\fP forward \fBt\fP delete forward up to a given char (next char typed) \fBw\fP delete next word forward .TE .TP .B e Forward to next end-of-word .TP .B f Find the next char typed. In navigate mode, insert the outer frame range which includes the current cell. .TP .B g In navigate mode, allows you to `goto' a cell or range, just like the regular .B goto command. Ignored in edit, insert or replace modes. .TP .B h Move left a char .TP .B i Insert before cursor; revert back to edit mode .TP .B j Forward through history (same as +) .TP .B k Backward through history (same as \-) .TP .B l Move right a char .TP .B n Repeat the last search (find the next match) .TP .B o When highlighting a range in navigate mode, move to the opposite corner of the highlighted range. .TP .B p Insert the most recently deleted text after the cursor .TP .B q Stop editing .TP .B r Replace char. In navigate mode, insert the inner frame range which includes the current cell. .TP .B s Delete current char and enter insert mode (stands for substitute) .TP .B t Goto a char .TP .B u Undo .TP .B w Forward a word .TP .B x Delete the current char (moving to the right) .TP .B y Copies to the delete buffer without deleting. Use like d (above). .RE .\" ---------- .TP .B E Edit the string associated with the current cell. This is identical to \(lq<\(rq, \(lq\\\(rq, or \(lq>\(rq except that the command line starts out containing the old string value or expression associated with the cell. See .B e above. .\" ---------- .PP To enter and edit a cell's number part, use the \(lq=\(rq, \(lq+\(rq, and .B e commands. To enter and edit a cell's string part, use the \(lq<\(rq, \(lq\\\(rq, \(lq>\(rq, and .B E commands. See the sections below on numeric and string expressions for more information. Note that the descriptions of the \(lq+\(rq and \(lq\-\(rq commands below may seem very confusing at first, but once they're understood, they can facilitate the rapid entry of expressions which add and subtract large numbers of cells and sums of ranges of cells, so read them over carefully several times until you understand them. .\" ---------- .IP \fB"\fP Specify a named buffer for the next yank/delete/pull command. Buffers are named with a single character. Buffers .RB \(lq a \(rq through .RB \(lq z \(rq are general purpose buffers, buffers .RB \(lq 1 \(rq through .RB \(lq 9 \(rq hold the last nine deletions, with buffer .RB \(lq 1 \(rq being the most recent, and buffer .RB \(lq 0 \(rq holds the last cell or range yanked. Buffer .RB \(lq """" \(rq is the default buffer, which holds the last cell or range that was deleted or yanked. .\" ---------- .TP .B x Clear the current cell. Deletes the numeric value, label string, and/or numeric or string expression. You can prefix this command with a count of the number of cells on the current row to clear. The current column is used if column recalculation order is set. Cells cleared with this command may be recalled with any of the .B pull commands (see below). .\" ---------- .TP .BI m x Mark the current cell. .B pname will prompt for a lowercase letter to be used as a mark specifier. Marked cells may be used as the source for the .B copy command, or as the target of a .B ` or .B ' (go to marked cell) command. .\" ---------- .TP .BI c x Copy a marked cell to the current cell, adjusting row and column references in its numeric or string expression, if any. .B pname will prompt for the name of the cell to be copied, which may be a lowercase letter specified previously with the .B m command, a digit 1-9 to reference one of the last nine edited cells (0 will reference the last cell in which an edit was begun, regardless of whether the edit was completed or not), or .RB \(lq . \(rq to reference the current cell, which, as a special case, is to be used as a source rather than a destination, and is to be copied into a range which includes the current cell. When .RB \(lq . \(rq is specified, the current cell is set as the default source range for the range copy .RB ( rc ) command, and then the .B copy command is entered into the command line and .B pname switches to navigate mode. Moving the cell cursor will then highlight the destination range. After the desired range is highlighted, press to execute the copy. .\" ---------- .TP .B + If not in numeric mode, add the current numeric argument (default 1) to the value of the current cell. The current value of the cell must not be an expression. In numeric mode, .B + switches to insert mode and appends a \(lq+\(rq to the current expression or value, if any, which makes it easy to add to existing data. In navigate mode, .B + inserts the current cell address into the line, followed by another .BR + , and .B pname remains in navigate mode, unless a range is highlighted. If a range is highlighted and the character immediately preceding the cursor is a \(lq+\(rq or \(lq\-\(rq, or the cursor is at the beginning of an empty \(lqlet\(rq expression, the string \(lq@sum(\(rq will be inserted, followed by the highlighted range, followed by \(lq)+\(rq. If a range is highlighted and the character immediately preceding the cursor is not a \(lq+\(rq or \(lq\-\(rq, and the cursor is not at the beginning of an empty \(lqlet\(rq expression, the highlighted range will be inserted, followed by \(lq)+\(rq. .\" ---------- .TP .B \- If not in numeric mode, subtract the current numeric argument (default 1) from the value of the current cell. The current value of the cell must not be an expression. In numeric mode, .B \- switches to insert mode and appends a \(lq\-\(rq to the current expression or value, if any, which makes it easy to subtract from existing data. In navigate mode, .B \- inserts the current cell address into the line, followed by another .BR \- , and .B pname remains in navigate mode, unless a range is highlighted. If a range if highlighted and the character immediately preceding the cursor is a \(lq+\(rq or \(lq\-\(rq, or the cursor is at the beginning of an empty \(lqlet\(rq expression, the string \(lq@sum(\(rq will be inserted, followed by the highlighted range, followed by \(lq)\-\(rq. If a range is highlighted and the character immediately preceding the cursor is not a \(lq+\(rq or \(lq\-\(rq, and the cursor is not at the beginning of an empty \(lqlet\(rq expression, the highlighted range will be inserted, followed by \(lq)\-\(rq. .\" ---------- .TP If you are not editing a cell (top line is empty), pressing will make .B pname enter insert mode. At this point you may type any valid command or press once to edit. .\" ========== .SS "File Commands" .\" ---------- \0 \" exactly one blank line (hard to get) .PD 0 .TP .B \AGetG Get a new database from a file. If encryption is enabled, the file is decrypted before it is loaded into the spreadsheet. .PD .\" ---------- .TP .B \APutP Put the current database into a file. If encryption is enabled, the file is encrypted before it is saved. .\" ---------- .TP .B ZZ Save the current database into a file if it has been modified, and then quit. This is like the .B P command followed by the .B q command, except that the default filename will be used instead of prompting you for one, and the file will only be saved if it was modified. If there is no default filename, an error message will be displayed, and no action taken. .\" ---------- .TP .B W \AWriteWrite a listing of the current database into a file in a form that matches its appearance on the screen. This differs from the .B Put command in that its files are intended to be reloaded with .BR Get , while .B Write produces a file for people to look at. Hidden rows or columns are not shown when the data is printed. .\" ---------- .TP .B T Write a listing of the current database to a file, but include delimiters suitable for processing by the .BR tbl , .BR LaTeX , or .B TeX table processors. The delimiters are controlled by the .B tblstyle option. See .B Set above. The delimiters are a colon\ (:) for style .B 0 or .B tbl and an ampersand\ (&) for style .B latex or .BR tex . .\" ---------- .PP With the .BR Put , .BR Write , and .B Table commands, the optional range argument writes a subset of the spreadsheet to the output file. .\" ---------- .PP With the .B Write and .B Table commands, if you try to write to the last file used with the .B Get or .B Put commands, or the file specified on the command line when .B pname was invoked, you are asked to confirm that the (potentially) dangerous operation is really what you want. .\" ---------- .PP The three output commands, .BR Put , .BR Write , and .BR Table , can pipe their (unencrypted only) output to a program. To use this feature, enter \(lq| program\(rq to the prompt asking for a filename. For example, to redirect the output of the .B Write command to the printer, you might enter \(lq| lpr \-p\(rq. .\" ---------- .TP .B M Merge the database from the named file into the current database. Values and expressions defined in the named file are read into the current spreadsheet overwriting the existing entries at matching cell locations. .\" ---------- .TP .B R Run macros. There are two different kinds of macros that can be used with .BR pname : simple macros, which are stored in plain text files, and advanced macros, which are executable files, and which can be written in the language of your choice. Advanced macros are only available on systems that support pipes. Simple macros are interpreted by .BR pname 's internal parser, and use the same commands used to enter data and perform other operations (the single key commands are shortcuts which switch to input mode after first entering the beginning of the full command for you). These are also the same commands found in .B pname files created with the Put command. Since .B pname files are saved as ASCII files, it is possible to use them as primitive macro definition files. The .B Run command makes this easier. It's like the .B Merge command, but prints a saved path name as the start of the filename to merge in. The string to use is set with the .B Define command. To write macros, you must be familiar with the file format written by the .B Put command. Advanced macros use executable files that are started by .B pname as a child process with stdin and stdout redirected back to .B pname for bidirectional communication. Special commands are available for requesting information such as cell contents, formatting information, or the current location of the cell cursor. Commands are written to stdout, and responses are read from stdin. To use advanced macros, the filename must be preceded by a | (the pipe symbol), and the file must be executable. If the pathname set with the .B Define command begins with a |, all files in that path will be executed as advanced macros. It is also possible to include a filename as part of the path when using advanced macros, which allows you to put multiple macros in a single file, and use the .B Run command to add command line arguments or options to determine which macro should be run. Advanced macros are relatively new, and documentation is still incomplete. This feature will probably be enhanced in future releases. .\" ---------- .TP .B A Specify a macro to be automatically run whenever the current sheet is reloaded from a file. .\" ---------- .TP .B D Define a path for the .B Run command to use (see above). .\" ---------- .PP All file operations take a filename as the first argument to the prompt on the top line. The prompt supplies a " to aid in typing in the filename. The filename can also be obtained from a cell's label string or string expression. In this case, delete the leading " with the backspace key and enter a cell name such as .B a22 instead. If the resulting string starts with \(lq|\(rq, the rest of the string is interpreted as a .SM UNIX command, as above. .\" ========== .SS "Row and Column Commands" .\" ---------- These are two-letter commands which can be used on either rows or columns. The exceptions are the .B f command, which only works on columns, and therefore doesn't require a second letter, and the .B p command which, in addition to operating on rows or columns, has several other options for merging the data in directly, without opening up a new row or column. There are also a few special cases where pressing the same letter twice will affect only the current cell instead of a row or column (except for .BR ZZ , which is a special case all its own). .PP In all of the remaining cases, the second letter of the command will be either .B r or .BR c , depending on whether the operation should be performed on rows or columns, respectively (additional options for the .B p command and the double letter cases are listed below). A small menu lists the choices for the second letter when you type the first letter of one of these commands. .PP Alternatively, you may define a range of rows or columns by moving the cell cursor, either a cell at a time, or by pages (roughly 1/2 screen, unless the .B pagesize option has been set), but this only works for the .BR d , .BR y , and .B Z commands. Vertical cursor movement will begin highlighting rows, and horizontal movement will highlight columns. Pressing the RETURN key will then perform the chosen operation on the specified rows/columns. .PP Commands which copy cells also modify the row and column references in affected cell expressions. The references may be frozen by using the .B @fixed operator or using the .B $ character in the reference to the cell (see below). Commands which create new rows or columns will include all newly created cells in the same ranges (named, framed, color, or those used in expressions) as their counterparts in the current row or column. This can sometimes be a significant factor when deciding whether to use .BR ir / ic " or " or / oc . .\" ---------- .TP .B "ir, ic" Insert a new row (column) by moving the row (column) containing the cell cursor, and all following rows (columns), down (right) one row (column). The new row (column) is empty. Inserting rows while the cell cursor is in a framed range will only effect rows in that range, leaving all rows to the left and right untouched. .\" ---------- .TP .B "or, oc" Open a new row (column). These commands work like the .B ir and .B ic commands, except that the new row (column) will be inserted .B after the current row (column) instead of before it. .\" ---------- .TP .B "ar, ac" Append a new row (column) immediately following the current row (column). It is initialized as a copy of the current one. Appending rows while the cell cursor is in a framed range will only effect rows in that range, leaving all rows to the left and right untouched. .\" ---------- .TP .B "dr, dc, dd" Delete the current row (column). .B dd deletes the current cell (i.e., it is a synonym for .BR x ). Deleting rows while the cell cursor is in a framed range will only effect rows in that range, leaving all rows to the left and right untouched. .\" ---------- .TP .B "yr, yc, yy" Yank a copy of the current row (column) into the delete buffer without actually deleting it. .B yy yanks the current cell (similar to .BR x , but without actually deleting the contents of the cell). Yanking rows while the cell cursor is in a framed range will only copy the portion of each row contained in that range, while ignoring everything outside the range. .\" ---------- .TP .B "pr, pc, pp, pm, px, pt, pC, p." Pull deleted rows/columns/cells back into the spreadsheet. The last set of cells that was deleted or yanked is put back into the spreadsheet at the current location. .B pr inserts enough rows to hold the data. .B pc inserts enough columns to hold the data. .B pp (paste) does not insert rows or columns; it overwrites the cells beginning at the current cell cursor location. .B pm (merge) merges the cells in at the current cell cursor location, but does not erase the destination range first like .BR pp . The difference between .B pp and .B pm is similar to the difference between the .B Get and .B Merge commands. .B pf (format) works like .B pm except that only cell formatting information is merged in, leaving the actual data untouched. This makes it easy to copy cell formats from one part of the spreadsheet to another, such as when expanding an existing spreadsheet file. .B px (exchange) copies the contents of the delete buffer into the range beginning at the current cell cursor location, while simultaneously copying the contents of this range back into the delete buffer, replacing its current contents. .B pt (transpose) overwrites the cells beginning at the current cell cursor location like .BR pp , but transposes rows for columns and vice versa. .B pC (copy) works like .BR pp , except that all cell references are adjusted in the same way that they are for the .B copy command. .B p. is the same as .BR pC , except that it switches to navigate mode and allows you to define the destination range to be used. This works like the .B copy command in that if the source range (the contents of the delete buffer) is a single row, column, or cell, multiple copies may be made. .\" ---------- .TP .B "vr, vc, vv" Remove expressions from the affected rows (columns), leaving only the values which were in the cells before the command was executed. When used in a framed range, .B vr only affects the portion of the the row inside the range, leaving the rest of the row unchanged. .B vv only affects the contents of the current cell. .\" ---------- .TP .B "Zr, Zc, ZZ" Hide (\(lqzap\(rq) the current row (column). This keeps a row (column) from being displayed but keeps it in the data base. The status of the rows and columns is saved with the data base so hidden rows and columns will still be hidden when you reload the spreadsheet. Hidden rows or columns are not printed by the .B W command. The .B ZZ command is a special case. It does not hide anything. Instead, the file will be saved, if modified, and .B pname will exit. See .B ZZ above, under .BR "File Commands" . .\" ---------- .TP .B "sr, sc" Show hidden rows (columns). Enter a range of rows (columns) to be revealed. The default is the first range of rows (columns) currently hidden. This command ignores the repeat count, if any. .\" ---------- .TP .B \AFCmdf Set the output format to be used for printing the numeric values in each cell in the current column. This command has only a column version (no second letter). You may change the column width by pressing the .BR h , .BR < , or cursor left key to reduce it, or the .BR l , .BR > , or cursor right key to increase it. Likewise, you may change the precision (the number of digits to follow decimal points) by pressing the .BR j , .BR \- , or cursor down key to reduce it, or the .BR k , .BR + , or cursor up key to increase it. You may also change the format type for the column by pressing any digit. If the .B f command is preceded by a numeric argument, that argument will determine how many columns should be changed, beginning with the current column, and in the case of incrementing or decrementing the width or precision of the columns, each column will be incremented or decremented separately, regardless of its initial values. Several formatting operations may be performed in sequence. To leave the formatting command, simply press , .BR ^G , .BR q , or . .IP Alternatively, you may press to get the .B format command in the top line and enter all three values directly. In order, these are: the total width in characters of the column, the precision, and the format type. Format types are 0 for fixed point, 1 for scientific notation, 2 for engineering notation, 3 for dates with a two digit year, and 4 for dates with a four digit year. Values are rounded off to the least significant digit displayed. The total column width affects displays of strings as well as numbers. A preceding count can be used to affect more than one column. .IP You can also create your own format types by pressing .B = after the .B f command, followed by any digit (see the .B F command above under .B "Cell Entry and Editing Commands" for a description of how to build a format string). Format numbers 0 through 4 will supersede the built-in format types, while numbers 5 through 9 will supplement them. User defined format types may be used in the same way as the built-in types. For example, the command .RS .IP .B "format 5 = ""#,0.& ;(#,0.&)""" .RE .IP will define a currency format which may then be assigned to column C, for example, with the command .RS .IP .B format C 10 2 5 .RE .\" ---------- .TP .B "@myrow, @mycol" Are functions that return the row or column of the current cell respectively. ex: The cell directly above a cell in the D column could then be accessed by @nval("d",@myrow-1). NOTE: @myrow and @mycol can't be used in specifying ranges. .\" ---------- .TP .B "@lastrow, @lastcol" These return the last row and column of the spreadsheet, respectively. They are useful for macros designed to default to the whole spreadsheet. .\" ========== .SS "Range Commands" .\" ---------- Range operations affect a rectangular region on the screen defined by the upper left and lower right cells in the region. All of the commands in this class begin with \(lqr\(rq; the second letter of the command indicates which command. A small menu lists the choices for the second letter when you type \(lqr\(rq. .B pname prompts for needed parameters for each command. Phrases surrounded by square brackets in the prompt are informational only and may be erased with the backspace key. .\" ---------- .PP Prompts requesting variable names may be satisfied with either an explicit variable name, such as .BR A10 , or with a variable name previously defined in a .B rd command (see below). Range name prompts require either an explicit range such as .BR A10:B20 , or a range name previously defined with a .B rd command. A default range shown in the second line is used if you omit the range from the command or press the key (see below). The default range can be changed by moving the cell cursor via the control commands .RB ( ^P " or " ^N ) or the arrow keys. The cells in the default range are highlighted (using the terminal's standout mode, if available). .\" ---------- .TP .B rx Clear a range. Cells cleared with this command will be saved in the delete buffer, and may be recalled with any of the .B pull commands. .\" ---------- .TP .B ry Yank a range. Like .BR rx , cells yanked with this command will be saved in the delete buffer, and may be recalled with any of the .B pull commands. This command differs from .BR rx , however, in that the original cells will not be cleared. Although this command may be used to copy a range of cells, it treats all references as fixed. Use .B rc if you want references to be relative to the cell which contains them unless specified otherwise, either with the .B @fixed operator or using the .B $ character in the reference to the cell. .\" ---------- .TP .B rc Copy a source range to a destination range. The source and destination may be different sizes. The result is always one or more full copies of the source. Copying a row to a row yields a row. Copying a column to a column yields a column. Copying a range to anything yields a range. Copying a row to a column or a column to a row yields a range with as many copies of the source as there are cells in the destination. This command can be used to duplicate a cell through an arbitrary range by making the source a single cell range such as .BR b20:b20 . If the source range is omitted (second argument), the source range from the last .B copy command will be used, unless a range is currently highlighted, in which case the highlighted range will be copied instead. If both the source range and destination range are omitted, the current cell will be used as the destination, unless a range is currently highlighted, in which case the highlighted range will serve as the destination, and the source range from the last .B copy command will be copied into that destination. .\" ---------- .TP .B rm Move a source range to a destination range. This differs from deleting a range with .B rx and pulling it back in with .B pm in that any expressions that reference a cell in the range to be moved will reference the cell at its new address after the move. Unlike the .B rc command, the destination of a move is a single cell, which will be the upper lefthand corner of the source range after the move. .\" ---------- .TP .B rv Values only. This command removes the expressions from a range of cells, leaving just the values of the expressions. .\" ---------- .TP .B rs Sort a range. The rows in the specified range will be sorted according to criteria given in the form of a string of characters. This string, enclosed in double quotes, may comprise a single criterion or multiple criteria in decreasing order of precedence. Each criterion has three parts, all of which are mandatory. The first part is a single character, which must be either .B + or .BR \- , which specifies whether the sort should be done in ascending or descending order, respectively. The second part, which is also a single character, must be either .B # or .BR $ , and is used to specify whether the sort should be based on the numeric portion or the string portion, respectively, of the cells being used for the comparison. The third part may be either one or two characters, and must be alphabetic (case insensitive), and specifies the column to be used when making the comparisons. This column must be in the range being sorted. Any number of criteria may be concatenated, and will be used in the order specified. If no criteria are specified, the default behavior is to sort in ascending order, first by string and then by number, using the leftmost column of the range being sorted. This is equivalent to specifying the sort criteria to be .RB """ +$a+#a """, where both .BR a 's are replaced by the name of the leftmost column of the range being sorted. .\" ---------- .TP .B \ArfCmdrf Fill a range with constant values starting with a given value and increasing by a given increment. Each row is filled before moving on to the next row if row order recalculation is set. Column order fills each column in the range before moving on to the next column. The start and increment numbers may be positive or negative. To fill all cells with the same value, give an increment of zero. .\" ---------- .TP .B r{ Left justify all strings in the specified range. .\" ---------- .TP .B r} Right justify all strings in the specified range. .\" ---------- .TP .B r| Center all strings in the specified range. .\" ---------- .TP .B rd Use this command to assign a symbolic name to a single cell or a rectangular range of cells on the screen. The parameters are the name, surrounded by "", and either a single cell name such as .B A10 or a range such as .BR a1:b20 . Names defined in this fashion are used by the program in future prompts, may be entered in response to prompts requesting a cell or range name, and are saved when the spreadsheet is saved with the .B Put command. Names defined may be any combination of alphanumeric characters and .RB \(oq _ \(cq as long as the name isn't a valid cell address. Thus, .BR x , .BR H2SO4 , and .B 3rdDay are all valid names, but .B H2 is not. .\" ---------- .TP .B rl Use this command to lock the current cell or a range of cells, i.e. make them immune to any type of editing. A locked cell can't be changed in any way until it is unlocked. .\" ---------- .TP .B rU This command is the opposite of the .B rl command and thus unlocks a locked cell and makes it editable. .\" ---------- .TP .B rS This command shows lists of the currently defined range names, framed ranges, and color definitions and ranges, one after the other. The output of this command will be piped to .BR less . If the environment variable PAGER is set, its value is used in place of .BR less . .\" ---------- .TP .B ru Use this command to undefine a previously defined range name. .\" ---------- .TP .B rF Use this command to assign a value format string (see the \(lqF\(rq cell entry command) to a range of cells. .\" ---------- .TP .B \AFramedRangesrr This command is used for creating, modifying, and deleting framed ranges. A framed range, is one which has a number of rows or columns specified at the top, bottom, left, and/or right (the frame) which must remain onscreen whenever the cell cursor is within that range. In other words, a frame consists of an outer range and an inner range, where the inner range is allowed to scroll within the outer range. Once a frame is defined, the inner range may be resized, but the outer range remains fixed unless rows or columns are added or deleted within the range. When this command is invoked, you will be prompted for the type of frame-related action you would like to perform. You may select an option from the list by typing its first letter. The options are .BR top , .BR bottom , .BR left , .BR right , .BR all , and .BR unframe . If you choose .BR top , .BR bottom , .BR left , or .BR right , you will be prompted for a range and number of rows/columns. The range may be omitted if the cell cursor is in a previously defined framed range, in which case that range's outer range will be used instead. The number of rows/columns will set or adjust the width of the corresponding side of the frame. If all of these widths are set to zero, the frame will be undefined (same as the unframe command). If you choose .BR all , you will be prompted for an outer range and an inner range, in which case the inner range will scroll within the outer range, and any rows or columns outside of the inner range, but inside the outer range will be part of the \(lqframe\(rq that is to remain onscreen. The outer range may be omitted if the cell cursor is in a previously defined framed range, in which case the previously defined outer range will be used. However, if a single range is specified on the command line, while another range wholly contained within this range is highlighted, the specified range will be used as the outer range, and the highlighted range will be used as the inner range. If no range is specified on the command line, but a range is highlighted, and the highlighted range is wholly contained within a previously defined framed range, the highlighted range will be used as the inner range, and the previously defined outer range will be used as the outer range. If you choose .BR unframe , you will be prompted for a range, and if the range is found in the list of frames, the frame will be deleted, and the framing will no longer be active (the specified range must be the outer range of the previously defined frame to be deleted). The range may be omitted if the cell cursor is in a previously defined framed range, in which case that range will be used by default. Framed ranges may not be nested or overlapping. If you try to define a range that contains any cells in common with a previously defined framed range, an error message will be issued, and the frame will not be created. .\" ---------- .TP .B rC This command defines a color range, and specifies a foreground/background pair to be used for that range. See "Color Commands" below for more information. .\" ========== .SS "Note Commands" .\" ---------- A note is a cell or range of cells that can be jumped to quickly from another cell by creating a special link in that cell. The note may contain text explaining the contents of the cell containing the link, similar to a footnote, or it may simply be another part of the spreadsheet that is related to the cell in some way. When you press the \(oqn\(cq key, you will get a short prompt asking you whether you want to add or delete a note, or to \(lqshow\(rq (by highlighting) which cells on the screen have attached notes. .PP If a cell with an attached note contains numeric data, it will be preceded with an \(lq*\(rq. If color is available and turned on, the \(lq*\(rq will be displayed with color 4. Also, the note address will be displayed in curly braces on the top line, preceded by an \(lq*\(rq, when the cell is current (e.g. .B {*AC30:AE43} or .B {*note1} for a named range). You may also use the .B *s (Note/Show) command to highlight all cells on the current screen with attached notes. .TP .B *a Add a note. This will bring up the addnote command in the top line, followed by the target address of the cell where you want the note added. You must then enter the cell or range where the note resides to add the note. If you omit the note address or range, the currently highlighted range, if any, will be used. Otherwise, the current cell will be used (you would, of course, want to move away from the cell in which the addnote command was invoked in the latter case). .\" ---------- .TP .B *d Delete a note. If there is a note attached to the current cell, the link will be removed (deleted). The note itself will not be removed from the spreadsheet. If it is no longer needed, it must be deleted in a separate step. .\" ---------- .TP .B *s Show all notes on the current screen. If there are any cells on the visible portion of the spreadsheet which contain attached notes, they will be highlighted until the next screen change, no matter how minor. Simply moving to a new cell will be enough to turn off the highlighting. .\" ---------- .TP .B ** Jump to a note. If there is a note attached to the current cell, you will be immediately transported to that cell. You may return from the note to where you were by pressing ` twice. .\" ========== .SS "Color Commands" .\" ---------- Color may be enabled by setting the color option (\(lqset color\(rq), or by toggling it with ^TC (control-T followed by an uppercase C). If color is enabled, you may define up to eight color pairs, each consisting of a foreground color and a background color. Each of these colors may be defined by an expression which is evaluated at the same time the rest of the spreadsheet is evaluated. Color expressions may be simple, specifying only a foreground color and a background color, or they may be arbitrarily complex, causing the colors to change based upon other data in the spreadsheet, for example. Color ranges may then be defined using the .B rC command, with a color number (1-8) assigned to the range (see below). .PP Some of the color numbers may have special meaning under certain circumstances, but may also be used explicitly at the same time. For example, color 1 is the default color pair if color is enabled but no color has been defined for a given cell. It is also the color used for the column and row labels and the top two lines of the display, which are used for prompts, input, error messages, etc. Color 2, while not explicitly used for all negative numbers, will be used for negative numbers in cells which have no other color defined when colorneg is turned on (\(lqset colorneg\(rq or ^TN). This is because colorneg causes all cells with negative numbers to have their color number incremented by one (cycling back to color 1 if the cell is defined as using color 8). Color 3 is used for all cells with errors (ERROR or INVALID), if colorerr is set (\(lqset colorerr\(rq or ^TE), regardless of which color they have been defined to use, or whether they have been defined to use any color at all. Color 4 is used to highlight the \(lq*\(rq which signifies that a cell has a note attached. .PP If two color ranges are nested or overlapping, any cell that is common to both will be displayed using the color of the most recently defined color range. You can list all color definitions and color ranges with the rS (show) command (see below). .TP .B C This command first prompts you for the color number you would like to define (or redefine). After selecting a number (1-8), you may enter an expression which defines the foreground and background colors. If the chosen color has previously been defined, the old definition will be presented for editing. The syntax of the color command is: .RS .IP .B color .I number = .I expression .RE .IP where .I number is the number of the color pair you want to define, and .I expression is the definition. If the expression is missing, the specified color number will be unset (it will revert to its default start-up colors). Unlike setting it explicitly to its original value, this will not cause the expression to be written to the file when saved. See below for an explanation of the format of a color expression. .\" ---------- .TP .B rC This command defines a color range, and specifies a foreground/background pair to be used for that range. Although this command also uses the .B color command, the syntax is different from that used for defining a color pair. This syntax is: .RS .IP .B color .I range number .RE .\" ---------- .TP .B rS This command shows lists of the currently defined range names, framed ranges, and color definitions and ranges, one after the other. The output of this command will be piped to .BR less . If the environment variable PAGER is set, its value is used in place of .BR less . .PP Color expressions are exactly like any other numeric expression, and may contain any function or operator that is valid in any other numeric expression. There are, however special functions designed specifically for defining colors. These functions are: .IP @black @red @green @yellow @blue @magenta @cyan @white .PP Although these function names are intended to reflect the color they produce, and use the same names as the curses colors, @yellow may appear as brown on many displays, especially those based on the VGA standard. .PP In addition to special functions for specifying colors, there is also a special operator for combining two such colors into a single number which specifies both a foreground and a background color. This operator is the semicolon (;). For example, the command .IP .B color 1 = @white;@green .PP will set the foreground color to white and the background color to green for any cell or range of cells defined to use color 1, or which have no color defined. If the semicolon operator is not used, and only one color is specified, that color will be used for the foreground, and the background will default to black. .PP Although the above example is the easiest way to specify foreground and background colors, and will probably meet most people's needs, .B pname allows much more power and flexibility, should the need arise, due to the fact that any color can be specified by an expression. For example, .IP .B color 5 = B23 Abort entry of the current command. .PD .\" ---------- .TP .B ? Enter an interactive help facility. Lets you look up brief summaries of the main features of the program. The help facility is structured like this manual page so it is easy to find more information on a particular topic, although it may not be completely up-to-date. .\" ---------- .TP .B ! Shell escape. .B pname prompts for a shell command to run. End the command line with the RETURN key. If the environment variable .SM SHELL is defined, that shell is run. If not, /bin/sh is used. Giving a null command line starts the shell in interactive mode. A second \(lq!\(rq repeats the previous command. .\" ---------- .TP .B ~ Abbreviations. You may set abbreviations to speed up the entry of repetitive data. Abbreviations work much like abbreviations in vi, except that when defining an abbreviation, both the abbreviation and the expanded text must be contained within quotes, separated by a single space. If more than one space separates the abbreviation from the expanded text, it will be included as part of the expanded text. There are three types of abbreviations available in sc. In the first type, all characters must be either alphanumeric or \(lq_\(rq. In the second type, the last character must be alphanumeric or \(lq_\(rq, but all other characters must not be alphanumeric or \(lq_\(rq. Neither type may contain spaces. The third type of abbreviation is a single character, and must be alphanumeric or \(lq_\(rq. When using abbreviations, the first type must be at the beginning of the line, or must be preceded by any character which is not alphanumeric or \(lq_\(rq. The second type must be at the beginning of the line, or must be preceded either by an alphanumeric character, \(lq_\(rq, or a space. Single character abbreviations must be at the beginning of the line or preceded by a space. Abbreviations will be automatically expanded as soon as the space bar or return key is pressed, or when pressing the key at the end of the abbreviation to switch to edit mode. You can also force an abbreviation to be expanded by following it with a .BR ^] , which won't be inserted into the line. If you don't want an abbreviation to be expanded, you must either press .B ^V twice or switch to edit mode and back again somewhere within the abbreviation (pressing .B ^V twice also has the effect of switching to navigate mode and back again). If the string in the abbreviation command contains no spaces, the entire string will be looked up in the list of abbreviations, and if found, the definition will be displayed in the form of the original .B abbreviation command used to define it. When looking up an abbreviation in this manner, be sure to disable abbreviation expansion, as described above, or the results may not be what you expect. If the string is empty, a list of all abbreviations and their corresponding expanded text will be output to your pager. Note that abbreviations are not saved with the file. This allows each user to create his own file of abbreviations and either merge them in or include them in his own .scrc file, rather than force all users who access a file to use the same list of abbreviations. .\" ---------- .TP .B ^L Redraw the screen. .\" ---------- .TP .B ^R Redraw the screen with special highlighting of cells to be filled in. This is useful for finding values you need to provide or update in a form with which you aren't familiar or of which you have forgotten the details. It's also useful for checking a form you are creating. All cells which contain constant numeric values (not the result of a numeric expression) are highlighted temporarily, until the next screen change, however minor. To avoid ambiguity, the current range (if any) and current cell are not highlighted. .\" ---------- .TP .B ^X This command is similar to .BR ^R , but highlights cells which have expressions. It also displays the expressions in the highlighted cells as left-flushed strings, instead of the numeric values and/or label strings of those cells. This command makes it easier to check expressions, at least when they fit in their cells or the following cell(s) are blank so the expressions can slop over (like label strings). In the latter case, the slop over is not cleared on the next screen update, so you may want to type .B ^L after the .B ^X in order to clean up the screen. .\" ---------- .TP .B @ Recalculates the spreadsheet. .\" ========== .SS "Variable Names" .\" ---------- Normally, a variable name is just the name of a cell, such as .BR K20 . The value is the numeric or string value of the cell, according to context. .\" ---------- .PP When a cell's expression (formula) is copied to another location via .B copy or .BR range-copy , variable references are by default offset by the amount the formula moved. This allows the new formula to work on new data. If cell references are not to change, you can either use the .B @fixed operator (see below), or one of the following variations on the cell name. .\" ---------- .TP .B K20 References cell .BR K20 ; the reference changes when the formula is copied. .\" ---------- .TP .B $ K $ 20 Always refers to cell .BR K20 ; the reference stays fixed when the formula is copied. .\" ---------- .TP .B $ K20 Keeps the column fixed at column K; the row is free to vary. .\" ---------- .TP .B K $ 20 Similarly, this fixes the row and allows the column to vary. .\" ---------- .PP These conventions also hold on defined ranges. Range references vary when formulas containing them are copied. If the range is defined with fixed variable references, the references do not change. .\" ---------- .TP .B @fixed To make a variable not change automatically when a cell moves, put the word .B @fixed in front of the reference, for example: B1 \(** @fixed C3. .\" ========== .SS "Numeric Expressions" .\" ---------- Numeric expressions used with the \(lq=\(rq and .B e commands have a fairly conventional syntax. Terms may be constants, variable names, parenthesized expressions, and negated terms. Ranges may be operated upon with range functions such as sum .RB ( @sum ()) and average .RB ( @avg ()). Terms may be combined using binary operators. .\" ---------- .TP .BI \- e Negation. .\" ---------- .TP .IB e + e Addition. .\" ---------- .TP .IB e \- e Subtraction. .\" ---------- .TP .IB e \(** e Multiplication. .\" ---------- .TP .IB e / e Division. .\" ---------- .TP .IB e1 % e2 e1 mod e2. .\" ---------- .TP .IB e ^ e Exponentiation. .\" ---------- .TP .IB e < e .PD 0 .TP .IB e <= e .TP .IB e = e .TP .IB e != e .TP .IB e >= e .TP .IB e > e Relationals: true (1) if and only if the indicated relation holds, else false (0). Note that \(lq<=\(rq, \(lq!=\(rq, and \(lq>=\(rq are converted to their \(lq!()\(rq equivalents. .PD .\" ---------- .TP .BI ~ e Boolean operator .SM NOT. .\" ---------- .TP .IB e & e Boolean operator .SM AND. .\" ---------- .TP .IB e | e Boolean operator .SM OR. .\" ---------- .TP .BI @if( e , e , e ) .PD 0 .TP .IB e ? e : e .PD Conditional: If the first expression is true then the value of the second is returned, otherwise the value of the third. .\" ---------- .PP Operator precedence from highest to lowest is: .PP .nf .RS \-, ~, ! ^ \(**, / +, \- <, <=, =, !=, >=, > & | ?: .RE .fi .\" ========== .SS "Built-in Range Functions" .\" ---------- These functions return numeric values. The @sum, @prod, @avg, @count, @max, @min, and @stddev functions may take an optional second argument which is an expression that is to be evaluated for each cell in the specified range to determine which cells to include in the function. Only those cells for which the expression evaluates to true (non-zero) will be used in calculating the value of the function. Before evaluation for each cell, the expression is first converted as if it was being copied from the cell in the upper left-hand corner of the range into the cell under consideration, with all cell references adjusted accordingly. Because the parts of the expression that should remain fixed during the evaluation of the function may not necessarily be the same as those which should remain fixed during an actual copy operation, the rules for adjusting cell references during a copy operation are slightly different than normal. In particular, these rules differ in two different ways. The first difference is that the .B @fixed operator is ignored during a copy operation unless it is enclosed in parentheses. This is so that selected cells whose addresses should remain fixed during any given evaluation of a range function can be adjusted relative to the cell containing the range function when copied (the .B $ prefix is still honored for these cells when copying). Enclosing the .B @fixed operator in parentheses will have the opposite effect. That is, it will cause cell references to be fixed while copying, while allowing them to be adjusted when the function is being evaluated, subject to any .B $ prefixes present. Note that only the .B @fixed operator itself should be enclosed in parentheses for this to work properly. The second difference is that any references in the expression that refer to cells in the range in the first argument of the range function will have any .B $ prefixes ignored, and the references will be treated instead as if they had the same .B $ prefixes as the left side of the range argument. For example, if the left side of the range argument (the cell address on the left side of the colon) has a fixed row, but does not have a fixed column, any cell references that refer to cells in that range will also have a fixed row, but will not have a fixed column. This is so that if the range reference moves when copying, references to any cells in that range will also move accordingly. Note that the test expression will be evaluated once for every cell in the range, which means that excessive use of these functions with the optional test expression, or the use of overly complex test expressions or with very large ranges can greatly slow down the recalculation of a spreadsheet, and may require turning off autocalc for speed, and then manually recalculating with the .B @ command. .\" ---------- .TP 18 .BI @sum( r ) .PD 0 .TP 18 .BI @sum( r , e ) .PD Sum all valid (nonblank) entries in the region whose two corners are defined by the two variable names (e.g. .BR c5:e14 ) or the range name specified. The optional second argument is an expression which can be used to determine which cells in the range to sum (see above). .\" ---------- .TP 18 .BI @prod( r ) .PD 0 .TP 18 .BI @prod( r , e ) .PD Multiply together all valid (nonblank) entries in the specified region. The optional second argument is an expression which can be used to determine which cells in the range to multiply (see above). .\" ---------- .TP 18 .BI @avg( r ) .PD 0 .TP 18 .BI @avg( r , e ) .PD Average all valid (nonblank) entries in the specified region. The optional second argument is an expression which can be used to determine which cells in the range to average (see above). .\" ---------- .TP 18 .BI @count( r ) .PD 0 .TP 18 .BI @count( r , e ) .PD Count all valid (nonblank) entries in the specified region. The optional second argument is an expression which can be used to determine which cells in the range to count (see above). .\" ---------- .TP 18 .BI @max( r ) .PD 0 .TP 18 .BI @max( r , e ) .PD Return the maximum value in the specified region. The optional second argument is an expression which can be used to exclude specific cells in the range when determining this maximum value (see above). See also the multi argument version of .B @max below. .\" ---------- .TP 18 .BI @min( r ) .PD 0 .TP 18 .BI @min( r , e ) .PD Return the minimum value in the specified region. The optional second argument is an expression which can be used to exclude specific cells in the range when determining this minimum value (see above). See also the multi argument version of .B @min below. .\" ---------- .TP 18 .BI @stddev( r ) .PD 0 .TP 18 .BI @stddev( r , e ) .PD Return the sample standard deviation of the cells in the specified region. The optional second argument is an expression which can be used to exclude specific cells in the range when calculating the standard deviation (see above). .\" ---------- .TP 18 .BI @rows( r ) Return the number of rows in the specified range. .\" ---------- .TP 18 .BI @cols( r ) Return the number of columns in the specified range. .\" ---------- .TP 18 .BI @lookup( e , r ) .PD 0 .TP 18 .BI @lookup( r , e ) .PD 0 .TP 18 .BI @lookup( se , r ) .PD 0 .TP 18 .BI @lookup( r , se ) .PD Evaluates the expression then searches through the range .B r for a matching value. The range should be either a single row or a single column. The expression can be either a string expression or a numeric expression. If it is a numeric expression, the range is searched for the the last value less than or equal to .IR e . If the expression is a string expression, the string portions of the cells in the range are searched for an exact string match. The value returned is the numeric value from the next row and the same column as the match, if the range was a single row, or the value from the next column and the same row as the match if the range was a single column. .\" ---------- .TP 18 .BI @hlookup( e , r , n ) .PD 0 .TP 18 .BI @hlookup( r , e , n ) .PD 0 .TP 18 .BI @hlookup( se , r , n ) .PD 0 .TP 18 .BI @hlookup( r , se , n ) .PD Evaluates the expression then searches through the first row in the range .I r for a matching value. The expression can be either a string expression or a numeric expression. If it is a numeric expression, the row is searched for the the last value less than or equal to .IR e . If the expression is a string expression, the string portions of the cells in the row are searched for an exact string match. The value returned is the numeric value from the same column .I n rows below the match. .\" ---------- .TP 18 .BI @vlookup( e , r , n ) .PD 0 .TP 18 .BI @vlookup( r , e , n ) .PD 0 .TP 18 .BI @vlookup( se , r , n ) .PD 0 .TP 18 .BI @vlookup( r , se , n ) .PD Evaluates the expression then searches through the first column in the range .I r for a matching value. The expression can be either a string expression or a numeric expression. If it is a numeric expression, the column is searched for the the last value less than or equal to .IR e . If the expression is a string expression, the string portions of the cells in the column are searched for an exact string match. The value returned is the numeric value from the same row .I n columns to the right of the match. .\" ---------- .TP 18 .BI @index( e1 , r ) .PD 0 .TP 18 .BI @index( r , e1 ) .PD 0 .TP 18 .BI @index( r , e1 , e2 ) .PD Use the values of expressions .I e1 and (optionally) .I e2 to index into the range .IR r . The numeric value at that position is returned. With two arguments, the range should be either a single row or a single column. An expression with the value 1 selects the first item in the range, 2 selects the second item, etc. With three arguments, the range must come first, and the second and third arguments will then be interpreted as row and column, respectively, for indexing into a two-dimensional table. .\" ---------- .TP 18 .BI @stindex( e1 , r ) .PD 0 .TP 18 .BI @stindex( r , e1 ) .PD 0 .TP 18 .BI @stindex( r , e1 , e2 ) .PD Use the values of expressions .I e1 and (optionally) .I e2 to index into the range .IR r . The string value at that position is returned. With two arguments, the range should be either a single row or a single column. An expression with the value 1 selects the first item in the range, 2 selects the second item, etc. With three arguments, the range must come first, and the second and third arguments will then be interpreted as row and column, respectively, for indexing into a two-dimensional table. .\" ========== .SS "Built-in Numeric Functions" .\" ---------- All of these functions operate on floating point numbers (doubles) and return numeric values. Most of them are standard system functions more fully described in .BR math (3). The trig functions operate with angles in radians. .\" ---------- .TP 18 .BI @sqrt( e ) Return the square root of .IR e . .\" ---------- .TP 18 .BI @exp( e ) Return the exponential function of .IR e . .\" ---------- .TP 18 .BI @ln( e ) Return the natural logarithm of .IR e . .\" ---------- .TP 18 .BI @log( e ) Return the base 10 logarithm of .IR e . .\" ---------- .TP 18 .BI @floor( e ) Return the largest integer not greater than .IR e . .\" ---------- .TP 18 .BI @ceil( e ) Return the smallest integer not less than .IR e . .\" ---------- .TP 18 .BI @rnd( e ) Round .I e to the nearest integer. default: *.5 will be rounded up to the next integer; doing a 'set rndtoeven' will cause it to be rounded to the closest even number instead (aka banker's round). Round-to-even has advantages over the default rounding for some applications. For example, if X+Y is an integer, then X+Y = rnd(X)+rnd(Y) with round-to-even, but not always with the defaulting rounding method. This could be an advantage, for example, when trying to split an odd amount of money evenly between two people (it would determine who gets the extra penny). .TP 18 .BI @round( e , n ) Round .I e to .I n decimal places. .I n may be positive to round off the right side of the decimal or negative to round off the left side. See @rnd(e) above for rounding types. .\" ---------- .TP 18 .BI @abs( e ) .PD 0 .TP 18 .BI @fabs( e ) Return the absolute value of .IR e . .PD .\" ---------- .TP 18 .BI @pow( e1 , e2 ) Return .I e1 raised to the power of .IR e2 . .\" ---------- .TP 18 .BI @hypot( e1 , e2 ) Return sqrt(e1\(**e1+e2\(**e2), taking precautions against unwarranted overflows. .\" ---------- .TP 18 .B @pi A constant quite close to pi. .\" ---------- .TP 18 .BI @dtr( e ) Convert .I e in degrees to radians. .\" ---------- .TP 18 .BI @rtd( e ) Convert .I e in radians to degrees. .\" ---------- .TP 18 .BI @sin( e ) .PD 0 .TP 18 .BI @cos( e ) .TP 18 .BI @tan( e ) Return trigonometric functions of radian arguments. The magnitude of the arguments are not checked to assure meaningful results. .PD .\" ---------- .TP 18 .BI @asin( e ) Return the arc sine of .I e in the range \-pi/2 to pi/2. .\" ---------- .TP 18 .BI @acos( e ) Return the arc cosine of .I e in the range 0 to pi. .\" ---------- .TP 18 .BI @atan( e ) Return the arc tangent of .I e in the range \-pi/2 to pi/2. .\" ---------- .TP 18 .BI @atan2( e1 , e2 ) Returns the arc tangent of .IR e1 / e2 in the range \-pi to pi. .\" ---------- .TP 18 .BI @max( e1 , e2 , ... ) Return the maximum of the values of the expressions. Two or more expressions may be specified. See also the range version of .B @max above. .\" ---------- .TP 18 .BI @min( e1 , e2 , ... ) Return the minimum of the values of the expressions. Two or more expressions may be specified. See also the range version of .B @min above. .\" ---------- .TP 18 .BI @ston( se ) Convert string expression .I se to a numeric value. .\" ---------- .TP 18 .BI @eqs( se1 , se2 ) Return 1 if string expression .I se1 has the same value as string expression .IR se2 , 0 otherwise. .\" ---------- .TP 18 .BI @nval( se , e ) Return the numeric value of a cell selected by name. String expression .I se must evaluate to a column name (\(lqA\(rq-\(lqAE\(rq) and .I e must evaluate to a row number (0-199). If .I se or .I e is out of bounds, or the cell has no numeric value, the result is 0. You can use this for simple table lookups. Be sure the table doesn't move unexpectedly! See also .BR @sval () below. .\" ---------- .TP 18 .B @err Force an error. This will force the expression which contains it to result in an error. .\" ========== .SS "String Expressions" .\" ---------- String expressions are made up of constant strings (characters surrounded by double quotation marks), variables (cell names, which refer to the cells's label strings or expressions), and string functions. Note that string expressions are only allowed when entering a cell's label string, not its numeric part. Also note that string expression results may be left or right flushed or centered, according to the type of the cell's string label. .\" ---------- .TP .B # Concatenate strings. For example, the string expression .IP "" A0 # "zy dog" .IP "" displays the string \(lqthe lazy dog\(rq in the cell if the value of .BR A0 's string is \(lqthe la\(rq. .\" ========== .SS "Built-in String Functions" .\" ---------- \0 \" exactly one blank line (hard to get) .PD 0 .TP 18 .BI @filename( e ) Return the current default filename, as specified when the file was first loaded or created, or during the last save, with the .B Put command. If .I e is 0, only the actual filename will be returned, with any path removed. If non-zero, the full path specified on the command line or in the last .B Get or .B Put command will be returned. If the path begins with \(lq~\(rq, it will be expanded to the appropriate users home directory. .PD .\" ---------- .TP 18 .BI @substr( se , e1 , e2 ) Extract and return from string expression .I se the substring indexed by character number .I e1 through character number .I e2 (defaults to the size of .I se if beyond the end of it). If .I e1 is less than 1 or greater than .IR e2 , the result is the null string. For example, .IP "" @substr ("Nice jacket", 4, 8) .IP "" returns the string \(lqe jac\(rq. .\" ---------- .TP 18 .BI @fmt( se , e ) Convert a number to a string. The argument .I se must be a valid .BR printf (3) format string. .I e is converted according to the standard rules. For example, the expression .IP "" @fmt ("\(**\(**%6.3f\(**\(**", 10.5) .IP "" yields the string \(lq\(**\(**10.500\(**\(**\(rq. .I e is a double, so applicable formats are e, E, f, g, and G. Try \(lq%g\(rq as a starting point. .\" ---------- .TP 18 .BI @sval( se , e ) Return the string value of a cell selected by name. String expression .I se must evaluate to a column name (\(lqA\(rq-\(lqAE\(rq) and .I e must evaluate to a row number (0-199). If .I se or .I e is out of bounds, or the cell has no string value, the result is the null string. You can use this for simple table lookups. Be sure the table doesn't move unexpectedly! .\" ---------- .TP 18 .BI @upper( se ) .PD 0 .TP 18 .BI @lower( se ) will case the string expression to upper or lower. .PD .\" ---------- .TP 18 .BI @capital( se ) will convert the first letter of words in a string into upper case and other letters to lower case (the latter if all letters of the string are upper case). .\" ---------- .TP 18 .BI \A@ext@ext( se , e ) Call an external function (program or script). The purpose is to allow arbitrary functions on values, e.g. table lookups and interpolations. String expression .I se is a command or command line to call with .BR popen (3). The value of .I e is converted to a string and appended to the command line as an argument. The result of .BR @ext () is a string: the first line printed to standard output by the command. The command should emit exactly one output line. Additional output, or output to standard error, messes up the screen. .BR @ext () returns a null string and prints an appropriate warning if external functions are disabled, .I se is null, or the attempt to run the command fails. .IP "" External functions can be slow to run, and if enabled are called at each screen update, so they are disabled by default. You can enable them with .B ^T when you really want them called. .IP "" A simple example: .IP "" @ext ("echo", a1) .IP "" You can use .BR @ston () to convert the .BR @ext () result back to a number. For example: .IP "" @ston (@ext ("form.sc.ext", a9 + b9)) .IP "" Note that you can build a command line (including more argument values) from a string expression with concatenation. You can also "hide" the second argument by ending the command line (first argument) with \(lq #\(rq (shell comment). .\" ---------- .TP 18 .BI @coltoa( e ) Returns a string name for a column from the numeric argument. For example: .IP "" @coltoa(@mycol\-1) @nval(coltoa(@mycol\-1), @myrow+1) .IP "" .\" ========== .SS "Built-in Financial Functions" .\" ---------- Financial functions compute the mortgage (or loan) payment, future value, and the present value functions. Each accepts three arguments, an amount, a rate of interest (per period), and the number of periods. These functions are the same as those commonly found in other spreadsheets and financial calculators .\" ---------- .TP 18 .BI @pmt( e1 , e2 , e3 ) .B @pmt(60000,.01,360) computes the monthly payments for a $60000 mortgage at 12% annual interest (.01 per month) for 30 years (360 months). .\" ---------- .TP 18 .BI @fv( e1 , e2 , e3 ) .B @fv(100,.005,36) computes the future value for 36 monthly payments of $100 at 6% interest (.005 per month). It answers the question: "How much will I have in 36 months if I deposit $100 per month in a savings account paying 6% interest compounded monthly?" .\" ---------- .TP 18 .BI @pv( e1 , e2 , e3 ) .B @pv(1000,.015,36) computes the present value of an ordinary annuity of 36 monthly payments of $1000 at 18% annual interest. It answers the question: "How much can I borrow at 18% for 3 years if I pay $1000 per month?" .\" ========== .SS "Built-in Date and Time Functions" .\" ---------- Time for .B pname follows the system standard: the number of seconds since the beginning of 1970. All date and time functions except .BR @date () return numbers, not strings. .\" ---------- .TP 18 .B @now Return the current time encoded as the number of seconds since the beginning of the epoch (December 31, 1969, midnight, GMT). .\" ---------- .TP 18 .BI @dts( e1 , e2 , e3 ) Convert a date to the number of seconds from the epoch to the first second of the specified date, local time. Dates may be specified in either (m,d,y) or (y,m,d) format, although the latter is preferred, since it's more universally recognized (m,d,y is only used in America). If e2 > 12 or e3 > 31, then (m,d,y) is assumed. Otherwise, (y,m,d) is assumed. For example, .B @date(@dts(1976,12,14)) yields .IP "" .B " Tue Dec 14 00:00:00 1976" .IP "" The month should range from 1 to 12; the day should range from 1 to the number of days in the specified month; and the year should include the century (e.g. 1999 instead of 99). Any date capable of being handled by the system is valid, typically 14 Dec 1901 to 18 Jan 2038 on a system that uses a 32 bit time_t. Invalid dates or dates outside of this range will return ERROR. For rapid entry of dates using only the numeric keypad, .B pname provides the alternate syntax .IR y . m . d or .IR m . d . y , which is automatically converted to the @dts(...) format above. The year, month, and day must be entered numerically in the alternate syntax; formulas are not allowed. .\" ---------- .TP 18 .BI @tts( e1 , e2 , e3 ) .B @tts(8,20,45) converts the time 8:40:45 to the number of seconds since midnight, the night before. The hour should range from 0 to 23; the minutes and seconds should range from 0 to 59. .\" ---------- .PP The following functions take the time in seconds (e.g. from .BR @now ) as an argument and return the specified value. The functions all convert from GMT to local time. .\" ---------- .TP 18 .BI @date( e ) .PD 0 .TP 18 .BI @date( e , se ) .PD Convert the time in seconds to a date string. With a single numeric argument, the date will be 24 characters long in the following form: .IP "" .B " Sun Sep 16 01:03:52 1973" .IP "" Note that you can extract parts of this fixed-format string with .BR @substr (). A format string compatible with the strftime() function may optionally be given as a second argument to override the default format. See the strftime(3) man page for details. .\" ---------- .TP 18 .BI @year( e ) Return the year. Valid years begin with 1970, although many systems will return years prior to 1970 if .I e is negative. The last legal year is system dependent. .\" ---------- .TP 18 .BI @month( e ) Return the month, encoded as 1 (January) to 12 (December). .\" ---------- .TP 18 .BI @day( e ) Return the day of the month, encoded as 1 to 31. .\" ---------- .TP 18 .BI @hour( e ) Return the number of hours since midnight, encoded as 0 to 23. .\" ---------- .TP 18 .BI @minute( e ) Return the number of minutes since the last full hour, encoded as 0 to 59. .\" ---------- .TP 18 .BI @second( e ) Return the number of seconds since the last full minute, encoded as 0 to 59. .\" ========== .SS "Spreadsheet Update" .\" ---------- Re-evaluation of spreadsheet expressions is done by row or by column depending on the selected calculation order. Evaluation is repeated up to .I iterations times for each update if necessary, so forward references usually work as expected. See .B set above. If stability is not reached after ten iterations, a warning is printed. This is usually due to a long series of forward references, or to unstable cyclic references (for example, set .BR A0 's expression to \(lqA0+1\(rq). .TP 18 .B @numiter Returns the number of iterations performed so far. .\" ========== .SS "Programmable Function Keys" .\" ---------- Function keys can be used in .B pname if your terminal supports them, and they are programmable. To program the function keys, you use the .B fkey command. This command may be used in a .B .scrc file or a macro file, or it may be entered directly into .BR pname 's command line. Defined function keys will be saved with the file. There is no shortcut, as there is with most commands, so the full command must be typed in. Pressing enter when not editing a line will start you off with a blank line for this purpose. The format of the .B fkey command is: .IP .B fkey .I n = .BI \(dq command \(dq .LP where .I n is the function key number (n = 1 for F1, n = 2 for F2, etc.), and .I command is the command to be run. For example, .IP .B "fkey 2 = ""merge \\\\""|~/scmacros/macro1\\\\"""" .LP will run the macro called .I macro1 located in a subdirectory of your home directory called .I scmacros when the F2 key is pressed. Note that embedded quotes must be escaped by a backslash. If you want to include the cell address of the current cell in the command line, you may do so by entering \(lq$$\(rq in its place in the command. For example, .IP .B "fkey 5 = ""fmt $$ \\\\""^D%A\\\\"""" .LP will cause the F5 key to format the current cell to display the full weekday name of the numeric date value stored there. The ^D is a CTRL-D character, which denotes a date format. .LP Although it may be overridden by the .B fkey command, the F1 key is predefined by default to execute .BR "man sc" . Unlike the user-defined function keys, this definition will also work in edit, insert, replace, and navigate modes. .LP To undefine a function key, merely define it as the empty string (""). Undefining the F1 key will restore the default behavior. .\" ========== .SS "Plugins" .\" ---------- There are three ways in which external programs can be used as plugins with .BR pname . First, they can be used as external commands. When used as an external command, any command not recognized by .B pname will be searched for first in $HOME/.sc/plugins, and then in #LIBDIR#/plugins. If found, it will be run with its standard input and standard output redirected back to .BR pname . These are used to send commands to, and receive responses back from .B pname in the same way that advanced macros do. .LP The second and third ways that programs can be used as plugins with .B pname are to automatically convert files to and from .B sc format based on their extensions. In order to use them in this way, you must first associate a given extension to a corresponding plugin (for reading) or plugout (for writing) using the .B plugin and .B plugout commands. These commands should be placed in your .B .scrc file, and have the following syntax: .IP .B plugin .BI \(dq ext \(dq = .BI \(dq programname \(dq .LP or .IP .B plugout .BI \(dq ext \(dq = .BI \(dq programname \(dq .LP where .I ext is the extension and .I programname is the name of the plugin program to be used for filenames with that extension. For input, the plugin merely reads the specified file, performs whatever conversion is necessary, and writes the resulting data to standard output. For output, the plugin writes .B pname commands to standard output and reads the replies from standard input in the same way that an advanced macro would, and then converts the data to the appropriate format and writes it to a file with the specified filename. .\" ========== .SH FILES .TP 3in #LIBDIR#/tutorial.pname Tutorial spreadsheet. .TP 3in $HOME/.scrc Initialization commands. .IP "./.scrc" More initialization commands. .SH SEE ALSO bc(1), dc(1), crypt(1), ppname(1) .\" ========== .SH BUGS Top-to-bottom, left-to-right evaluation of expressions is silly. A proper following of the dependency graph with (perhaps) recourse to relaxation should be implemented. .\" ---------- .PP On some systems, if the cell cursor is in column 0 with topline enabled (so the current cell is highlighted), or if any cell in column 0 is highlighted, the corresponding row number gets displayed and then blanked during a screen refresh. This looks like a bug in .BR curses . .\" ---------- .PP Many commands give no indication (a message or beep) if they have null effect. Some should give confirmation of their action, but they don't. .SH AUTHORS This is a much modified version of a public domain spread sheet originally authored by James Gosling, and subsequently modified and posted to USENET by Mark Weiser under the name .BR vc . The program was subsequently renamed .BR sc , and further modified by numerous contributors, Jeff Buhrt of Proslink, Inc. and Robert Bond of Sequent, prominent among them. Maintainer until 2002 had been .MT nrocinu@myrealbox.com Chuck Martin .ME . Bugs and other issues can be reported at .UR https://github.com/n-t-roff/sc the current maintenance site .UE . .PP Other contributors include: Tom Anderson, Glenn T. Barry, Gregory Bond, Stephen (Steve) M. Brooks, Peter Brower, John Campbell, Lawrence Cipriani, Jim Clausing, Dave Close, Chris Cole, Jonathan Crompron, David I. Dalva, Glen Ditchfield, Sam Drake, James P. Dugal, Paul Eggert, Andy Fyfe, Jack Goral, Piercarlo "Peter" Grandi, Henk Hesselink, Jeffrey C Honig, Kurt Horton, Jonathan I. Kamens, Peter King, Tom Kloos, Michael Lapsley, Casey Leedom, Jay Lepreau, Dave Lewis, Rick Linck, Soren Lundsgaard, Tad Mannes, Rob McMahon, Chris Metcalf, Mark Nagel, Ulf Noren, Marius Olafsson, Gene H. Olson, Henk P. Penning, Rick Perry, Larry Philps, Eric Putz, Jim Richardson, Michael Richardson, R. P. C. Rodgers, Kim Sanders, Mike Schwartz, Alan Silverstein, Lowell Skoog, Herr Soeryantono, Tim Theisen, Tom Tkacik, Andy Valencia, Adri Verhoef, Rick Walker, Petri Wessman, and Tim Wilson. .\" end of man page .\" vim:set syntax=groff: sc-7.16_1.1.2/sc.h000066400000000000000000000521601326046243500133620ustar00rootroot00000000000000/* SC A Table Calculator * Common definitions * * original by James Gosling, September 1982 * modified by Mark Weiser and Bruce Israel, * University of Maryland * R. Bond 12/86 * More mods by Alan Silverstein, 3-4/88, see list of changes. * $Revision: 7.16 $ * */ #ifdef MSDOS #include #endif #define CLEAR_LINE error("%s", "") /* suppress warning on NetBSD curses */ #define ATBL(tbl, row, col) (*(tbl + row) + (col)) #define MINROWS 100 /* minimum size at startup */ #define MINCOLS 30 #define ABSMAXCOLS 702 /* absolute cols: ZZ (base 26) */ #define CRROWS 1 #define CRCOLS 2 #define RESROW 3 /* rows reserved for prompt, error, and column numbers */ /* formats for engformat() */ #define REFMTFIX 0 #define REFMTFLT 1 #define REFMTENG 2 #define REFMTDATE 3 #define REFMTLDATE 4 #define DEFWIDTH 10 /* Default column width and precision */ #define DEFPREC 2 #define DEFREFMT REFMTFIX /* Make default format fixed point THA 10/14/90 */ #define FKEYS 24 /* Number of function keys available */ #define HISTLEN 100 /* Number of history entries for vi emulation */ #define CPAIRS 8 /* Number of color pairs available */ #define COLFORMATS 10 /* Number of custom column formats */ #define DELBUFSIZE 40 /* Number of named buffers + 4 */ #ifdef PSC # define error(msg) fprintf(stderr, msg); #else # define error if (isatty(fileno(stdout)) && !move(1,0) && !clrtoeol()) printw #endif #define FBUFLEN 1024 /* buffer size for a single field */ #define PATHLEN (PATH_MAX < 8192 ? 8192 : PATH_MAX) /* maximum path length */ #ifndef DFLT_PAGER #define DFLT_PAGER "more" /* more is probably more widespread than less */ #endif /* DFLT_PAGER */ #define MAXCMD 160 /* for ! command and commands that use the pager */ #ifndef A_CHARTEXT /* Should be defined in curses.h */ #define A_CHARTEXT 0xff #endif #ifndef color_set #define color_set(c, o) attron(COLOR_PAIR(c)) #endif #if !defined(HAVE_ATTR_T) && defined(_COMPAT_H) /* Not defined for psc */ typedef chtype attr_t; #endif #if !defined(HAVE_ATTR_GET) && !defined(NO_ATTR_GET) #define attr_get(a, p, o) ((void)((a) != 0 && (*(a) = stdscr->_attrs)), \ (void)((p) != 0 && \ (*(p) = PAIR_NUMBER(stdscr->_attrs))), OK) #endif #if (defined(BSD42) || defined(BSD43)) && !defined(strrchr) #define strrchr rindex #endif #if (defined(BSD42) || defined(BSD43)) && !defined(strchr) #define strchr index #endif #ifdef SYSV4 size_t strlen(); #endif #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif /* !FALSE */ /* * ent_ptr holds the row/col # and address type of a cell * * vf is the type of cell address, 0 non-fixed, or bitwise OR of FIX_ROW or * FIX_COL * vp : we just use vp->row or vp->col, vp may be a new cell just for holding * row/col (say in gram.y) or a pointer to an existing cell */ struct ent_ptr { int vf; struct ent *vp; }; /* holds the beginning/ending cells of a range */ struct range_s { struct ent_ptr left, right; }; /* * Some not too obvious things about the flags: * IS_VALID means there is a valid number in v. * IS_LOCKED means that the cell cannot be edited. * IS_LABEL set means it points to a valid constant string. * IS_STREXPR set means expr yields a string expression. * If IS_STREXPR is not set, and expr points to an expression tree, the * expression yields a numeric expression. * So, either v or label can be set to a constant. * Either (but not both at the same time) can be set from an expression. */ #define VALID_CELL(p, r, c) ((p = *ATBL(tbl, r, c)) && \ ((p->flags & IS_VALID) || p->label)) /* info for each cell, only alloc'd when something is stored in a cell */ struct ent { double v; /* v && label are set in EvalAll() */ char *label; struct enode *expr; /* cell's contents */ short flags; short row, col; short nrow, ncol; /* link to note */ short nlastrow, nlastcol; struct ent *next; /* next deleted ent (pulled, deleted cells) */ char *format; /* printf format for this cell */ char cellerror; /* error in a cell? */ }; #define FIX_ROW 1 #define FIX_COL 2 /* stores type of operation this cell will perform */ struct enode { int op; union { int gram_match; /* some compilers (hp9000ipc) need this */ double k; /* constant # */ struct ent_ptr v; /* ref. another cell */ struct range_s r; /* op is on a range */ char *s; /* string part of a cell */ struct { /* other cells use to eval()/seval() */ struct enode *left, *right; char *s; /* previous value of @ext function in case */ } o; /* external functions are turned off */ } e; }; /* stores a range (left, right) */ struct range { struct ent_ptr r_left, r_right; char *r_name; /* possible name for this range */ struct range *r_next, *r_prev; /* chained ranges */ int r_is_range; }; /* stores a framed range (left, right) */ struct frange { struct ent *or_left, *or_right; /* outer range */ struct ent *ir_left, *ir_right; /* inner range */ struct frange *r_next, *r_prev; /* chained ranges */ }; /* stores a color range (left, right) */ struct crange { struct ent *r_left, *r_right; int r_color; struct crange *r_next, *r_prev; /* chained ranges */ }; struct colorpair { int fg; int bg; struct enode *expr; }; /* stores an abbreviation and its expansion */ struct abbrev { char *abbr; char *exp; struct abbrev *a_next, *a_prev; }; struct impexfilt { char ext[PATHLEN]; char plugin[PATHLEN]; char type; struct impexfilt *next; }; /* Use this structure to save the last 'g' command */ struct go_save { int g_type; double g_n; char *g_s; int g_row; int g_col; int g_lastrow; int g_lastcol; int strow; int stcol; int stflag; int errsearch; }; /* op values */ #define O_VAR 'v' #define O_CONST 'k' #define O_ECONST 'E' /* constant cell w/ an error */ #define O_SCONST '$' #define REDUCE 0200 /* Or'ed into OP if operand is a range */ #define OP_BASE 256 #define ACOS (OP_BASE + 0) #define ASIN (OP_BASE + 1) #define ATAN (OP_BASE + 2) #define CEIL (OP_BASE + 3) #define COS (OP_BASE + 4) #define EXP (OP_BASE + 5) #define FABS (OP_BASE + 6) #define FLOOR (OP_BASE + 7) #define HYPOT (OP_BASE + 8) #define LOG (OP_BASE + 9) #define LOG10 (OP_BASE + 10) #define POW (OP_BASE + 11) #define SIN (OP_BASE + 12) #define SQRT (OP_BASE + 13) #define TAN (OP_BASE + 14) #define DTR (OP_BASE + 15) #define RTD (OP_BASE + 16) #define SUM (OP_BASE + 17) #define PROD (OP_BASE + 18) #define AVG (OP_BASE + 19) #define COUNT (OP_BASE + 20) #define STDDEV (OP_BASE + 21) #define MAX (OP_BASE + 22) #define MIN (OP_BASE + 23) #define RND (OP_BASE + 24) #define HOUR (OP_BASE + 25) #define MINUTE (OP_BASE + 26) #define SECOND (OP_BASE + 27) #define MONTH (OP_BASE + 28) #define DAY (OP_BASE + 29) #define YEAR (OP_BASE + 30) #define NOW (OP_BASE + 31) #define DATE (OP_BASE + 32) #define FMT (OP_BASE + 33) #define SUBSTR (OP_BASE + 34) #define STON (OP_BASE + 35) #define EQS (OP_BASE + 36) #define EXT (OP_BASE + 37) #define ELIST (OP_BASE + 38) /* List of expressions */ #define LMAX (OP_BASE + 39) #define LMIN (OP_BASE + 40) #define NVAL (OP_BASE + 41) #define SVAL (OP_BASE + 42) #define PV (OP_BASE + 43) #define FV (OP_BASE + 44) #define PMT (OP_BASE + 45) #define STINDEX (OP_BASE + 46) #define LOOKUP (OP_BASE + 47) #define ATAN2 (OP_BASE + 48) #define INDEX (OP_BASE + 49) #define DTS (OP_BASE + 50) #define TTS (OP_BASE + 51) #define ABS (OP_BASE + 52) #define HLOOKUP (OP_BASE + 53) #define VLOOKUP (OP_BASE + 54) #define ROUND (OP_BASE + 55) #define IF (OP_BASE + 56) #define FILENAME (OP_BASE + 57) #define MYROW (OP_BASE + 58) #define MYCOL (OP_BASE + 59) #define LASTROW (OP_BASE + 60) #define LASTCOL (OP_BASE + 61) #define COLTOA (OP_BASE + 62) #define UPPER (OP_BASE + 63) #define LOWER (OP_BASE + 64) #define CAPITAL (OP_BASE + 65) #define NUMITER (OP_BASE + 66) #define ERR_ (OP_BASE + 67) #define PI_ (OP_BASE + 68) #define BLACK (OP_BASE + 69) #define RED (OP_BASE + 70) #define GREEN (OP_BASE + 71) #define YELLOW (OP_BASE + 72) #define BLUE (OP_BASE + 73) #define MAGENTA (OP_BASE + 74) #define CYAN (OP_BASE + 75) #define WHITE (OP_BASE + 76) /* flag values */ #define IS_VALID 0001 #define IS_CHANGED 0002 #define IS_STREXPR 0004 #define IS_LEFTFLUSH 0010 #define IS_DELETED 0020 #define IS_LOCKED 0040 #define IS_LABEL 0100 #define IS_CLEARED 0200 #define MAY_SYNC 0400 /* cell error (1st generation (ERROR) or 2nd+ (INVALID)) */ #define CELLOK 0 #define CELLERROR 1 #define CELLINVALID 2 #define ctl(c) ((c)&037) #define ESC 033 #define DEL 0177 /* calculation order */ #define BYCOLS 1 #define BYROWS 2 /* values for showrange for ranges of rows or columns */ #define SHOWROWS 2 #define SHOWCOLS 3 /* tblprint style output for: */ #define TBL 1 /* 'tbl' */ #define LATEX 2 /* 'LaTeX' */ #define TEX 3 /* 'TeX' */ #define SLATEX 4 /* 'SLaTeX' (Scandinavian LaTeX) */ #define FRAME 5 /* tblprint style output for FrameMaker */ /* Types for etype() */ #define NUM 1 #define STR 2 #define GROWAMT 30 /* default minimum amount to grow */ #define GROWNEW 1 /* first time table */ #define GROWROW 2 /* add rows */ #define GROWCOL 3 /* add columns */ #define GROWBOTH 4 /* grow both */ extern struct ent ***tbl; /* data table ref. in vmtbl.c and ATBL() */ extern char curfile[PATHLEN]; extern int arg; extern int strow, stcol; extern int currow, curcol; extern int gmyrow, gmycol; /* globals used for @myrow, @mycol cmds */ extern int rescol; /* columns reserved for row numbers */ extern int savedrow[37], savedcol[37]; extern int savedstrow[37], savedstcol[37]; extern int FullUpdate; extern int maxrow, maxcol; extern int maxrows, maxcols; /* # cells currently allocated */ extern int rowsinrange; /* Number of rows in target range of a goto */ extern int colsinrange; /* Number of cols in target range of a goto */ extern int *fwidth; extern int *precision; extern int *realfmt; extern char *colformat[10]; extern char *col_hidden; extern char *row_hidden; extern char line[FBUFLEN]; extern ssize_t linelim; extern int changed; extern struct ent *delbuf[DELBUFSIZE]; extern char *delbuffmt[DELBUFSIZE]; extern int dbidx; extern int qbuf; /* buffer no. specified by `"' command */ extern int showsc, showsr; extern int showrange; /* Causes ranges to be highlighted */ extern int cellassign; extern int macrofd; extern int cslop; extern int usecurses; extern int brokenpipe; /* Set to true if SIGPIPE is received */ extern char dpoint; /* country-dependent decimal point from locale */ extern char thsep; /* country-dependent thousands separator from locale */ extern char histfile[PATHLEN]; extern int lastmx, lastmy; /* Screen address of the cursor */ extern int lastcol, lcols; /* Spreadsheet Column the cursor was in last */ extern int lastendrow; /* Last bottom row of screen */ extern int framerows; /* Rows in current frame */ extern int framecols; /* Columns in current frame */ extern char mode_ind; /* Mode indicator */ extern int seenerr; extern char *rev; extern FILE *openfile(char *, size_t, int *, int *); extern char *coltoa(int col); extern char *findplugin(char *ext, char type); extern char *findhome(char *, size_t); extern char *r_name(int r1, int c1, int r2, int c2); extern void *scxmalloc(size_t n); extern void *scxrealloc(void *ptr, size_t n); extern char *seval(register struct enode *se); extern char *v_name(int row, int col); extern double eval(register struct enode *e); extern int any_locked_cells(int r1, int c1, int r2, int c2); extern int are_colors(void); extern int are_frames(void); extern int are_ranges(void); extern int atocol(char *string, int len); extern int creadfile(char *save, int eraseflg); extern int cwritefile(char *fname, int r0, int c0, int rn, int cn); extern bool engformat(int fmt, int width, int lprecision, double val, char *buf, int buflen); extern int etype(register struct enode *e); extern int find_range(char *name, int len, struct ent *lmatch, struct ent *rmatch, struct range **rng); extern bool format(char *fmt, int lprecision, double val, char *buf, size_t buflen); extern int get_rcqual(int ch); extern int growtbl(int rowcol, int toprow, int topcol); extern int locked_cell(int r, int c); extern int modcheck(char *endstr); extern int nmgetch(void); extern int plugin_exists(char *name, size_t len, char *path); extern int readfile(char *fname, int eraseflg); extern int writefile(char *fname, int r0, int c0, int rn, int cn); extern int yn_ask(char *msg); extern struct abbrev *find_abbr(char *abbrev, int len, struct abbrev **prev); extern struct colorpair *cpairs[8]; extern struct enode *copye(register struct enode *e, int Rdelta, int Cdelta, int r1, int c1, int r2, int c2, int transpose); extern struct enode *new(int op, struct enode *a1, struct enode *a2); extern struct enode *new_const(int op, double a1); extern struct enode *new_range(int op, struct range_s a1); extern struct enode *new_str(char *s); extern struct enode *new_var(int op, struct ent_ptr a1); extern struct ent *lookat(int row, int col); extern struct crange *find_crange(int row, int col); extern struct frange *find_frange(int row, int col); extern void EvalAll(void); extern void add_crange(struct ent *r_left, struct ent *r_right, int pair); extern void add_frange(struct ent *or_left, struct ent *or_right, struct ent *ir_left, struct ent *ir_right, int toprows, int bottomrows, int leftcols, int rightcols); extern void add_range(char *name, struct ent_ptr left, struct ent_ptr right, int is_range); extern void addplugin(char *ext, char *plugin, char type); extern void backcol(int arg); extern void backrow(int arg); extern void change_color(int pair, struct enode *e); extern void checkbounds(int *rowp, int *colp); extern void clearent(struct ent *v); extern void clean_crange(void); extern void clean_frange(void); extern void clean_range(void); extern void closecol(int arg); extern void closefile(FILE *f, int pid, int rfd); extern void closerow(int r, int numrow); extern void colshow_op(void); extern void copy(struct ent *dv1, struct ent *dv2, struct ent *v1, struct ent *v2); extern void copyent(register struct ent *n, register struct ent *p, int dr, int dc, int r1, int c1, int r2, int c2, int transpose); extern void decompile(register struct enode *e, int priority); extern void deleterow(register int arg); extern void del_range(struct ent *left, struct ent *right); extern void del_abbr(char *abbrev); extern void deraw(int ClearLastLine); extern void diesave(void); extern void doend(int rowinc, int colinc); extern void doformat(int c1, int c2, int w, int p, int r); extern void dupcol(void); extern void duprow(void); extern void doquery(char *s, char *data, int fd); extern void dostat(int fd); extern void dotick(int tick); extern void editexp(int row, int col); extern void editfmt(int row, int col); extern void edit_mode(void); extern void edits(int row, int col); extern void editv(int row, int col); extern void efree(struct enode *e); extern void erase_area(int sr, int sc, int er, int ec, int ignorelock); extern void erasedb(void); extern void eraser(struct ent *v1, struct ent *v2); extern void fgetnum(int r0, int c0, int rn, int cn, int fd); extern void fill(struct ent *v1, struct ent *v2, double start, double inc); extern void fix_colors(int row1, int col1, int row2, int col2, int delta1, int delta2); extern void fix_frames(int row1, int col1, int row2, int col2, int delta1, int delta2); extern void fix_ranges(int row1, int col1, int row2, int col2, int delta1, int delta2); extern void flush_saved(void); extern void formatcol(int arg); extern void format_cell(struct ent *v1, struct ent *v2, char *s); extern void forwcol(int arg); extern void forwrow(int arg); extern void free_ent(register struct ent *p, int unlock); extern void getexp(int r0, int c0, int rn, int cn, int fd); extern void getfmt(int r0, int c0, int rn, int cn, int fd); extern void getformat(int col, int fd); extern void getnum(int r0, int c0, int rn, int cn, int fd); extern void getstring(int r0, int c0, int rn, int cn, int fd); extern void go_last(void); extern void goraw(void); extern void help(void); extern void hide_col(int arg); extern void hide_row(int arg); extern void hidecol(int arg); extern void hiderow(int arg); extern void initcolor(int colornum); extern void initkbd(void); extern void ins_in_line(int c); extern void ins_string(char *s); extern void insert_mode(void); extern void insertcol(int arg, int delta); extern void insertrow(int arg, int delta); extern void kbd_again(void); extern void label(register struct ent *v, register char *s, int flushdir); extern void let(struct ent *v, struct enode *e); extern void list_colors(FILE *f); extern void list_ranges(FILE *f); extern void lock_cells(struct ent *v1, struct ent *v2); extern void markcell(void); extern void move_area(int dr, int dc, int sr, int sc, int er, int ec); extern void mover(struct ent *d, struct ent *v1, struct ent *v2); extern void moveto(int row, int col, int lastrow, int lastcol, int cornrow, int corncol); extern void toggle_navigate_mode(void); extern void num_search(double n, int firstrow, int firstcol, int lastrow, int lastcol, int errsearch); extern void printfile(char *fname, int r0, int c0, int rn, int cn); extern void pullcells(int to_insert); extern void query(const char *s, char *data); extern void read_hist(void); extern void remember(int save); extern void resetkbd(void); extern void rowshow_op(void); extern void scxfree(void *p); extern void setauto(int i); extern void setiterations(int i); extern void setorder(int i); extern void showcol(int c1, int c2); extern void showdr(void); extern void showrow(int r1, int r2); extern void showstring(char *string, int dirflush, int hasvalue, int row, int col, int *nextcolp, int mxcol, int *fieldlenp, int r, int c, struct frange *fr, int frightcols, int flcols, int frcols); extern void signals(void); extern void slet(struct ent *v, struct enode *se, int flushdir); extern void sortrange(struct ent *left, struct ent *right, char *criteria); extern void startshow(void); extern void startdisp(void); extern void stopdisp(void); extern void str_search(char *s, int firstrow, int firstcol, int lastrow, int lastcol, int num); extern void sync_cranges(void); extern void sync_franges(void); extern void sync_ranges(void); extern void sync_refs(void); extern void tblprintfile(char *fname, int r0, int c0, int rn, int cn); extern void unlock_cells(struct ent *v1, struct ent *v2); extern void update(int anychanged); extern void valueize_area(int sr, int sc, int er, int ec); extern void write_cells(register FILE *f, int r0, int c0, int rn, int cn, int dr, int dc); extern void write_colors(FILE *f, int indent); extern void write_cranges(FILE *f); extern void write_fd(register FILE *f, int r0, int c0, int rn, int cn); extern void write_franges(FILE *f); extern void write_hist(void); extern void write_line(int c); extern void write_ranges(FILE *f); extern void yank_area(int sr, int sc, int er, int ec); extern void yyerror(char *err); extern int yylex(void); extern int yyparse(void); extern int backup_file(char *path); extern int modflg; #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH) extern int Crypt; #endif extern char *mdir; extern char *autorun; extern int skipautorun; extern char *fkey[FKEYS]; extern char *scext; extern char *ascext; extern char *tbl0ext; extern char *tblext; extern char *latexext; extern char *slatexext; extern char *texext; extern int scrc; extern double prescale; extern int extfunc; extern int propagation; extern int repct; extern int calc_order; extern int autocalc; extern int autolabel; extern int autoinsert; extern int autowrap; extern int optimize; extern int numeric; extern int showcell; extern int showtop; extern int color; extern int colorneg; extern int colorerr; extern int braille; extern int braillealt; extern int dobackups; extern int loading; extern int getrcqual; extern int tbl_style; extern int rndtoeven; extern char *progname; #ifdef TRACE extern FILE *ftrace; #endif extern int numeric_field; extern int craction; extern int pagesize; /* If nonzero, use instead of 1/2 screen height */ extern int rowlimit; extern int collimit; #ifdef NCURSES_MOUSE_VERSION extern MEVENT mevent; #endif void gotonote(void); void center(int, int, int, int); void rjustify(int, int, int, int); void ljustify(int, int, int, int); void yankcol(int); void yankrow(int); void list_frames(FILE *); void yankr(struct ent *, struct ent *); void dogetkey(void); void doseval(struct enode *, int, int, int); void doeval(struct enode *, char *, int, int, int); void getrange(char *, int); void getframe(int); void add_abbr(char *); void repaint(int, int, int, int, int); void update(int); void doshell(void); #ifdef SIGVOID void doquit(int); void time_out(int); void dump_me(int); void nopipe(int); # ifdef SIGWINCH void winchg(int); # endif #else int doquit(); int time_out(); int dump_me(); int nopipe(); # ifdef SIGWINCH int winchg(); # endif #endif void gohome(void); void leftlimit(void); void rightlimit(void); void gototop(void); void gotobottom(void); void mouseon(void); void mouseoff(void); #if BSD42 || SYSIII #ifndef cbreak #define cbreak crmode #define nocbreak nocrmode #endif #endif #if defined(BSD42) || defined(BSD43) && !defined(ultrix) #define memcpy(dest, source, len) bcopy(source, dest, (unsigned int)len); #define memset(dest, zero, len) bzero((dest), (unsigned int)(len)); #else #include #endif sc-7.16_1.1.2/scqref.doc000066400000000000000000000013751326046243500145600ustar00rootroot00000000000000.\" Carsten Kunze, 2016 .Dd April 11, 2016 .Dt PNAMEQREF 1 .Sh NAME .Nm pnameqref .Nd output quick reference card in roff's .Fl ms format .Sh SYNOPSIS .Nm .Li | Ar "roff postprocessing ..." .Sh DESCRIPTION .Nm outputs a quick reference card for .Nm sc in roff's .Fl ms format. This output needs to be postprocessed by a roff tool to format it suited to be viewed or printed. .Sh EXAMPLES Generate PDF document with traditional troff: .Bd -filled -offset indent .Nm .Li | troff -ms | dpost | ps2pdf - Ar filename Ns Li .pdf .Ed .Pp .No The same using Nm groff : .Bd -filled -offset indent .Nm .Li | troff -Tpdf -ms | gropdf > Ar filename Ns Li .pdf .Ed .Pp View in a text terminal: .Bd -filled -offset indent .Nm .Li | nroff -ms | less .Ed .\" vim:set syntax=groff: sc-7.16_1.1.2/screen.c000066400000000000000000001003311326046243500142210ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Curses based Screen driver * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * More mods by Alan Silverstein, 3-4/88, see list of changes. * $Revision: 7.16 $ * */ #include #include #include #ifndef MSDOS #include #endif #include "compat.h" #include "sc.h" #ifdef VMS extern int VMS_read_raw; /*sigh*/ VMS_read_raw = 1; #endif #ifdef BROKENCURSES /* nl/nonl bug fix */ #undef nl #undef nonl #define nl() (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty)) #define nonl() (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty)) #endif char under_cursor = ' '; /* Data under the < cursor */ char mode_ind = 'i'; char search_ind = ' '; extern char revmsg[80]; int lines, cols; int rows, lcols; int lastmx, lastmy; /* Screen address of the cursor */ int lastrow = -1; /* Spreadsheet Row the cursor was in last */ int lastcol = -1; /* Spreadsheet Column the cursor was in last */ int lastendrow = -1; /* Last bottom row of screen */ int lastftoprows = 0; /* Rows in top of frame cursor was in last */ int lastfbottomrows = 0; /* Rows in bottom of frame cursor was in last */ int lastfleftcols = 0; /* Columns in left side of frame cursor was in last */ int lastfrightcols = 0; struct frange *lastfr = 0; /* Last framed range we were in */ bool frTooLarge = 0; /* If set, either too many rows or too many columns exist in frame to allow room for the scrolling portion of the framed range */ int framerows; /* Rows in current frame */ int framecols; /* Columns in current frame */ int rescol = 4; /* Columns reserved for row numbers */ extern int showneed; /* Causes cells needing values to be highlighted */ extern int showexpr; /* Causes cell exprs to be displayed, highlighted */ extern int shownote; /* Causes cells with attached notes to be highlighted */ #ifdef RIGHT_CBUG extern int wasforw; /* Causes screen to be redisplay if on lastcol */ #endif extern struct go_save gs; /* * update() does general screen update * * standout last time in update()? * At this point we will let curses do work */ int standlast = FALSE; void update(int anychanged) /* did any cell really change in value? */ { int row, col; struct ent **pp; int mxrow, mxcol; int minsr = 0, minsc = 0, maxsr = 0, maxsc = 0; int r, i; struct frange *fr; struct crange *cr; int ftoprows, fbottomrows, fleftcols, frightcols; int ftrows, fbrows, flcols, frcols; bool message; #ifndef MSDOS /* * If receiving input from a pipeline, don't display spreadsheet data * on screen. */ if (!usecurses) return; #endif getmaxyx(stdscr, lines, cols); fr = lastfr; if (!(fr && fr->or_left->row <= currow && /* If we've left the */ fr->or_left->col <= curcol && /* previous framed range... */ fr->or_right->row >= currow && fr->or_right->col >= curcol)) { fr = find_frange(currow, curcol); if (fr != lastfr) FullUpdate++; } if (fr) { ftoprows = fr->ir_left->row - fr->or_left->row; fbottomrows = fr->or_right->row - fr->ir_right->row; fleftcols = fr->ir_left->col - fr->or_left->col; frightcols = fr->or_right->col - fr->ir_right->col; framerows = RESROW + ftoprows + fbottomrows; framecols = rescol; for (r = fr->or_left->row - 1, i = ftoprows; i; i--) if (row_hidden[r+i]) framerows--; for (r = fr->or_right->row - 1, i = fbottomrows; i; i--) if (row_hidden[r-i]) framerows--; for (r = fr->or_left->col - 1, i = fleftcols; i; i--) { if (col_hidden[r+i]) continue; framecols += fwidth[r+i]; } for (r = fr->or_right->col - 1, i = frightcols; i; i--) { if (col_hidden[r-i]) continue; framecols += fwidth[r-i]; } if (framerows >= lines || framecols >= cols) { frTooLarge = TRUE; if (FullUpdate) { error("Frame too large for screen size - ignoring"); } ftoprows = fbottomrows = fleftcols = frightcols = 0; strow -= lastftoprows; stcol -= lastfleftcols; } else { frTooLarge = FALSE; if (strow >= fr->or_left->row) { if (fr == lastfr && strow < fr->or_left->row + ftoprows) strow = fr->or_left->row; else if (strow > fr->ir_right->row) { strow = fr->ir_right->row; FullUpdate++; } } if (stcol >= fr->or_left->col) { if (stcol < fr->or_left->col + fleftcols) stcol = fr->or_left->col; else if (stcol > fr->ir_right->col) { stcol = fr->ir_right->col; FullUpdate++; } } if (fr == lastfr && currow == lastrow) fbottomrows = lastfbottomrows; else if (currow < fr->ir_right->row) fbottomrows = fr->or_right->row - fr->ir_right->row; else fbottomrows = fr->or_right->row - currow; if (fr == lastfr && curcol == lastcol) frightcols = lastfrightcols; else if (curcol < fr->ir_right->col) frightcols = fr->or_right->col - fr->ir_right->col; else frightcols = fr->or_right->col - curcol; } } else { ftoprows = fbottomrows = fleftcols = frightcols = 0; framerows = framecols = 0; } if (fr != lastfr && !gs.stflag && lastfr) { if (strow >= lastfr->ir_left->row) strow -= lastftoprows; if (stcol >= lastfr->ir_left->col) stcol -= lastfleftcols; } ftrows = ftoprows; fbrows = fbottomrows; flcols = frcols = 0; if (fr) { for (r = fr->or_left->row - 1, i = ftrows; i; i--) if (row_hidden[r+i]) ftrows--; for (r = fr->or_right->row + 1, i = fbrows; i; i--) if (row_hidden[r-i]) fbrows--; for (r = fr->or_left->col - 1, i = fleftcols; i; i--) { if (col_hidden[r+i]) continue; flcols += fwidth[r+i]; } for (r = fr->or_right->col + 1, i = frightcols; i; i--) { if (col_hidden[r-i]) continue; frcols += fwidth[r-i]; } } /* * Place the cursor on the screen. Set col, curcol, stcol, lastcol as * needed. If strow and stcol are negative, centering is forced. */ if ((curcol != lastcol) || FullUpdate) { while (col_hidden[curcol]) /* You can't hide the last row or col */ curcol++; if (fwidth[curcol] > cols - rescol - 2) { error("column %s too wide - resizing", coltoa(curcol)); doformat(curcol, curcol, cols - rescol - 2, precision[curcol], realfmt[curcol]); } /* First see if the last display still covers curcol */ if (stcol >= 0 && stcol <= curcol) { int c = 0; if (fr) { if (fr != lastfr) { if (stcol == fr->or_left->col) stcol += fleftcols; else if (stcol >= fr->or_left->col && !gs.stflag) { stcol += fleftcols; if (stcol > fr->ir_right->col) stcol = fr->ir_right->col + 1; } } else if (stcol == fr->or_left->col) stcol += fleftcols; } i = stcol; lcols = 0; col = rescol + frcols; if (fr && stcol >= fr->or_left->col) { if (stcol < fr->ir_left->col) i = fr->or_left->col; else col += flcols; } for (; i < maxcols && (col + fwidth[i] < cols-1 || col_hidden[i] || i < curcol); i++) { lcols++; if (fr && i == fr->ir_right->col + 1) { col -= frcols; frcols = frightcols = 0; } if (col_hidden[i]) continue; /* If there isn't room for more columns, and we haven't yet * reached the current column, start removing columns from * the left. */ while (col + fwidth[i] > cols - 2) { lcols--; col -= fwidth[stcol]; while (col_hidden[++stcol]) /**/ ; FullUpdate++; c++; } col += fwidth[i]; } if (!frTooLarge && fr && curcol <= stcol + lcols && fr->ir_left->col >= stcol + lcols) { while (stcol + lcols < fr->ir_left->col) { col -= fwidth[stcol]; lcols--; while (col_hidden[++stcol]) lcols--; while (col + fwidth[stcol + lcols] < cols - 1) { col += fwidth[stcol + lcols]; lcols++; } } } else if (c) stcol = -1; } while (stcol < 0 || curcol < stcol || stcol + lcols - 1 < curcol || (colsinrange != fwidth[curcol] && stcol != curcol && stcol + lcols - 1 < gs.g_lastcol)) { FullUpdate++; /* How about back one? */ if (stcol - 1 == curcol) { stcol--; /* Forward one? */ } else if (stcol >= 0 && stcol + lcols == curcol) { stcol++; } else if (stcol >= 0 && fr && curcol >= fr->or_left->col && curcol <= fr->ir_left->col && stcol < curcol && curcol <= stcol + lcols + fr->ir_left->col - fr->or_left->col) { while ((stcol + lcols < fr->ir_left->col && !frTooLarge) || (colsinrange != fwidth[curcol] && stcol != curcol && stcol + lcols - 1 < gs.g_lastcol)) { if (col_hidden[++stcol]) lcols--; } } else { /* Try to put the cursor in the center of the screen. * If we've just jumped to a range using the goto command, * center the range instead. */ colsinrange = (colsinrange > cols - rescol - flcols - frcols - 2 ? cols - rescol - flcols - frcols - 2 : colsinrange); col = (cols - rescol - flcols - frcols - colsinrange)/2; stcol = curcol; for (i = curcol - 1; i >= (fr ? fr->or_left->col + fleftcols : 0) && (col - fwidth[i] > 0 || col_hidden[i]); i--) { stcol--; if (col_hidden[i]) continue; col -= fwidth[i]; } if (fr && stcol < fr->or_left->col + fleftcols) { stcol = fr->or_left->col + fleftcols; if (curcol < stcol) stcol = curcol; } } /* Now pick up the counts again */ i = stcol; lcols = 0; col = rescol + frcols; if (fr && stcol >= fr->or_left->col) { if (stcol < fr->ir_left->col) i = fr->or_left->col; else col += flcols; } for (; i < maxcols && (col + fwidth[i] < cols-1 || col_hidden[i] || i < curcol); i++) { lcols++; if (fr && i == fr->ir_right->col + 1) { col -= frcols; frcols = frightcols = 0; } if (col_hidden[i]) continue; col += fwidth[i]; } } } if (fleftcols && stcol >= fr->or_left->col && stcol < fr->or_left->col + fleftcols) { lcols += (fr->or_left->col - stcol); stcol = fr->or_left->col + fleftcols; if (curcol < stcol) stcol = curcol; } /* Now - same process on the rows as the columns */ if ((currow != lastrow) || FullUpdate) { while (row_hidden[currow]) /* You can't hide the last row or col */ currow++; if (strow >= 0 && strow <= currow) { int c = 0; if (fr) { if (fr != lastfr) { if (strow == fr->or_left->row) strow += ftoprows; else if (strow >= fr->or_left->row && !gs.stflag) { strow += ftoprows; if (strow > fr->ir_right->row) strow = fr->ir_right->row + 1; } } else if (strow == fr->or_left->row) strow += ftoprows; } i = strow; rows = 0; row = RESROW + fbrows; if (fr && strow >= fr->or_left->row) { if (strow < fr->ir_left->row) i = fr->or_left->row; else row += ftrows; } for (; (row < lines || row_hidden[i] || i < currow) && i < maxrows; i++) { rows++; if (fr && i == fr->ir_right->row + 1) { row -= fbrows; fbrows = fbottomrows = 0; } if (row_hidden[i]) continue; /* If there isn't room for more rows, and we haven't yet * reached the current row, start removing rows from the * top. */ if (row >= lines) { rows--; row--; while (row_hidden[++strow]) /**/; FullUpdate++; c++; } row++; } if (!frTooLarge && fr && currow <= strow + rows && fr->ir_left->row >= strow + rows) { while (strow + rows < fr->ir_left->row) { while (row_hidden[++strow]) /**/; } } else if (c && currow > lastendrow) strow = -1; } while (strow < 0 || currow < strow || strow + rows - 1 < currow || strow + rows < currow + rowsinrange) { FullUpdate++; /* How about up one? */ if (strow - 1 == currow) { strow--; /* Down one? */ } else if (strow >= 0 && strow + rows == currow) { strow++; } else if (strow >= 0 && fr && currow >= fr->or_left->row && currow <= fr->ir_left->row && strow < currow && currow <= strow + rows + fr->ir_left->row - fr->or_left->row) { while ((strow + rows < fr->ir_left->row && !frTooLarge) || (rowsinrange > 1 && strow != currow && strow + rows - 1 < gs.g_lastrow)) { if (row_hidden[++strow]) rows--; } } else { /* Try to put the cursor in the center of the screen. * If we've just jumped to a range using the goto command, * center the range instead. */ rowsinrange = (rowsinrange > lines - RESROW - ftrows - fbrows ? lines - RESROW - ftrows - fbrows : rowsinrange); row = (lines - RESROW - ftrows - fbrows - rowsinrange)/2; strow = currow; for (i = currow - 1; i >= (fr ? fr->or_left->row + ftoprows : 0) && (row > 0 || row_hidden[i]); i--) { strow--; if (row_hidden[i]) continue; row--; } if (fr && strow < fr->or_left->row + ftoprows) { strow = fr->or_left->row + ftoprows; if (currow < strow) strow = currow; } } /* Now pick up the counts again */ i = strow; rows = 0; row = RESROW + fbrows; if (fr && strow >= fr->or_left->row) { if (strow < fr->ir_left->row) i = fr->or_left->row; else row += ftrows; } for (; (row < lines || row_hidden[i] || i < currow) && i < maxrows; i++) { rows++; if (fr && i == fr->ir_right->row + 1) { row -= fbrows; fbrows = fbottomrows = 0; } if (row_hidden[i]) continue; row++; } } } if (ftoprows && strow >= fr->or_left->row && strow < fr->or_left->row + ftoprows) { rows += (fr->or_left->row - strow); strow = fr->or_left->row + ftoprows; if (currow < strow) strow = currow; } mxcol = frightcols ? fr->or_right->col : stcol + lcols - 1; mxrow = fbottomrows ? fr->or_right->row : strow + rows - 1; gs.stflag = 0; lastfr = fr; lastftoprows = ftoprows; lastfbottomrows = fbottomrows; lastfleftcols = fleftcols; lastfrightcols = frightcols; /* Get rid of cursor standout on the cell at previous cursor position */ if (!FullUpdate) { if (showcell) { pp = ATBL(tbl, lastrow, lastcol); if (color && has_colors()) { if ((cr = find_crange(lastrow, lastcol))) color_set(cr->r_color, NULL); else color_set(1, NULL); if (*pp) { if (colorneg && (*pp)->flags & IS_VALID && (*pp)->v < 0) { if (cr) color_set(((cr->r_color) % CPAIRS) + 1, NULL); else color_set(2, NULL); } else if (colorerr && (*pp)->cellerror) color_set(3, NULL); } } repaint(lastmx, lastmy, fwidth[lastcol], 0, A_STANDOUT); } (void) move(lastmy, lastmx+fwidth[lastcol]); if ((inch() & A_CHARTEXT) == '<') (void) addch(under_cursor | (inch() & A_ATTRIBUTES)); repaint(lastmx, RESROW - 1, fwidth[lastcol], A_STANDOUT, 0); repaint(0, lastmy, rescol - 1, A_STANDOUT, 0); if (color && has_colors()) color_set(1, NULL); } lastrow = currow; lastcol = curcol; lastendrow = strow + rows; /* where is the the cursor now? */ lastmy = RESROW; if (fr && strow >= fr->or_left->row) if (strow < fr->ir_left->row) row = fr->or_left->row; else { row = strow; lastmy += ftrows; } else row = strow; for (; row < currow; row++) if (!row_hidden[row]) lastmy++; lastmx = rescol; if (fr && stcol >= fr->or_left->col) if (stcol < fr->ir_left->col) col = fr->or_left->col; else { col = stcol; lastmx += flcols; } else col = stcol; for (; col < curcol; col++) if (!col_hidden[col]) lastmx += fwidth[col]; if (color && has_colors()) color_set(1, NULL); if (FullUpdate || standlast) { (void) move(2, 0); (void) clrtobot(); (void) standout(); for (row = RESROW, i = (ftoprows && strow >= fr->or_left->row ? fr->or_left->row : strow); i <= mxrow; i++) { if (ftoprows && strow >= fr->or_left->row && row == RESROW + ftrows) i = (strow < i ? i : strow); if (fbottomrows && row == lines - fbrows) i = fr->or_right->row - fbottomrows + 1; if (row_hidden[i]) continue; (void) move(row, 0); (void) printw("%*d", rescol - 1, i); row++; } #ifdef RIGHT_CBUG if (wasforw) { clearok(stdscr, TRUE); wasforw = 0; } #endif (void) move(2, 0); (void) printw("%*s", rescol, " "); for (col = rescol, i = (fleftcols && stcol >= fr->or_left->col ? fr->or_left->col : stcol); i <= mxcol; i++) { register int k; if (fleftcols && stcol >= fr->or_left->col && col == rescol + flcols) i = (stcol < i ? i : stcol); if (frightcols && col + fwidth[i] >= cols - 1 - frcols && i < fr->or_right->col - frightcols + 1) i = fr->or_right->col - frightcols + 1; if (col_hidden[i]) continue; (void) move(2, col); k = (fwidth[i] - strlen(coltoa(i)))/2; if (fwidth[i] == 1) (void) printw("%1s", coltoa(i%26)); else if (braille) (void) printw("%-*s", fwidth[i], coltoa(i)); else (void) printw("%*s%-*s", k, "", fwidth[i]-k, coltoa(i)); col += fwidth[i]; } (void) standend(); } (void) move(1, 0); message = (inch() & A_CHARTEXT) != ' '; if (showrange) { if (showrange == SHOWROWS) { minsr = showsr < currow ? showsr : currow; minsc = fr ? fr->or_left->col : 0; maxsr = showsr > currow ? showsr : currow; maxsc = fr ? fr->or_right->col : maxcols; if (showtop && !message) { (void) clrtoeol(); (void) printw("Default range: %d:%d", minsr, maxsr); } } else if (showrange == SHOWCOLS) { minsr = 0; minsc = showsc < curcol ? showsc : curcol; maxsr = maxrows; maxsc = showsc > curcol ? showsc : curcol; if (showtop && !message) { char r_[6]; strlcpy(r_, coltoa(minsc), sizeof r_); strlcat(r_, ":", sizeof r_); strlcat(r_, coltoa(maxsc), sizeof r_); (void) clrtoeol(); (void) printw("Default range: %s", r_); } } else { minsr = showsr < currow ? showsr : currow; minsc = showsc < curcol ? showsc : curcol; maxsr = showsr > currow ? showsr : currow; maxsc = showsc > curcol ? showsc : curcol; if (showtop && !message) { (void) clrtoeol(); (void) printw("Default range: %s", r_name(minsr, minsc, maxsr, maxsc)); } } } else if (braille && braillealt && !message && mode_ind == 'v') { (void) clrtoeol(); (void) printw("Current cell: %s%d ", coltoa(curcol), currow); } /* Repaint the visible screen */ if (showrange || anychanged || FullUpdate || standlast) { /* may be reset in loop, if not next time we will do a FullUpdate */ if (standlast) { FullUpdate = TRUE; standlast = FALSE; } for (row = (ftoprows && strow >= fr->or_left->row ? fr->or_left->row : strow), r = RESROW; row <= mxrow; row++) { int c = rescol; int do_stand = 0; int fieldlen; int nextcol; if (row_hidden[row]) continue; if (ftoprows && strow >= fr->or_left->row && r == RESROW + ftrows) row = (strow < row ? row : strow); if (fbottomrows && r == lines - fbrows) row = fr->or_right->row - fbottomrows + 1; for (pp = ATBL(tbl, row, col = (fleftcols && stcol >= fr->or_left->col ? fr->or_left->col : stcol)); col <= mxcol; pp += nextcol - col, col = nextcol, c += fieldlen) { if (fleftcols && stcol >= fr->or_left->col && c == rescol + flcols) { col = (stcol < col ? col : stcol); pp = ATBL(tbl, row, col); } if (frightcols && c + fwidth[col] >= cols - 1 - frcols && col < fr->or_right->col - frightcols + 1) { col = fr->or_right->col - frightcols + 1; pp = ATBL(tbl, row, col); } nextcol = col + 1; if (col_hidden[col]) { fieldlen = 0; continue; } fieldlen = fwidth[col]; /* * Set standout if: * * - showing ranges, and not showing cells which need to be filled * in, and not showing cell expressions, and in a range, OR * * - showing cells which need to be filled in and this one is * of that type (has a value and doesn't have an expression, * or it is a string expression), OR * * - showing cells which have expressions and this one does. */ if ((showrange && (!showneed) && (!showexpr) && (row >= minsr) && (row <= maxsr) && (col >= minsc) && (col <= maxsc)) || (showneed && (*pp) && ((*pp)->flags & IS_VALID) && (((*pp)->flags & IS_STREXPR) || !((*pp)->expr))) || (showexpr && (*pp) && ((*pp)->expr)) || (shownote && (*pp) && ((*pp)->nrow >= 0))) { (void) move(r, c); (void) standout(); if (color && has_colors() && (cr = find_crange(row, col))) color_set(cr->r_color, NULL); standlast++; if (!*pp) { /* no cell, but standing out */ (void) printw("%*s", fwidth[col], " "); (void) standend(); if (color && has_colors()) color_set(1, NULL); continue; } else do_stand = 1; } else do_stand = 0; if ((cr = find_crange(row, col)) && color && has_colors()) color_set(cr->r_color, NULL); if ((*pp) && (((*pp)->flags & IS_CHANGED || FullUpdate) || do_stand)) { if (do_stand) { (*pp)->flags |= IS_CHANGED; } else { (void) move(r, c); (*pp)->flags &= ~IS_CHANGED; } /* * Show expression; takes priority over other displays: */ if ((*pp)->cellerror) { if (color && colorerr && has_colors()) color_set(3, NULL); (void) printw("%*.*s", fwidth[col], fwidth[col], (*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"); } else if (showexpr && ((*pp)->expr)) { linelim = 0; editexp(row, col); /* set line to expr */ linelim = -1; showstring(line, /* leftflush = */ 1, /* hasvalue = */ 0, row, col, &nextcol, mxcol, &fieldlen, r, c, fr, frightcols, flcols, frcols); } else { /* * Show cell's numeric value: */ if ((*pp)->flags & IS_VALID) { char field[FBUFLEN]; char *cfmt; int note; *field = '\0'; note = (*pp)->nrow >= 0 ? 1 : 0; cfmt = (*pp)->format ? (*pp)->format : (realfmt[col] >= 0 && realfmt[col] < COLFORMATS && colformat[realfmt[col]]) ? colformat[realfmt[col]] : NULL; if (color && has_colors() && colorneg && (*pp)->v < 0) { if (cr) color_set(((cr->r_color) % CPAIRS) + 1, NULL); else color_set(2, NULL); } if (cfmt) { if (*cfmt == ctl('d')) { time_t v = (time_t) ((*pp)->v); strftime(field, sizeof(field), cfmt + 1, localtime(&v)); } else (void) format(cfmt, precision[col], (*pp)->v, field, sizeof(field)); } else { (void) engformat(realfmt[col], fwidth[col] - note, precision[col], (*pp)->v, field, sizeof(field)); } if ((ssize_t)strlen(field) > fwidth[col]) { for (i = 0; i < fwidth[col]; i++) { if (note) { #ifndef NO_ATTR_GET attr_t attr; short curcolor = 0; if (!i && color && has_colors()) { /* silence warning */ attr_t *attrp = &attr; short *curcolorp = &curcolor; attr_get(attrp, curcolorp, NULL); color_set(4, NULL); } #endif (void)addch('*'); i++; #ifndef NO_ATTR_GET if (!i && color && has_colors()) color_set(curcolor, NULL); #endif } (void)addch('*'); } } else { if (cfmt && *cfmt != ctl('d')) for (i = 0; i < fwidth[col] - (ssize_t)strlen(field) - note; i++) (void)addch(' '); if (note) { #ifndef NO_ATTR_GET attr_t attr; short curcolor = 0; if (color && has_colors()) { /* silence warning */ attr_t *attrp = &attr; short *curcolorp = &curcolor; attr_get(attrp, curcolorp, NULL); color_set(4, NULL); } #endif (void)addch('*'); #ifndef NO_ATTR_GET if (color && has_colors()) color_set(curcolor, NULL); #endif } (void)addstr(field); if (cfmt && *cfmt == ctl('d')) for (i = 0; i < fwidth[col] - (ssize_t)strlen(field) - note; i++) (void)addch(' '); } } /* * Show cell's label string: */ if ((*pp)->label) { showstring((*pp)->label, (*pp)->flags & (IS_LEFTFLUSH|IS_LABEL), (*pp)->flags & IS_VALID, row, col, &nextcol, mxcol, &fieldlen, r, c, fr, frightcols, flcols, frcols); } else /* repaint a blank cell: */ if ((((do_stand || !FullUpdate) && ((*pp)->flags & IS_CHANGED)) || (color && has_colors() && cr && cr->r_color != 1)) && !((*pp)->flags & IS_VALID) && !(*pp)->label) { (void) printw("%*s", fwidth[col], " "); } } /* else */ } else if (!*pp && color && has_colors() && cr && cr->r_color != 1) { move(r, c); color_set(cr->r_color, NULL); printw("%*s", fwidth[col], " "); } if (color && has_colors()) color_set(1, NULL); if (do_stand) { (void) standend(); do_stand = 0; } } r++; } } /* place 'cursor marker' */ if (showcell && (!showneed) && (!showexpr) && (!shownote)) { (void) move(lastmy, lastmx); pp = ATBL(tbl, currow, curcol); if (color && has_colors()) { if ((cr = find_crange(currow, curcol))) color_set(cr->r_color, NULL); else color_set(1, NULL); if (*pp) { if (colorneg && (*pp)->flags & IS_VALID && (*pp)->v < 0) { if (cr) color_set(((cr->r_color) % CPAIRS) + 1, NULL); else color_set(2, NULL); } else if (colorerr && (*pp)->cellerror) color_set(3, NULL); } } repaint(lastmx, lastmy, fwidth[lastcol], A_STANDOUT, 0); if (color && has_colors()) color_set(1, NULL); } repaint(lastmx, RESROW - 1, fwidth[lastcol], 0, A_STANDOUT); repaint(0, lastmy, rescol - 1, 0, A_STANDOUT); (void) move(lastmy, lastmx+fwidth[lastcol]); under_cursor = (inch() & A_CHARTEXT); if (!showcell) (void) addch('<' | (inch() & A_ATTRIBUTES)); (void) move(0, 0); (void) clrtoeol(); if (linelim >= 0) { int ctlchars; for (i = ctlchars = 0; i < linelim; i++) if ((unsigned char) line[i] < ' ') ctlchars++; (void) addch(mode_ind); (void) addch('>'); (void) addch(search_ind); (void) addstr(line); if (!braille || (!message && mode_ind != 'v')) (void) move((linelim+3+ctlchars)/cols, (linelim+3+ctlchars)%cols); else if (message) move(1, 0); else if (braillealt) move(1, 16); else move(lastmy, lastmx); } else { if (showtop) { /* show top line */ register struct ent *p1; int printed = 0; /* printed something? */ (void) printw("%s%d ", coltoa(curcol), currow); if ((p1 = *ATBL(tbl, currow, curcol)) && p1->nrow > -1) printw("{*%s} ", r_name(p1->nrow, p1->ncol, p1->nlastrow, p1->nlastcol)); /* show the current cell's format */ if ((p1) && p1->format) printw("(%s) ", p1->format); else printw("(%d %d %d) ", fwidth[curcol], precision[curcol], realfmt[curcol]); if (p1) { if (p1->expr) { /* has expr of some type */ linelim = 0; editexp(currow, curcol); /* set line to expr */ linelim = -1; } /* * Display string part of cell: */ if ((p1->expr) && (p1->flags & IS_STREXPR)) { if (p1->flags & IS_LABEL) (void) addstr("|{"); else (void) addstr((p1->flags & IS_LEFTFLUSH) ? "<{" : ">{"); (void) addstr(line); (void) addstr("} "); /* and this '}' is for vi % */ printed = 1; } else if (p1->label) { /* has constant label only */ if (p1->flags & IS_LABEL) (void) addstr("|\""); else (void) addstr((p1->flags & IS_LEFTFLUSH) ? "<\"" : ">\""); (void) addstr(p1->label); (void) addstr("\" "); printed = 1; } /* * Display value part of cell: */ if (p1->flags & IS_VALID) { /* has value or num expr */ if ((!(p1->expr)) || (p1->flags & IS_STREXPR)) snprintf(line, sizeof line, "%.15g", p1->v); (void) addch('['); (void) addstr(line); (void) addch(']'); *line = '\0'; /* this is the input buffer ! */ printed = 1; } } if (!printed) (void) addstr("[]"); /* Display if cell is locked */ if (p1 && p1->flags&IS_LOCKED) (void) addstr(" locked"); } if (braille) if (message) move(1, 0); else if (braillealt) move(0, 0); else move(lastmy, lastmx); else if (showcell) move(lines - 1, cols - 1); else (void) move(lastmy, lastmx+fwidth[lastcol]); } if (color && has_colors()) color_set(1, NULL); if (revmsg[0]) { (void) move(0, 0); (void) clrtoeol(); /* get rid of topline display */ (void) printw(revmsg); *revmsg = '\0'; /* don't show it again */ if (braille) if (message) move(1, 0); else if (braillealt) move(0, 0); else move(lastmy, lastmx); else if (showcell) move(lines - 1, cols - 1); else (void) move(lastmy, lastmx+fwidth[lastcol]); } if (color && has_colors()) color_set(1, NULL); if (revmsg[0]) { (void) move(0, 0); (void) clrtoeol(); /* get rid of topline display */ (void) printw(revmsg); *revmsg = '\0'; /* don't show it again */ if (braille) if (message) move(1, 0); else move(lastmy, lastmx); else if (showcell) move(lines - 1, cols - 1); else (void) move(lastmy, lastmx + fwidth[lastcol]); } FullUpdate = FALSE; } /* redraw what is under the cursor from curses' idea of the screen */ void repaint(int x, int y, int len, int attron, int attroff) { while (len-- > 0) { (void) move(y, x); addch((inch() | attron) & ~attroff); x++; } } int seenerr; /* error routine for yacc (gram.y) */ void yyerror(char *err) { if (usecurses) { if (seenerr) return; seenerr++; move(1, 0); clrtoeol(); printw("%s: %.*s<=%s", err, (int)linelim, line, line + linelim); } else fprintf(stderr, "%s: %.*s<=%s\n", err, (int)linelim, line, line + linelim); } #ifdef XENIX2_3 struct termio tmio; #endif void startdisp(void) { #if sun int fd; fd = dup(0); #endif #ifndef MSDOS if (usecurses) { #endif int i; #ifdef TIOCGSIZE { struct ttysize size; if (ioctl(0, TIOCGSIZE, &size) == 0) { lines = size.ts_lines; cols = size.ts_cols; } } #endif #ifdef XENIX2_3 (void) ioctl(fileno(stdin), TCGETA, & tmio); #endif (void) initscr(); start_color(); for (i = 0; i < 8; i++) if (cpairs[i]) init_pair(i + 1, cpairs[i]->fg, cpairs[i]->bg); if (color && has_colors()) bkgdset(COLOR_PAIR(1) | ' '); #if sun close(0); dup(fd); close(fd); #endif (void) clear(); #ifdef VMS VMS_read_raw = 1; #else nonl(); noecho(); cbreak(); #endif initkbd(); scrollok(stdscr, 1); #if defined(SYSV3) && !defined(NOIDLOK) # ifndef IDLOKBAD /* * turn hardware insert/delete on, if possible. * turn on scrolling for systems with SYSVr3.{1,2} (SYSVr3.0 has * this set as the default) */ idlok(stdscr,TRUE); # else /* * This seems to fix (with an empty spreadsheet): * a) Redrawing the bottom half of the screen when you * move between row 9 <-> 10 * b) the highlighted row labels being trash when you * move between row 9 <-> 10 * c) On an xterm on Esix Rev. D+ from eating lines * -goto (or move) a few lines (or more) past the bottom * of the screen, goto (or move) to the top line on the * screen, move upward and the current line is deleted, the * others move up even when they should not, check by * noticing the rows become 2, 3, 40, 41, 42... (etc). */ idlok(stdscr,FALSE); # endif #endif FullUpdate++; #ifndef MSDOS } #endif } void stopdisp(void) { #ifndef MSDOS if (usecurses) { #endif deraw(1); resetkbd(); endwin(); #ifdef XENIX2_3 (void) ioctl(fileno(stdin), TCSETAW, & tmio); #endif #ifndef MSDOS } #endif } /* init curses */ #ifdef VMS void goraw(void) { if (usecurses) { VMS_read_raw = 1; if (color && has_colors()) bkgdset(COLOR_PAIR(1) | ' '); FullUpdate++; } } void deraw(int ClearLastLine) { if (usecurses) { if (ClearLastLine) { if (color && has_colors()) bkgdset(COLOR_PAIR(0) | ' '); (void) move(lines - 1, 0); (void) clrtoeol(); (void) refresh(); } VMS_read_raw = 0; } } #else /* VMS */ void goraw(void) { if (usecurses) { #ifdef HAVE_FIXTERM fixterm(); #else cbreak(); nonl(); noecho (); #endif kbd_again(); if (color && has_colors()) bkgdset(COLOR_PAIR(1) | ' '); FullUpdate++; } } /* clean up curses */ void deraw(int ClearLastLine) { if (usecurses) { if (ClearLastLine) { if (color && has_colors()) bkgdset(COLOR_PAIR(0) | ' '); (void) move(lines - 1, 0); (void) clrtoeol(); (void) refresh(); } #ifdef HAVE_RESETTERM resetterm(); #else nocbreak(); nl(); echo(); #endif resetkbd(); } } #endif /* VMS */ sc-7.16_1.1.2/sort.c000066400000000000000000000071501326046243500137360ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Sorting routines * * Chuck Martin * Originally created: April, 2001 * * $Revision: 7.16 $ */ #include #include #include #include #include #include #include #include "compat.h" #include "sc.h" int compare(const void *row1, const void *row2); static struct sortcrit { int direction, type, column; } *sort; static int howmany; void sortrange(struct ent *left, struct ent *right, char *criteria) { int minr, minc, maxr, maxc, r, c; int *rows, col = 0; int cp = 0; struct ent *p; minr = left->row < right->row ? left->row : right->row; minc = left->col < right->col ? left->col : right->col; maxr = left->row > right->row ? left->row : right->row; maxc = left->col > right->col ? left->col : right->col; sort = scxmalloc((2 * sizeof(struct sortcrit))); rows = scxmalloc((maxr - minr + 1) * sizeof(int)); for (r = minr, c = 0; r <= maxr; r++, c++) rows[c] = r; if (!criteria) { sort[0].direction = 1; sort[0].type = 1; sort[0].column = minc; sort[1].direction = 1; sort[1].type = 0; sort[1].column = minc; howmany = 2; } else for (howmany = 0; criteria[cp]; howmany++) { if (howmany > 1) sort = scxrealloc(sort, (howmany + 1) * (sizeof(struct sortcrit))); switch (criteria[cp++]) { case '+': sort[howmany].direction = 1; break; case '-': sort[howmany].direction = -1; break; default: error("Invalid sort criteria"); return; } switch (criteria[cp++]) { case '#': sort[howmany].type = 0; break; case '$': sort[howmany].type = 1; break; default: error("Invalid sort criteria"); return; } if (criteria[cp]) col = toupper((int)criteria[cp++]) - 'A'; else { error("Invalid sort criteria"); return; } if (criteria[cp] && criteria[cp] != '+' && criteria[cp] != '-') col = (col + 1) * 26 + toupper((int)criteria[cp++]) - 'A'; sort[howmany].column = col; if (col < minc || col > maxc) { error("Invalid sort criteria"); return; } } qsort(rows, maxr - minr + 1, sizeof(int), compare); erase_area(minr, minc, maxr, maxc, 1); sync_ranges(); for (c = 0, p = delbuf[dbidx]; p; p = p->next) { if (rows[c] != p->row) { for (c = 0; c <= maxr - minr && rows[c] != p->row; c++) ; if (c > maxr - minr) { error("sort error"); return; } } p->row = minr + c; } scxfree((char *)sort); scxfree((char *)rows); if (criteria) scxfree(criteria); r = currow; c = curcol; currow = minr; curcol = minc; pullcells('m'); flush_saved(); currow = r; curcol = c; } int compare(const void *row1, const void *row2) { struct ent *p1; struct ent *p2; double diff; int result = 0; int i; for (i = 0; !result && i < howmany; i++) { p1 = *ATBL(tbl, *((const int *) row1), sort[i].column); p2 = *ATBL(tbl, *((const int *) row2), sort[i].column); if (sort[i].type) { if (p1 && p1->label) if (p2 && p2->label) result = strcmp(p1->label, p2->label); else result = -1; else if (p2 && p2->label) result = 1; } else if (p1 && p2 && p1->flags & IS_VALID && p2->flags & IS_VALID) { diff = (p1->v - p2->v); result = (diff > 0 ? 1 : diff < 0 ? -1 : 0); } else if (p1 && p1->flags & IS_VALID) result = -1; else if (p2 && p2->flags & IS_VALID) result = 1; result *= sort[i].direction; } if (!result) result = (*((const int *) row1) - *((const int *) row2)); return (result); } sc-7.16_1.1.2/sres.sed000066400000000000000000000000661326046243500142530ustar00rootroot00000000000000/%token.*S_/!d /%token.*S_\(.*\)/s// { "\1", S_\1 },/ sc-7.16_1.1.2/torev000066400000000000000000000003521326046243500136620ustar00rootroot00000000000000REVISION=`sed -e '/Revision/!D' -e 's/.*$Revision: \(.*\) $.*/\1/' version.c` sed -e s/pname/$name/g -e s/PNAME/$NAME/g \ -e "s%#LIBDIR#%$LIBDIR%g" \ -e "s/#REVISION#/$REVISION/" \ -e 's/\\T[^]*//g;s/\\T//g;s/\\A[^]*//g' $1 sc-7.16_1.1.2/tutorial.sc000066400000000000000000000107031326046243500147730ustar00rootroot00000000000000# This data file was generated by the Spreadsheet Calculator. # You almost certainly shouldn't edit it. define "page5" A81:H100 define "page4" A61:H80 define "page3" A41:H60 define "page2" A21:H40 define "page1" A0:H20 leftstring A1 = "This is a brief sc tutorial, best run in a 24-line window." leftstring A2 = "Type 'q' to exit, ^Z to suspend (w/ Job Control)." leftstring A3 = "^G interrupts a command." leftstring A5 = "Cells are named by their column and row number. For example," leftstring A6 = "Cell A6" leftstring B6 = "Cell B6" leftstring C6 = "Cell C6" leftstring A7 = "Cell A7" leftstring A8 = "Cell A8" leftstring C8 = "Cell C8" leftstring A9 = "Cells range from A0 to ZZ(some number depending on free memory)." leftstring A10 = "Cells can also be named by the user. See 'range names' in the manual." leftstring A11 = "You can move the cursor a couple of different ways:" leftstring B12 = "^n, j and the arrow key go down" leftstring B13 = "^p, k and the arrow key go up" leftstring B14 = "^b, h and the arrow key go left" leftstring B15 = "^f, l and the arrow key go right" leftstring B16 = "You can go directly to a cell by typing 'g' and the cell name. " leftstring B17 = "'g c6' will take you to cell c6." leftstring A18 = "Cells can contain numbers, formulas, or text." leftstring A19 = "Most of the cells on this page contain text." leftstring C20 = "" leftstring A22 = "Cell d22 contains text" leftstring D22 = "Text " leftstring A23 = "Cell d23 contains a number" let D23 = 123.34 leftstring A24 = "Cell d24 contains a formula" let D24 = D23+88 leftstring A26 = "To see what the cell contains, just move the cursor" leftstring A27 = "onto the cell. The contents will show up on line 1 in the brackets." leftstring A29 = "You can enter data into cells like this:" leftstring B30 = "' #include #include #include #include #include #include #include #if defined(REGCOMP) #include #endif #include "compat.h" #include "sc.h" #if defined(RE_COMP) extern char *re_comp(char *s); extern char *re_exec(char *s); #endif #if defined(REGCMP) char *regcmp(); char *regex(); #endif #define istext(a) (isalnum(a) || ((a) == '_')) static void append_line(void); static void back_hist(void); static int back_line(int arg); static int back_word(int arg, int big_word); static void back_space(void); static void change_case(int arg); static void col_0(void); static void cr_line(void); static void del_in_line(int arg, int back_null); static void del_to_end(void); static void doabbrev(void); static void dogoto(void); static void dotab(void); static void dotcmd(void); static int find_char(int arg, int n); static void for_hist(void); static int for_line(int arg, int stop_null); static int for_word(int arg, int end_word, int big_word, int stop_null); static int istart; static void last_col(void); static void match_paren(void); static void readhistfile(FILE *fp); static void rep_char(void); static void replace_in_line(int c); static void replace_mode(void); static void restore_it(void); static void savedot(int c); static void save_hist(void); static void search_again(bool reverse); static void search_hist(void); static void search_mode(char sind); static void stop_edit(void); static int to_char(int arg, int n); static void u_save(int c); static void yank_cmd(int delete, int change); static void yank_chars(register int first, register int last, int delete); static int get_motion(int); int vigetch(void); #ifdef NCURSES_MOUSE_VERSION void mouse_set_pos(int); #endif extern int framerows; /* Rows in current frame */ extern char mode_ind; /* Mode indicator */ extern char search_ind; /* Search indicator */ extern int lcols; /* Spreadsheet Column the cursor was in last */ static char *completethis = NULL; static int search_dir; /* Search direction: forward = 0; back = 1 */ char histfile[PATHLEN] = "~/.sc_history"; /* values for mode below */ #define INSERT_MODE 0 /* Insert mode */ #define EDIT_MODE 1 /* Edit mode */ #define REP_MODE 2 /* Replace mode */ #define SEARCH_MODE 3 /* Get arguments for '/' command */ #define NAVIGATE_MODE 4 /* Navigate the spreadsheet while editing a line */ #define DOTLEN 200 static int mode = INSERT_MODE; static struct hist { unsigned int len; char *histline; } history[HISTLEN + 1]; static int histp = 0; static int lasthist = 0; static int endhist = -1; static int histsessionstart = 0; static int histsessionnew = 0; #ifdef REGCOMP static regex_t preg; static regex_t *last_search = NULL; static int errcode; #else #ifndef RE_COMP static char *last_search = NULL; #endif #endif static char *undo_line = NULL; static unsigned undolen = 0; static int undo_lim; static char dotb[DOTLEN]; static int doti = 0; static int do_dot = 0; static int nosavedot = 1; static int dotarg = 1; static char putbuf[FBUFLEN]; static int findfunc = '\0'; static int findchar = 1; static int finddir = 0; void write_line(int c) { struct frange *fr; struct crange *cr; CLEAR_LINE; if (c != ctl('i')) completethis = NULL; if (mode == EDIT_MODE) { nosavedot = 0; switch (c) { case KEY_BACKSPACE: case (ctl('h')): linelim = back_line(arg); break; case (ctl('i')): dotab(); break; case (ctl('m')): if (search_ind != ' ') search_hist(); else cr_line(); break; case 'v': case (ctl('v')): toggle_navigate_mode(); break; case ESC: stop_edit(); break; case '+': for_hist(); break; case '-': back_hist(); break; case KEY_END: case (ctl('e')): case '$': last_col(); break; case '.': dotcmd(); break; case '!': doshell(); break; case ';': if (findfunc) ungetch(findchar); else break; findchar = 0; if (findfunc == 'f') linelim = find_char(arg, finddir); else linelim = to_char(arg, finddir); break; case ',': if (findfunc) ungetch(findchar); else break; findchar = 0; if (findfunc == 'f') linelim = find_char(arg, -(finddir)); else linelim = to_char(arg, -(finddir)); break; case '~': u_save(c); change_case(arg); break; case '%': match_paren(); break; #ifdef KEY_FIND case KEY_FIND: #endif case '?': case '/': search_mode(c); break; case KEY_HOME: case (ctl('a')): case '0': col_0(); break; case 'A': u_save(c); last_col(); append_line(); break; case 'B': linelim = back_word(arg, 1); break; case 'C': u_save(c); del_to_end(); append_line(); break; case 'D': u_save(c); del_to_end(); break; case 'E': linelim = for_word(arg, 1, 1, 0); break; case 'F': linelim = find_char(arg, -1); break; case 'G': if (histp > 0) histp = lasthist; for_hist(); break; case 'I': u_save(c); col_0(); insert_mode(); break; case 'N': search_again(true); break; case 'P': u_save(c); ins_string(putbuf); linelim = back_line(1); break; case 'R': u_save(c); replace_mode(); break; case 'T': linelim = to_char(arg, -1); break; case 'W': linelim = for_word(arg, 0, 1, 0); break; case 'X': u_save(c); back_space(); break; case 'Y': yank_chars(linelim, strlen(line), 0); break; case 'a': u_save(c); append_line(); break; case 'b': linelim = back_word(arg, 0); break; case 'c': u_save(c); yank_cmd(1, 1); insert_mode(); break; case 'd': u_save(c); yank_cmd(1, 0); break; case 'e': linelim = for_word(arg, 1, 0, 0); break; case 'f': linelim = find_char(arg, 1); break; case KEY_LEFT: case (ctl('b')): case 'h': linelim = back_line(arg); break; case KEY_IC: case 'i': u_save(c); insert_mode(); break; case KEY_DOWN: case 'j': for_hist(); break; case KEY_UP: case 'k': back_hist(); break; case KEY_RIGHT: case (ctl('f')): case ' ': case 'l': linelim = for_line(arg, 0); break; case 'n': search_again(false); break; case 'p': u_save(c); linelim = for_line(1, 1); ins_string(putbuf); linelim = back_line(1); break; case 'q': stop_edit(); break; case 'r': u_save(c); rep_char(); break; case 's': u_save(c); del_in_line(arg, 0); insert_mode(); break; case 't': linelim = to_char(arg, 1); break; case 'u': restore_it(); break; case 'w': linelim = for_word(arg, 0, 0, 0); break; case KEY_DC: case 'x': u_save(c); del_in_line(arg, 1); break; case 'y': yank_cmd(0, 0); break; #ifdef NCURSES_MOUSE_VERSION case KEY_MOUSE: if (getmouse(&mevent) != OK || mevent.y) break; if (mevent.bstate & BUTTON1_CLICKED) mouse_set_pos(0); else if (mevent.bstate & BUTTON1_PRESSED) mouse_set_pos(1); else if (mevent.bstate & BUTTON1_RELEASED) mouse_set_pos(2); break; #endif default: break; } } else if (mode == INSERT_MODE) { if (c == (ctl('m'))) savedot(ESC); else savedot(c); switch (c) { case KEY_BACKSPACE: case (ctl('h')): back_space(); break; case (ctl('i')): dotab(); break; case (ctl('m')): cr_line(); break; case (ctl('v')): toggle_navigate_mode(); break; case KEY_LEFT: case (ctl('b')): if (numeric_field) { if (linelim > 0 && (size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-')) { toggle_navigate_mode(); backcol(1); } else { c = craction; craction = 0; cr_line(); craction = c; backcol(1); } } else { linelim = back_line(arg); istart = linelim; } break; case KEY_RIGHT: case (ctl('f')): if (numeric_field) { if (linelim > 0 && (size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-')) { toggle_navigate_mode(); forwcol(1); } else { c = craction; craction = 0; cr_line(); craction = c; forwcol(1); } } else { linelim = for_line(arg, 1); istart = linelim; } break; case KEY_DOWN: case (ctl('n')): if (numeric_field) { if (linelim > 0 && (size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-')) { toggle_navigate_mode(); forwrow(1); } else { c = craction; craction = 0; cr_line(); craction = c; forwrow(1); } } else { for_hist(); istart = linelim; } break; case KEY_UP: case (ctl('p')): if (numeric_field) { if (linelim > 0 && (size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-')) { toggle_navigate_mode(); backrow(1); } else { c = craction; craction = 0; cr_line(); craction = c; backrow(1); } } else { back_hist(); istart = linelim; } break; case KEY_HOME: case (ctl('a')): col_0(); break; case KEY_END: case (ctl('e')): last_col(); break; case ESC: ins_in_line(0); edit_mode(); break; /* '\035' is ^], which expands abbreviations without inserting another * character in the line */ case '\035': if (linelim > 0) doabbrev(); break; #ifdef NCURSES_MOUSE_VERSION case KEY_MOUSE: if (getmouse(&mevent) != OK || mevent.y) break; if (mevent.bstate & BUTTON1_CLICKED) mouse_set_pos(0); else if (mevent.bstate & BUTTON1_PRESSED) mouse_set_pos(1); else if (mevent.bstate & BUTTON1_RELEASED) mouse_set_pos(2); break; #endif default: ins_in_line(c); break; } } else if (mode == SEARCH_MODE) { switch (c) { case KEY_BACKSPACE: case (ctl('h')): back_space(); break; case (ctl('m')): search_hist(); break; case ESC: ins_in_line(0); edit_mode(); break; /* '\035' is ^], which expands abbreviations without inserting another * character in the line */ case '\035': if (linelim > 0) doabbrev(); break; default: ins_in_line(c); break; } } else if (mode == REP_MODE) { savedot(c); switch (c) { case KEY_BACKSPACE: case (ctl('h')): if (linelim >= 0 && (size_t)linelim > strlen(undo_line)) back_space(); else { linelim = back_line(1); *(line+linelim) = *(undo_line+linelim); } break; case (ctl('m')): cr_line(); break; case ESC: edit_mode(); break; default: replace_in_line(c); break; } } else if (mode == NAVIGATE_MODE) { switch (c) { case '.': case ':': case (ctl('i')): if (!showrange) { toggle_navigate_mode(); startshow(); } else if ((size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-' || (line[linelim - 1] == ' ' && line[linelim - 2] == '='))) { ins_string("@sum("); showdr(); ins_in_line(')'); } else { showdr(); ins_in_line(' '); } break; case ' ': if (showrange) { showdr(); ins_in_line(' '); toggle_navigate_mode(); } else { forwcol(arg); } break; case '+': case '-': if (!showrange) { ins_string(v_name(currow, curcol)); ins_in_line(c); } else if ((size_t)linelim == strlen(line) && (line[linelim - 1] == '+' || line[linelim - 1] == '-' || (line[linelim - 1] == ' ' && line[linelim - 2] == '='))) { ins_string("@sum("); showdr(); ins_in_line(')'); ins_in_line(c); toggle_navigate_mode(); } else { showdr(); ins_in_line(')'); ins_in_line(c); } break; case (ctl('m')): if (!showrange) { ins_string(v_name(currow, curcol)); toggle_navigate_mode(); } else { toggle_navigate_mode(); cr_line(); } break; case 'v': { /* insert variable value */ struct ent *p = *ATBL(tbl, currow, curcol); char temp[100]; if (p && p->flags & IS_VALID) { snprintf(temp, sizeof temp, "%.*f", precision[curcol], p->v); ins_string(temp); toggle_navigate_mode(); } } break; case 'c': if ((cr = find_crange(currow, curcol))) { ins_string(r_name(cr->r_left->row, cr->r_left->col, cr->r_right->row, cr->r_right->col)); toggle_navigate_mode(); ins_in_line(' '); showrange = 0; } break; case 'f': if ((fr = find_frange(currow, curcol))) { ins_string(r_name(fr->or_left->row, fr->or_left->col, fr->or_right->row, fr->or_right->col)); toggle_navigate_mode(); ins_in_line(' '); showrange = 0; } break; case 'r': if ((fr = find_frange(currow, curcol))) { ins_string(r_name(fr->ir_left->row, fr->ir_left->col, fr->ir_right->row, fr->ir_right->col)); toggle_navigate_mode(); ins_in_line(' '); showrange = 0; } break; case KEY_LEFT: case 'h': backcol(arg); break; case KEY_RIGHT: case 'l': forwcol(arg); break; case KEY_DOWN: case (ctl('n')): case 'j': forwrow(arg); break; case KEY_UP: case (ctl('p')): case 'k': backrow(arg); break; case 'q': case ctl('g'): case (ctl('v')): case ESC: toggle_navigate_mode(); showrange = 0; break; case 'H': backcol(curcol - stcol + 2); break; case KEY_NPAGE: /* next page */ case (ctl('f')): case 'J': { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; forwrow(arg * ps); strow = strow + (arg * ps); FullUpdate++; } break; case KEY_PPAGE: /* previous page */ case (ctl('b')): case 'K': { int ps; ps = pagesize ? pagesize : (LINES - RESROW - framerows)/2; backrow(arg * ps); strow = strow - (arg * ps); if (strow < 0) strow = 0; FullUpdate++; } break; case 'L': forwcol(lcols - (curcol - stcol) + 1); break; case (ctl('a')): case KEY_HOME: gohome(); break; case '0': leftlimit(); break; case '$': rightlimit(); break; case '^': gototop(); break; case '#': gotobottom(); break; case 'o': if (showrange) { int r = currow; int cc = curcol; currow = showsr; showsr = r; curcol = showsc; showsc = cc; rowsinrange = 1; colsinrange = fwidth[curcol]; } break; case 'm': markcell(); break; case '`': case '\'': dotick(c); break; case '*': if (nmgetch() == '*') gotonote(); break; case 'g': dogoto(); break; case 'n': go_last(); break; case 'w': { register struct ent *p; while (--arg>=0) { do { if (curcol < maxcols - 1) curcol++; else { if (currow < maxrows - 1) { while(++currow < maxrows - 1 && row_hidden[currow]) ; curcol = 0; } else { error("At end of table"); break; } } } while(col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } rowsinrange = 1; colsinrange = fwidth[curcol]; break; } case 'b': { register struct ent *p; while (--arg>=0) { do { if (curcol) curcol--; else { if (currow) { while(--currow && row_hidden[currow]) ; curcol = maxcols - 1; } else { error("At start of table"); break; } } } while (col_hidden[curcol] || !VALID_CELL(p, currow, curcol)); } rowsinrange = 1; colsinrange = fwidth[curcol]; break; } case 'C': if (braille) braillealt ^= 1; break; } } } void edit_mode(void) { mode_ind = 'e'; mode = EDIT_MODE; if (linelim < 0) /* -1 says stop editing, ...so we still aren't */ return; numeric_field = 0; linelim = back_line(1); } void insert_mode(void) { mode_ind = 'i'; mode = INSERT_MODE; istart = linelim; } static void search_mode(char sind) { if (search_ind == ' ') { /* * This back and forth movement through the history is just a quick * way to force the current command to be saved in history[0].histline, * allocating space for it if necessary. The command will be copied * back into line by search_hist() before the search is done. - CRM */ back_hist(); for_hist(); line[0] = '\0'; linelim = 0; mode_ind = 'i'; search_ind = sind; search_dir = sind == '/' ? 1 : 0; mode = SEARCH_MODE; istart = linelim; } } static void replace_mode(void) { mode_ind = 'R'; mode = REP_MODE; } void toggle_navigate_mode(void) { static char prev_mode = NAVIGATE_MODE; int limtmp; switch (prev_mode) { case INSERT_MODE: if (mode == NAVIGATE_MODE) { prev_mode = NAVIGATE_MODE; insert_mode(); break; } else return; case EDIT_MODE: if (mode == NAVIGATE_MODE) { prev_mode = NAVIGATE_MODE; limtmp = linelim; edit_mode(); linelim = limtmp; break; } else return; case NAVIGATE_MODE: prev_mode = mode; mode_ind = 'v'; mode = NAVIGATE_MODE; break; default: prev_mode = NAVIGATE_MODE; break; } } void dotab(void) { static struct range *firstmatch; static struct range *lastmatch; static struct range *nextmatch; int len; if ((linelim > 0 && isalnum((int)line[linelim-1])) || line[linelim-1] == '_' || (completethis && line[linelim-1] == ' ')) { if (!completethis) { for (completethis = line + linelim - 1; isalnum((int)*completethis) || *completethis == '_'; completethis--) /* */; completethis++; len = line + linelim - completethis; if (!find_range(completethis, -len, NULL, NULL, &lastmatch)) { firstmatch = lastmatch; while (firstmatch->r_next && !strncmp(completethis, firstmatch->r_next->r_name, len)) firstmatch = firstmatch->r_next; nextmatch = firstmatch; } else nextmatch = NULL; } if (nextmatch) { len = line + linelim - completethis; memmove(completethis, line + linelim, strlen(line) - linelim + 1); linelim -= len; ins_string(nextmatch->r_name); if (*(completethis - 1) == ' ' && *(line + linelim) != ' ') ins_in_line(' '); if (nextmatch == lastmatch) nextmatch = firstmatch; else nextmatch = nextmatch->r_prev; } } else startshow(); } /* show the current range (see ^I), we are moving around to define a range */ void startshow(void) { showrange = 1; showsr = currow; showsc = curcol; toggle_navigate_mode(); } /* insert the range we defined by moving around the screen, see startshow() */ void showdr(void) { int minsr, minsc, maxsr, maxsc; char r[12]; struct frange *fr = find_frange(currow, curcol); if (showrange == SHOWROWS) { minsr = showsr < currow ? showsr : currow; minsc = fr ? fr->or_left->col : 0; maxsr = showsr > currow ? showsr : currow; maxsc = fr ? fr->or_right->col : maxcols; toggle_navigate_mode(); snprintf(r, sizeof r, "%d:%d", minsr, maxsr); ins_string(r); } else if (showrange == SHOWCOLS) { minsr = 0; minsc = showsc < curcol ? showsc : curcol; maxsr = maxrows; maxsc = showsc > curcol ? showsc : curcol; strlcpy(r, coltoa(minsc), sizeof r); strlcat(r, ":", sizeof r); strlcat(r, coltoa(maxsc), sizeof r); toggle_navigate_mode(); ins_string(r); } else { minsr = showsr < currow ? showsr : currow; minsc = showsc < curcol ? showsc : curcol; maxsr = showsr > currow ? showsr : currow; maxsc = showsc > curcol ? showsc : curcol; toggle_navigate_mode(); ins_string(r_name(minsr, minsc, maxsr, maxsc)); } showrange = 0; } /* dot command functions. Saves info so we can redo on a '.' command */ static void savedot(int c) { if (do_dot || nosavedot || (c == '\n')) return; if (doti == 0) dotarg = arg; if (doti < DOTLEN-1) { if (c > 255) { if (doti < DOTLEN-2) { dotb[doti++] = c/256; c %= 256; } else return; } dotb[doti++] = c; dotb[doti] = '\0'; } } static void dotcmd(void) { static int dotcalled = 0; int c; if (dotcalled) /* stop recursive calling of dotcmd() */ return; do_dot = 1; doti = 0; if (arg == 1) arg = dotarg; else dotarg = arg; while (dotb[doti] != '\0') { if ((c = dotb[doti++]) < 4) c = c * 256 + dotb[doti++]; dotcalled = 1; write_line(c); } do_dot = 0; doti = 0; dotcalled = 0; } int vigetch(void) { int c; if (do_dot) { if (dotb[doti] != '\0') { return (dotb[doti++]); } else { do_dot = 0; doti = 0; return (nmgetch()); } } update(1); c = nmgetch(); return (c); } /* saves the current line for possible use by an undo cmd */ static void u_save(int c) { if (strlen(line)+1 > undolen) { undolen = strlen(line)+40; undo_line = scxrealloc(undo_line, undolen); } strlcpy(undo_line, line, undolen); undo_lim = linelim; /* reset dot command if not processing it. */ if (!do_dot) { doti = 0; savedot(c); } } /* Restores the current line saved by u_save() */ static void restore_it(void) { static char *tempc = NULL; static unsigned templen = 0; int tempi; if ((undo_line == NULL) || (*undo_line == '\0')) return; if (strlen(line)+1 > templen) { templen = strlen(line) + 40; tempc = scxrealloc(tempc, templen); } strlcpy(tempc, line, templen); tempi = linelim; strlcpy(line, undo_line, sizeof line); linelim = undo_lim; strlcpy(undo_line, tempc, undolen); undo_lim = tempi; } /* This command stops the editing process. */ static void stop_edit(void) { if (search_ind != ' ') { search_ind = ' '; strlcpy(line, history[0].histline, sizeof line); write_line('G'); } else { showrange = 0; numeric_field = 0; linelim = -1; (void) move(1, 0); (void) clrtoeol(); } } /* * Motion commands. Forward motion commands take an argument * which, when set, cause the forward motion to continue onto * the null at the end of the line instead of stopping at the * the last character of the line. */ static int for_line(int a, int stop_null) { ssize_t cpos = linelim; if (linelim < 0) return (linelim); else if (a >= 0 && (size_t)(linelim + a) <= strlen(line)) cpos += a; else cpos = strlen(line); if (cpos > 0 && (size_t)cpos == strlen(line) && !stop_null) return (cpos - 1); else return (cpos); } /* If end_word is non-zero, go to next end-of-word. Otherwise, go to next * beginning-of-word. */ static int for_word(int a, int end_word, int big_word, int stop_null) { register int c; ssize_t cpos; cpos = linelim; while (cpos >= 0 && (size_t)cpos < strlen(line) && a--) { if (end_word) cpos++; if (line[cpos] == ' ') { while (line[cpos] == ' ') cpos++; if (cpos > 0 && line[cpos] == '\0') --cpos; if (!end_word) continue; } if (big_word) while ((c = line[cpos]) && c != ' ') cpos++; else if (istext((int)line[cpos])) { while ((c = line[cpos]) && istext(c)) cpos++; } else { while ((c = line[cpos]) && !istext(c) && c != ' ') cpos++; } if (end_word) cpos--; else while (line[cpos] == ' ') cpos++; if (cpos > 0 && line[cpos] == '\0' && !stop_null) --cpos; } return (cpos); } static int back_line(int a) { if (linelim > a) return (linelim - a); else return (0); } static int back_word(int a, int big_word) { register int c; register int cpos; cpos = linelim; while (cpos > 0 && a--) { if (line[cpos] == ' ') { /* Skip white space */ while (cpos > 0 && line[cpos] == ' ') --cpos; } else if (cpos > 0 && (line[cpos-1] == ' ' || ( istext((int)line[cpos]) && !istext((int)line[cpos-1])) || (!istext((int)line[cpos]) && istext((int)line[cpos-1])))) { /* Started on the first char of a word - back up to prev. word */ --cpos; while (cpos > 0 && line[cpos] == ' ') --cpos; } /* Skip across the word - goes 1 too far */ if (big_word) while (cpos > 0 && (c = line[cpos]) && c != ' ') --cpos; else if (istext((int)line[cpos])) { while (cpos > 0 && (c = line[cpos]) && istext(c)) --cpos; } else { while (cpos > 0 && (c = line[cpos]) && !istext(c) && c != ' ') --cpos; } /* We are done - fix up the one too far */ if (cpos > 0 && line[cpos] && line[cpos+1]) cpos++; } return(cpos); } /* Text manipulation commands */ /* If back_null is set, back up if the deletion leaves you at the null * line terminator. Otherwise, don't. */ static void del_in_line(int a, int back_null) { register int len, i; if (linelim >= 0) { len = strlen(line); if (a > len - linelim) a = len - linelim; if (linelim == len && linelim > 0) linelim--; strlcpy(putbuf, line + linelim, a); putbuf[a] = '\0'; for (i = linelim; i < len; i++) line[i] = line[i+a]; } if (back_null && linelim > 0 && line[linelim] == '\0') --linelim; } void ins_in_line(int c) { int i, len; static int inabbr; if (c < 256) { if (linelim < 0 && c > 0) { *line = '\0'; linelim = 0; } if (!inabbr && linelim > 0 && !(isalpha(c) || isdigit(c) || c == '_')) { inabbr++; doabbrev(); inabbr--; } if (c > 0) { len = strlen(line); for (i = len; i >= linelim; --i) line[i+1] = line[i]; line[linelim++] = c; line[len+1] = '\0'; } } } void ins_string(char *s) { while (*s) ins_in_line(*s++); } void doabbrev(void) { int len, pos; struct abbrev *a; struct abbrev *prev; if (istart < 0 || linelim < 2) return; if (!(isalpha((int)line[linelim - 1]) || isdigit((int)line[linelim - 1]) || line[linelim - 1] == '_') || !(mode == INSERT_MODE || mode == SEARCH_MODE) || istart >= linelim) return; pos = linelim - 2; if (isalpha((int)line[pos]) || isdigit((int)line[pos]) || line[pos] == '_') { for (; pos >= istart; pos--) if (!(isalpha((int)line[pos]) || isdigit((int)line[pos]) || line[pos] == '_')) break; } else if (line[pos] != ' ') for (; pos >= istart; pos--) if (isalpha((int)line[pos]) || isdigit((int)line[pos]) || line[pos] == '_' || line[pos] == ' ') break; pos++; if (istart && pos == istart) { if (isalpha((int)line[pos]) || isdigit((int)line[pos]) || line[pos] == '_') { if (isalpha((int)line[--pos]) || isdigit((int)line[pos]) || line[pos] == '_') return; } else { if (!(isalpha((int)line[--pos]) || isdigit((int)line[pos]) || line[pos] == '_' || line[pos] == ' ')) return; } pos++; } len = linelim - pos; if (len && (a = find_abbr(line + pos, len, &prev))) { if (len > 1 || pos == 0 || line[pos-1] == ' ') { linelim = pos; del_in_line(len, 0); ins_string(a->exp); } } } static void append_line(void) { register int i; i = linelim; if (i >= 0 && line[i]) linelim++; insert_mode(); } static void change_case(int a) { if (linelim < 0) { linelim = 0; *line = '\0'; } while (a--) { if (islower((int)line[linelim])) line[linelim] = toupper((int)line[linelim]); else if (isupper((int)line[linelim])) line[linelim] = tolower((int)line[linelim]); linelim = for_line(1, 0); } } static void rep_char(void) { int c; if (linelim < 0) { linelim = 0; *line = '\0'; } c = vigetch(); savedot(c); if (c < 256 && c!= ESC && c != ctl('g')) { if (line[linelim] == '\0') line[linelim+1] = '\0'; line[linelim] = c; } } static void replace_in_line(int c) { register int len; if (c < 256) { if (linelim < 0) { linelim = 0; *line = '\0'; } len = strlen(line); line[linelim++] = c; if (linelim > len) line[linelim] = '\0'; } } static void back_space(void) { if (linelim == 0) return; if (line[linelim] == '\0') { linelim = back_line(1); del_in_line(1, 1); linelim = strlen(line); } else { linelim = back_line(1); del_in_line(1, 1); } if (linelim < istart) istart = linelim; } /* Setting change to 1 makes `w' act like `e' so that `cw' will act like * `ce', just as in vi. Setting change to 0 causes `w' to act as usual. */ static int get_motion(int change) { int c; int arg2 = 0; c = vigetch(); if (c == '0') { savedot(c); return (0); } while (c >= '0' && c <= '9') { arg2 = 10 * arg2 + c - '0'; c = vigetch(); } if (!arg2) arg2++; arg *= arg2; if (!nosavedot) { savedot(c); dotarg = arg; } switch (c) { case '$': return (strlen(line)); case 'b': return (back_word(arg, 0)); case 'B': return (back_word(arg, 1)); case 'c': return (change ? -1 : linelim); case 'd': return (!change ? -1 : linelim); case 'e': return (for_word(arg, 1, 0, 1) + 1); case 'E': return (for_word(arg, 1, 1, 1) + 1); case 'f': return ((c = find_char(arg, 1)) == linelim ? c : c + 1); case 'F': return (find_char(arg, -1)); case 'h': return (back_line(arg)); case 'l': return (for_line(arg, 1)); case 't': return ((c = to_char(arg, 1)) == linelim ? c : c + 1); case 'T': return (to_char(arg, -1)); case 'w': return (for_word(arg, change, 0, 1) + change); case 'W': return (for_word(arg, change, 1, 1) + change); default: return (linelim); } } static void yank_cmd(int delete, int change) { int cpos; if ((cpos = get_motion(change)) == -1) { cpos++; linelim = strlen(line); } yank_chars(cpos, linelim, delete); } static void yank_chars(register int first, register int last, int delete) { int temp; if (first == last) return; if (last < first) { temp = last; last = first; first = temp; } linelim = first; strlcpy(putbuf, line + first, last - first); putbuf[last - first] = '\0'; if (delete) memmove(line + first, line + last, strlen(line + last) + 1); } static void del_to_end(void) { if (linelim < 0) return; strlcpy(putbuf, line + linelim, sizeof putbuf); line[linelim] = '\0'; linelim = back_line(1); } static void cr_line(void) { struct frange *fr; ins_in_line(0); insert_mode(); numeric_field = 0; if (linelim == -1) { /* '\n' alone will put you into insert mode */ *line = '\0'; /* unless numeric and craction are both set */ linelim = 0; if (numeric && craction) cellassign = 1; else return; } save_hist(); nosavedot = 1; linelim = 0; (void) yyparse(); showrange = 0; linelim = -1; if (cellassign) { cellassign = 0; switch (craction) { case CRROWS: if ((rowlimit >= 0) && (currow >= rowlimit)) { forwcol(1); currow = 0; } else { if ((fr = find_frange(currow, curcol))) { forwrow(1); if (currow > fr->ir_right->row) { backrow(1); if (autowrap) { forwcol(1); currow = fr->ir_left->row; if (row_hidden[currow]) forwrow(1); if (curcol > fr->ir_right->col) { backcol(1); if (autoinsert) insertcol(1, 1); else { currow = fr->ir_right->row; if (row_hidden[currow]) backrow(1); } } } else if (autoinsert) insertrow(1, 1); } } else forwrow(1); } break; case CRCOLS: if ((collimit >= 0) && (curcol >= collimit)) { forwrow(1); curcol = 0; } else { if ((fr = find_frange(currow, curcol))) { forwcol(1); if (curcol > fr->ir_right->col) { backcol(1); if (autowrap) { forwrow(1); curcol = fr->ir_left->col; if (col_hidden[curcol]) forwcol(1); if (currow > fr->ir_right->row) { backrow(1); if (autoinsert) insertrow(1, 1); else { curcol = fr->ir_right->col; if (col_hidden[curcol]) backcol(1); } } } else if (autoinsert) insertcol(1, 1); } } else forwcol(1); } break; default: break; } } } void doshell(void) { /* * "! command" executes command * "!" forks a shell * "!!" repeats last command */ #if VMS || MSDOS error("Not implemented on VMS or MS-DOS"); #else /* VMS */ char *shl; int pid, temp; char cmd[MAXCMD]; static char lastcmd[MAXCMD]; if (!(shl = getenv("SHELL"))) shl = "/bin/sh"; deraw(1); (void) fputs("! ", stdout); (void) fflush(stdout); (void) fgets(cmd, MAXCMD, stdin); cmd[strlen(cmd) - 1] = '\0'; /* clobber \n */ if (strcmp(cmd,"!") == 0) /* repeat? */ strlcpy(cmd, lastcmd, sizeof cmd); else strlcpy(lastcmd, cmd, sizeof lastcmd); if (modflg) { (void) puts("[No write since last change]"); (void) fflush(stdout); } if (!(pid = fork())) { (void) signal(SIGINT, SIG_DFL); /* reset */ if (strlen(cmd)) execl(shl, shl, "-c", cmd, (char *)NULL); else execl(shl, shl, (char *)NULL); exit (-127); } while (pid != wait(&temp)); (void) printf("Press any key to continue "); fflush(stdout); cbreak(); (void)getch(); goraw(); clear(); #endif /* VMS */ } /* History functions */ static void save_hist(void) { if (!lasthist || strcmp(history[lasthist].histline, line)) { if (lasthist < 0) lasthist = 1; else lasthist = lasthist % HISTLEN + 1; if (lasthist > endhist) endhist = lasthist; if (history[lasthist].len < strlen(line) + 1) { history[lasthist].len = strlen(line) + 40; history[lasthist].histline = scxrealloc(history[lasthist].histline, history[lasthist].len); } strlcpy(history[lasthist].histline, line, history[lasthist].len); histsessionnew++; } if (history[0].histline) { scxfree(history[0].histline); history[0].histline = NULL; history[0].len = 0; } histp = 0; } static void for_hist(void) { if (histp == 0) { last_col(); return; } if (histp == lasthist) histp = 0; else histp = histp % endhist + 1; if (lasthist >= 0) { strlcpy(line, history[histp].histline, sizeof line); last_col(); } if (histp) { error("History line %d", endhist - lasthist + histp); } else { CLEAR_LINE; } } static void back_hist(void) { if (histp == 0) { if (history[0].len < strlen(line) + 1) { history[0].len = strlen(line) + 40; history[0].histline = scxrealloc(history[0].histline, history[0].len); } strlcpy(history[0].histline, line, history[0].len); if (lasthist >= 0) histp = lasthist; } else if (histp == 1) { if (endhist != lasthist) histp = endhist; } else if (histp != ((lasthist + 1) % (endhist + 1))) histp--; if (lasthist >= 0) { strlcpy(line, history[histp].histline, sizeof line); last_col(); } if (histp) { error("History line %d", endhist - lasthist + histp); } else { CLEAR_LINE; } } static void search_hist(void) { #ifdef RECOMP char *tmp; #endif #if !defined(REGCOMP) && !defined(RE_COMP) && !defined(REGCMP) static unsigned lastsrchlen = 0; #endif if (linelim < 1) { linelim = 0; edit_mode(); return; } #if defined REGCOMP if (last_search) regfree(last_search); else last_search = &preg; if ((errcode = regcomp(last_search, line, REG_EXTENDED))) { char *tmp = scxmalloc(160); regerror(errcode, last_search, tmp, sizeof(tmp)); error(tmp); scxfree(tmp); return; } #elif defined RE_COMP if ((tmp = re_comp(line)) != NULL) { error(tmp); return; } #elif defined REGCMP free(last_search); if ((last_search = regcmp(line, NULL)) == NULL) { error("Invalid search string"); return; } #else if (strlen(line) + 1 > lastsrchlen) { lastsrchlen = strlen(line) + 40; last_search = scxrealloc(last_search, lastsrchlen); } strlcpy(last_search, line, lastsrchlen); #endif strlcpy(line, history[0].histline, sizeof line); search_again(false); if (mode != EDIT_MODE) edit_mode(); search_ind = ' '; } static void search_again(bool reverse) { int prev_match; int found_it = 0; #if !defined(REGCOMP) && !defined(RE_COMP) && !defined(REGCMP) char *look_here; int do_next; #endif #if defined REGCOMP if (last_search == NULL) return; #elif !defined(RE_COMP) if (last_search == NULL || *last_search == '\0') return; #endif prev_match = histp > 0 ? histp : 0; CLEAR_LINE; do { if (lasthist > 0) { if (!(search_dir ^ reverse) && histp != lasthist) if (histp <= 0) { histp = ((lasthist + 1) % endhist); strlcpy(line, history[histp].histline, sizeof line); } else for_hist(); else if ((search_dir ^ reverse) && histp != ((lasthist + 1) % endhist)) back_hist(); else { histp = 0; strlcpy(line, history[0].histline, sizeof line); last_col(); } } else break; if (histp == prev_match) { if (histp <= 0) { error("No matches found"); break; } } if (histp <= 0) { if (search_dir ^ reverse) back_hist(); else { histp = ((lasthist + 1) % endhist); strlcpy(line, history[histp].histline, sizeof line); } } found_it = 0; #if defined REGCOMP if (regexec(last_search, line, 0, NULL, 0) == 0) found_it++; #elif defined RE_COMP if (re_exec(line) != 0) found_it++; #elif defined REGCMP if (regex(last_search, line) != NULL) found_it++; #else look_here = line; do_next = 0; while ((look_here = (char *)strchr(look_here, *last_search)) != NULL && !found_it && !do_next) { if (strncmp(look_here, last_search, strlen(last_search)) == 0) found_it++; else if (look_here < line + strlen(line) - 1) look_here++; else do_next++; } #endif if (histp == prev_match) break; } while (!found_it); if (found_it) { error("History line %d", endhist - lasthist + histp); } else { error("No matches found"); } edit_mode(); linelim = strlen(line) - 1; } #if !defined(MSDOS) void write_hist(void) { int i; FILE *fp, *tmpfp = NULL; if (!*histfile) return; if (histsessionnew < HISTLEN) { /* write the new history for this session to a tmp file */ tmpfp = tmpfile(); for (i = 1; i <= histsessionnew; i++) { histsessionstart = histsessionstart % endhist + 1; if (history[histsessionstart].len > 40) fprintf(tmpfp, "%s\n", history[histsessionstart].histline); } fseek(tmpfp, 0, SEEK_SET); /* re-read the main history, then read back in the saved session hist*/ histp = 0; lasthist = 0; endhist = -1; read_hist(); readhistfile(tmpfp); } /* now write to whole lot out to the proper save file */ if (findhome(histfile, sizeof histfile) && (fp = fopen(histfile, "w")) != NULL) { for (i = 1; i <= endhist; i++) { lasthist = lasthist % endhist + 1; if (history[lasthist].len > 40) fprintf(fp, "%s\n", history[lasthist].histline); } fclose(fp); } } static void readhistfile(FILE *fp) { if (!*histfile) return; while (fgets(line, FBUFLEN, fp)) { line[strlen(line)-1] = '\0'; /* chop the \n */ save_hist(); } fclose(fp); } void read_hist(void) { FILE *fp; if (!*histfile) return; if (findhome(histfile, sizeof histfile) && (fp = fopen(histfile, "r")) != NULL) readhistfile(fp); histsessionstart = lasthist; histsessionnew = 0; } #endif static void col_0(void) { linelim = 0; } static void last_col(void) { linelim = strlen(line); if (linelim > 0 && mode_ind == 'e') --linelim; } static int find_char(int a, int n) { register int i; if (findchar) finddir = n; findchar = vigetch(); if (doti > 0) switch (dotb[doti - 1]) { case 'f': case 'F': case 't': case 'T': savedot(findchar); default: break; } i = linelim; while (a--) { i += n; while (i >= 0 && line[i] && line[i] != findchar) i += n; if (i < 0 || !line[i]) { i = linelim; break; } } findfunc = 'f'; return i; } static int to_char(int a, int n) { register int i; int tmp = linelim; if (linelim + n >= 0 && (size_t)(linelim + n) < strlen(line)) linelim += n; i = find_char(a, n); if (i != linelim) i -= n; linelim = tmp; findfunc = 't'; return (i); } static void match_paren(void) { int nest = 1; int tmp = linelim; if (line[linelim] == '(') { while (nest && ++linelim >= 0 && line[linelim]) { if (line[linelim] == '(') nest++; else if (line[linelim] == ')') nest--; } if (line[linelim] != ')') linelim = tmp; } else if (line[linelim] == ')') { while (nest && --linelim >= 0 && line[linelim]) { if (line[linelim] == ')') nest++; else if (line[linelim] == '(') nest--; } if (line[linelim] != '(') linelim = tmp; } } /* If save is 0, remember the current position. Otherwise, if the current * cell has changed since the last remember(0), save the remembered location * for the `, ', and c comands. */ void remember(int save) { static int remrow, remcol, remstrow, remstcol; if (save && (currow != remrow || curcol != remcol || strow != remstrow || stcol != remstcol)) { savedrow[0] = remrow; savedcol[0] = remcol; savedstrow[0] = remstrow; savedstcol[0] = remstcol; } else { remrow = currow; remcol = curcol; remstrow = strow; remstcol = stcol; } } void gohome(void) { struct frange *fr; remember(0); if ((fr = find_frange(currow, curcol))) { if (currow >= fr->ir_left->row && currow <= fr->ir_right->row && curcol >= fr->ir_left->col && curcol <= fr->ir_right->col && (currow > fr->ir_left->row || curcol > fr->ir_left->col)) { currow = fr->ir_left->row; curcol = fr->ir_left->col; } else if (currow > fr->or_left->row || curcol > fr->or_left->col) { currow = fr->or_left->row; curcol = fr->or_left->col; } else { currow = 0; curcol = 0; } } else { currow = 0; curcol = 0; } rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); FullUpdate++; } void leftlimit(void) { struct frange *fr; remember(0); if ((fr = find_frange(currow, curcol))) { if (currow >= fr->ir_left->row && currow <= fr->ir_right->row && curcol > fr->ir_left->col && curcol <= fr->ir_right->col) curcol = fr->ir_left->col; else if (curcol > fr->or_left->col && curcol <= fr->or_right->col) curcol = fr->or_left->col; else curcol = 0; } else curcol = 0; rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); } void rightlimit(void) { register struct ent *p; struct frange *fr; remember(0); if ((fr = find_frange(currow, curcol))) { if (currow >= fr->ir_left->row && currow <= fr->ir_right->row && curcol >= fr->ir_left->col && curcol < fr->ir_right->col) curcol = fr->ir_right->col; else if (curcol >= fr->or_left->col && curcol < fr->or_right->col) curcol = fr->or_right->col; else { curcol = maxcols - 1; while (!VALID_CELL(p, currow, curcol) && curcol > fr->or_right->col) curcol--; if ((fr = find_frange(currow, curcol))) curcol = fr->or_right->col; } } else { curcol = maxcols - 1; while (!VALID_CELL(p, currow, curcol) && curcol > 0) curcol--; if ((fr = find_frange(currow, curcol))) curcol = fr->or_right->col; } rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); } void gototop(void) { struct frange *fr; remember(0); if ((fr = find_frange(currow, curcol))) { if (curcol >= fr->ir_left->col && curcol <= fr->ir_right->col && currow > fr->ir_left->row && currow <= fr->ir_right->row) currow = fr->ir_left->row; else if (currow > fr->or_left->row && currow <= fr->or_right->row) currow = fr->or_left->row; else currow = 0; } else currow = 0; rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); } void gotobottom(void) { register struct ent *p; struct frange *fr; remember(0); if ((fr = find_frange(currow, curcol))) { if (curcol >= fr->ir_left->col && curcol <= fr->ir_right->col && currow >= fr->ir_left->row && currow < fr->ir_right->row) currow = fr->ir_right->row; else if (currow >= fr->or_left->row && currow < fr->or_right->row) currow = fr->or_right->row; else { currow = maxrows - 1; while (!VALID_CELL(p, currow, curcol) && currow > fr->or_right->row) currow--; if ((fr = find_frange(currow, curcol))) currow = fr->or_right->row; } } else { currow = maxrows - 1; while (!VALID_CELL(p, currow, curcol) && currow > 0) currow--; if ((fr = find_frange(currow, curcol))) currow = fr->or_right->row; } rowsinrange = 1; colsinrange = fwidth[curcol]; remember(1); } static void dogoto(void) { static char *tempc = NULL; static unsigned templen = 0; int tempi; if (strlen(line) + 1 > templen) { templen = strlen(line) + 40; tempc = scxrealloc(tempc, templen); } strlcpy(tempc, line, templen); tempi = linelim; /* Can't switch back to navigate mode if insert_mode() is used here * instead of toggle_navigate_mode(), which is what we want when doing * a goto from within navigate mode. */ insert_mode(); /* Tempted as I was, I had to resist making this "Where would you like * to go today?" - CRM :) */ query("goto where?", NULL); if (linelim >= 0) { memmove(line + 5, line, strlen(line) + 1); strlcpy(line, "goto ", 5); linelim = 0; yyparse(); } strlcpy(line, tempc, sizeof line); linelim = tempi; /* Now we need to change back to navigate mode ourselves so that * toggle_navigate_mode() will work properly again. */ mode_ind = 'v'; mode = NAVIGATE_MODE; if (!showrange) toggle_navigate_mode(); } void query(const char *s, char *data) { int c; insert_mode(); if (data != NULL) { strlcpy(line, data, sizeof line); linelim = strlen(line); } else { *line = '\0'; linelim = 0; } if (s != NULL) { error(s); } while (linelim >= 0) { update(0); switch (c = nmgetch()) { case ctl('m'): CLEAR_LINE; return; case ctl('g'): line[0] = '\0'; linelim = -1; CLEAR_LINE; update(0); return; case ctl('l'): FullUpdate++; clearok(stdscr,1); update(1); break; default: write_line(c); } } } #ifdef NCURSES_MOUSE_VERSION void mouse_set_pos(int m) { static int x0; switch (m) { case 1: x0 = mevent.x; break; case 2: if (x0 != mevent.x) break; default: linelim = mevent.x - 3; } } #endif sc-7.16_1.1.2/vmtbl.c000066400000000000000000000124441326046243500140750ustar00rootroot00000000000000/* SC A Spreadsheet Calculator * Spreadsheet 'tbl' creation * * original by James Gosling, September 1982 * modifications by Mark Weiser and Bruce Israel, * University of Maryland * * More mods Robert Bond, 12/86 * More mods by Alan Silverstein, 3-4/88, see list of changes. * $Revision: 7.16 $ * */ #ifdef PSC # include #endif /* PSC */ #include #include #include "compat.h" #include "sc.h" /* * check to see if *rowp && *colp are currently allocated, if not expand the * current size if we can. */ #ifndef PSC void checkbounds(int *rowp, int *colp) { if (*rowp < 0) *rowp = 0; else if (*rowp >= maxrows) { if (*colp >= maxcols) { if (!growtbl(GROWBOTH, *rowp, *colp)) { *rowp = maxrows - 1; *colp = maxcols - 1; } return; } else { if (!growtbl(GROWROW, *rowp, 0)) *rowp = maxrows - 1; return; } } if (*colp < 0) *colp = 0; else if (*colp >= maxcols) { if (!growtbl(GROWCOL, 0, *colp)) *colp = maxcols - 1; } } #endif /* !PSC */ /* scxrealloc will just scxmalloc if oldptr is == NULL */ #define GROWALLOC(newptr, oldptr, nelem, type, msg) \ newptr = scxrealloc(oldptr, \ nelem * sizeof(type)); \ if (newptr == (type *)NULL) { \ error(msg); \ return (FALSE); \ } \ oldptr = newptr /* wait incase we can't alloc */ #ifndef PSC static const char nolonger[] = "The table can't be any longer"; #endif /* !PSC */ static const char nowider[] = "The table can't be any wider"; /* * grow the main && auxiliary tables (reset maxrows/maxcols as needed) * toprow &&/|| topcol tell us a better guess of how big to become. * we return TRUE if we could grow, FALSE if not.... */ int growtbl(int rowcol, int toprow, int topcol) { int *fwidth2; int *precision2; int *realfmt2; int newcols; #ifndef PSC struct ent ***tbl2; struct ent ** nullit; int cnt; char *col_hidden2; char *row_hidden2; int newrows; int i; newrows = maxrows; #endif /* !PSC */ (void)toprow; /* unused */ newcols = maxcols; if (rowcol == GROWNEW) { #ifndef PSC maxrows = toprow = 0; /* when we first start up, fill the screen w/ cells */ { int startval; startval = LINES - RESROW; newrows = startval > MINROWS ? startval : MINROWS; startval = ((COLS) - rescol) / DEFWIDTH; newcols = startval > MINCOLS ? startval : MINCOLS; } #else newcols = MINCOLS; #endif /* !PSC */ maxcols = topcol = 0; } #ifndef PSC /* set how much to grow */ if ((rowcol == GROWROW) || (rowcol == GROWBOTH)) { if (toprow > maxrows) newrows = GROWAMT + toprow; else newrows += GROWAMT; } #endif /* !PSC */ if ((rowcol == GROWCOL) || (rowcol == GROWBOTH)) { if ((rowcol == GROWCOL) && ((maxcols == ABSMAXCOLS) || (topcol >= ABSMAXCOLS))) { error(nowider); return (FALSE); } if (topcol > maxcols) newcols = GROWAMT + topcol; else newcols += GROWAMT; if (newcols > ABSMAXCOLS) newcols = ABSMAXCOLS; } #ifndef PSC if ((rowcol == GROWROW) || (rowcol == GROWBOTH) || (rowcol == GROWNEW)) { struct ent *** lnullit; int lcnt; GROWALLOC(row_hidden2, row_hidden, newrows, char, nolonger); memset(row_hidden+maxrows, 0, (newrows-maxrows)*sizeof(char)); /* * alloc tbl row pointers, per net.lang.c, calloc does not * necessarily fill in NULL pointers */ GROWALLOC(tbl2, tbl, newrows, struct ent **, nolonger); for (lnullit = tbl+maxrows, lcnt = 0; lcnt < newrows-maxrows; lcnt++, lnullit++) *lnullit = (struct ent **)NULL; /* memset(tbl+maxrows, (char *)NULL, (newrows-maxrows)*(sizeof(struct ent **)));*/ } #endif /* !PSC */ if ((rowcol == GROWCOL) || (rowcol == GROWBOTH) || (rowcol == GROWNEW)) { GROWALLOC(fwidth2, fwidth, newcols, int, nowider); GROWALLOC(precision2, precision, newcols, int, nowider); GROWALLOC(realfmt2, realfmt, newcols, int, nowider); #ifdef PSC memset(fwidth+maxcols, 0, (newcols-maxcols)*sizeof(int)); memset(precision+maxcols, 0, (newcols-maxcols)*sizeof(int)); memset(realfmt+maxcols, 0, (newcols-maxcols)*sizeof(int)); } #else GROWALLOC(col_hidden2, col_hidden, newcols, char, nowider); memset(col_hidden+maxcols, 0, (newcols-maxcols)*sizeof(char)); for (i = maxcols; i < newcols; i++) { fwidth[i] = DEFWIDTH; precision[i] = DEFPREC; realfmt[i] = DEFREFMT; } /* [re]alloc the space for each row */ for (i = 0; i < maxrows; i++) { if ((tbl[i] = scxrealloc(tbl[i], newcols * sizeof(struct ent **))) == (struct ent **)0) { error(nowider); return(FALSE); } for (nullit = ATBL(tbl, i, maxcols), cnt = 0; cnt < newcols-maxcols; cnt++, nullit++) *nullit = (struct ent *)NULL; /* memset((char *)ATBL(tbl,i, maxcols), 0, (newcols-maxcols)*sizeof(struct ent **)); */ } } else i = maxrows; /* fill in the bottom of the table */ for (; i < newrows; i++) { if ((tbl[i] = scxmalloc((newcols * sizeof(struct ent **)))) == NULL) { error(nowider); return(FALSE); } for (nullit = tbl[i], cnt = 0; cnt < newcols; cnt++, nullit++) *nullit = (struct ent *)NULL; /* memset((char *)tbl[i], 0, newcols*sizeof(struct ent **));*/ } FullUpdate++; maxrows = newrows; if (maxrows > 1000) rescol = 5; if (maxrows > 10000) rescol = 6; #endif /* PSC */ maxcols = newcols; return (TRUE); } sc-7.16_1.1.2/xmalloc.c000066400000000000000000000022431326046243500144040ustar00rootroot00000000000000/* * A safer saner malloc, for careless programmers * $Revision: 7.16 $ */ #include #include #include "compat.h" #include "sc.h" static void fatal(char *); #define MAGIC (double)1234567890.12344 void * scxmalloc(size_t n) { void *ptr; if ((ptr = malloc(n + sizeof(double))) == NULL) fatal("scxmalloc: no memory"); *((double *) ptr) = MAGIC; /* magic number */ return(ptr + sizeof(double)); } /* we make sure realloc will do a malloc if needed */ void * scxrealloc(void *ptr, size_t n) { if (ptr == NULL) return(scxmalloc(n)); ptr -= sizeof(double); if (*((double *) ptr) != MAGIC) fatal("scxrealloc: storage not scxmalloc'ed"); if ((ptr = realloc(ptr, n + sizeof(double))) == NULL) fatal("scxmalloc: no memory"); *((double *) ptr) = MAGIC; /* magic number */ return(ptr + sizeof(double)); } void scxfree(void *p) { if (p == NULL) fatal("scxfree: NULL"); p -= sizeof(double); if (*((double *) p) != MAGIC) fatal("scxfree: storage not malloc'ed"); free(p); } static void fatal(char *str) { #ifndef PSC deraw(1); #endif /* PSC */ fprintf(stderr, "%s\n", str); #ifndef PSC diesave(); #endif /* PSC */ exit(1); } sc-7.16_1.1.2/xsc.doc000066400000000000000000000010751326046243500140670ustar00rootroot00000000000000.\" Carsten Kunze, 2016 .Dd August 5, 2016 .Dt XPNAME 1 .Sh NAME .Nm xpname .Nd open pname with mouse support in a separate xterm .Sh SYNOPSIS .Nm .Op Ar "pname options" .Sh DESCRIPTION There are use cases for .Nm pname where it is desirable to start it in a separate X window. .Nm just starts .Do Nm xterm Fl e Dc with .Nm pname and the .Ar "pname options" as argument and enables mouse support. File names with spaces need to be enclosed in single or double quotes .Po Sq Li \(aq or .Sq Li \(dq .Pc .Em and the spaces needs to be protected with a backslash .Pq Sq \(rs . sc-7.16_1.1.2/xsc.sh000066400000000000000000000003061326046243500137300ustar00rootroot00000000000000#!/bin/sh while [ $# -gt 0 ]; do case "$1" in -fg | -bg | -fn) XFl="$XFl $1 $2" shift ;; -*) Fl="$Fl $1" ;; *) break ;; esac shift done xterm $XFl -T "pname $*" -e "pname -M $Fl $*" &