This is patch14 to PennMUSH 1.7.7. After applying this patch, you will have version 1.7.7p14 To apply this patch, save it to a file in your top-level MUSH directory, and do the following: patch -p1 < 1.7.7-patch14 make install If you use GNU patch 2.2, you probably want the above to be 'patch -b -p1', not just 'patch -p1'. Unix (or cygwin) users need not worry about failed hunks in src/switchinc.c, hdrs/switches.h, hdrs/cmds.h, or hdrs/funs.h. These files are automatically rebuilt on compile. On the off chance they appear not to be, simply rm them and re-run make. Then @shutdown and restart your MUSH. - Alan/Javelin In this patch: Major changes: * Ancestors: an object can be configured to serve as an 'ultimate parent' of every room, exit, thing, or player, and attributes that are not found on an object or any of its parents may be inherited from the ancestor for that object type. The ORPHAN flag prevents ancestors from being used on an object. Patch by Walker@M*U*S*H. * Mail messages now track not only the dbref of the sender but the sender's creation time, so messages from dbrefs that have been nuked and recreated can be distinguished from messages from the original sender. This modifies the maildb and make it not usable with older versions. All existing @mail is grandfathered in, and can't be tracked this way. Suggested by Philip Mak. * New chunk memory allocator can be used to greatly reduce process memory requirements by swapping little-used attribute texts out to disk and caching often-used attribute texts in memory. This is incompatible with forking dumps, so if you use it, you'll do nonforking dumps. Configurable in mush.cnf, see comments there. [TAP] * Hardcode: new interaction type INTERACT_PRESENCE marks the arrival/departure/connection/disconnection/grows ears/loses ears messages. Many message types that used to be considered auditory (like most @verb-style messages) are now marked as visual instead. Functions: * strreplace() works like replace() for strings. [SW] * fraction(), for turning floating-point numbers into fractions. [SW] * root(), for finding roots higher than the square root. [SW] Minor changes: * We now use wrapper functions atr_value and safe_atr_value instead of uncompress(AL_STR(...)) or safe_uncompress(AL_STR(...)), so we can do future work with attribute storage cleanly. [TAP] * @*payment attributes now receive the amount of money paid in as %0. Suggested by Sholevi and Time@M*U*S*H. [SW] * Config options that take times now accept a notation describing what kind of units to use. For example, 3600s, 60m, 30m1800s, and 1h all refer to the same period of time. Suggested by Time@M*U*S*H. [SW] * Doxygen commenting of non-static members is essentially complete! Pennhacks see: http://www.pennmush.org/docs/1.7.7/html/ * The mail() function no longer matches non-player objects by name. * Several additional messages (locks, parents, etc.) are now quieted by the QUIET flag. Patch by [EEH]. * New config option default_home sets the room to send homeless things to. [TAP] * Added visual progress indicators for utils/mkcmds.sh so that slow systems won't think they're hung. Suggested by Cheetah@M*U*S*H. Fixes: * Fixes from 1.7.6p10. * The 'i' sort type was not properly implemented. Reported by Noltar and BlaZE@M*U*S*H. * Cleanup of all accesses to ATTR values to use AL_STR() in preparation for future work on attribute storage. [TAP] * The 'any' string for specifying flag types didn't work properly. Reported by Krad@M*U*S*H. * The connect screen may now appear correctly under windows telnet. [SW] * The more efficient channel buffer shifting code now handles pathological cases correctly. * The tag*() functions could leave tags open at the end of full buffers. No longer. * Code cleanup in src/notify.c. * @rejectmotd and @wizmotd set the wrong messages. Report by Konstantine Shirow. [SW] * Using @chan/buffer to resize a recall buffer gives feedback. Reported by Sholevi@M*U*S*H. [SW] * Help fix for grab() by Adu@AbryssMUSH. * You can no longer destroy the base_room (or default_home). Suggested by Philip Mak. [TAP] Prereq: 1.7.7p13 rm -rf CHANGES*~ CHANGES*bak utils/mkvershlp.pl game/txt/hlp CHANGES* *** 1_7_7.366/Patchlevel Wed, 09 Apr 2003 14:28:37 -0500 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.15 600) --- 1_7_7.446(w)/Patchlevel Thu, 22 May 2003 13:39:18 -0500 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.16 600) *************** *** 1,2 **** Do not edit this file. It is maintained by the official PennMUSH patches. ! This is PennMUSH 1.7.7p13 --- 1,2 ---- Do not edit this file. It is maintained by the official PennMUSH patches. ! This is PennMUSH 1.7.7p14 *** 1_7_7.366/CHANGES.176 Wed, 09 Apr 2003 14:28:37 -0500 dunemush (pennmush/g/17_CHANGES 1.10.1.6.1.2.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.9.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2 600) --- 1_7_7.446(w)/CHANGES.176 Tue, 13 May 2003 12:52:03 -0500 dunemush (pennmush/g/17_CHANGES 1.10.1.6.1.2.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.9.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2 600) *************** *** 13,18 **** --- 13,34 ---- ========================================================================== + Version 1.7.6 patchlevel 10 May 13, 2003 + + Minor changes: + * PCRE (the regex matching engine we use) is updated to version 4.2. [SW] + * @mail/file now unclears the cleared bit when filing @mail. + Suggested by Philip Mak. + Fixes: + * @edit is better with editing ansi. Reported by Trispis@M*U*S*H. [SW] + * Help file cleanup. [SW] + * @warnings about missing FAILURE messages were not correctly + checked, causing false positives. Reported by Cheetah. + * Page message no longer ends in a period. Suggested by Time@M*U*S*H. + * Help fixes by Intrevis@M*U*S*H. + * BASE_ROOM can't be destroyed any more. Suggested by Philip Mak. + + Version 1.7.6 patchlevel 9 April 9, 2003 Fixes: *************** *** 25,31 **** * Configure test for /dev/urandom from 1.7.5 got left out by mistake. * Critical overflow bug in command argument parsing fixed. - Version 1.7.6 patchlevel 8 March 21, 2003 Minor changes: --- 41,46 ---- *** 1_7_7.366/CHANGES.177 Wed, 09 Apr 2003 14:28:37 -0500 dunemush (pennmush/g/23_CHANGES 1.48.1.58 600) --- 1_7_7.446(w)/CHANGES.177 Thu, 22 May 2003 13:39:40 -0500 dunemush (pennmush/g/23_CHANGES 1.48.1.84 600) *************** *** 18,23 **** --- 18,91 ---- ========================================================================== + Version 1.7.7 patchlevel 14 May 22, 2003 + + Major changes: + * Ancestors: an object can be configured to serve as an 'ultimate + parent' of every room, exit, thing, or player, and attributes + that are not found on an object or any of its parents may be inherited + from the ancestor for that object type. The ORPHAN flag prevents + ancestors from being used on an object. Patch by Walker@M*U*S*H. + * Mail messages now track not only the dbref of the sender but the + sender's creation time, so messages from dbrefs that have been + nuked and recreated can be distinguished from messages from the + original sender. This modifies the maildb and make it not usable + with older versions. All existing @mail is grandfathered in, and + can't be tracked this way. Suggested by Philip Mak. + * New chunk memory allocator can be used to greatly reduce process + memory requirements by swapping little-used attribute texts out + to disk and caching often-used attribute texts in memory. + This is incompatible with forking dumps, so if you use it, + you'll do nonforking dumps. Configurable in mush.cnf, see comments + there. [TAP] + * Hardcode: new interaction type INTERACT_PRESENCE marks the + arrival/departure/connection/disconnection/grows ears/loses ears + messages. Many message types that used to be considered auditory + (like most @verb-style messages) are now marked as visual instead. + Functions: + * strreplace() works like replace() for strings. [SW] + * fraction(), for turning floating-point numbers into fractions. [SW] + * root(), for finding roots higher than the square root. [SW] + Minor changes: + * We now use wrapper functions atr_value and safe_atr_value instead of + uncompress(AL_STR(...)) or safe_uncompress(AL_STR(...)), so we + can do future work with attribute storage cleanly. [TAP] + * @*payment attributes now receive the amount of money paid in + as %0. Suggested by Sholevi and Time@M*U*S*H. [SW] + * Config options that take times now accept a notation describing what kind + of units to use. For example, 3600s, 60m, 30m1800s, and 1h all refer to + the same period of time. Suggested by Time@M*U*S*H. [SW] + * Doxygen commenting of non-static members is essentially complete! + Pennhacks see: http://www.pennmush.org/docs/1.7.7/html/ + * The mail() function no longer matches non-player objects by name. + * Several additional messages (locks, parents, etc.) are now quieted by + the QUIET flag. Patch by [EEH]. + * New config option default_home sets the room to send homeless things + to. [TAP] + * Added visual progress indicators for utils/mkcmds.sh so that slow + systems won't think they're hung. Suggested by Cheetah@M*U*S*H. + Fixes: + * Fixes from 1.7.6p10. + * The 'i' sort type was not properly implemented. Reported by + Noltar and BlaZE@M*U*S*H. + * Cleanup of all accesses to ATTR values to use AL_STR() in preparation + for future work on attribute storage. [TAP] + * The 'any' string for specifying flag types didn't work properly. + Reported by Krad@M*U*S*H. + * The connect screen may now appear correctly under windows telnet. [SW] + * The more efficient channel buffer shifting code now handles + pathological cases correctly. + * The tag*() functions could leave tags open at the end of full + buffers. No longer. + * Code cleanup in src/notify.c. + * @rejectmotd and @wizmotd set the wrong messages. Report by + Konstantine Shirow. [SW] + * Using @chan/buffer to resize a recall buffer gives feedback. Reported by + Sholevi@M*U*S*H. [SW] + * Help fix for grab() by Adu@AbryssMUSH. + * You can no longer destroy the base_room (or default_home). Suggested + by Philip Mak. [TAP] + Version 1.7.7 patchlevel 13 April 9, 2003 Major changes: *** 1_7_7.366/game/txt/hlp/penntop.hlp Thu, 06 Mar 2003 10:51:34 -0600 dunemush (pennmush/13_penntop.hl 1.2.1.27.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.6 600) --- 1_7_7.446(w)/game/txt/hlp/penntop.hlp Tue, 15 Apr 2003 12:12:10 -0500 dunemush (pennmush/13_penntop.hl 1.2.1.27.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.12.1.7 600) *************** *** 75,91 **** & topics Help is available on the following topics: ! ACTION LISTS ATTRIB-OWNERSHIP ATTRIBUTES ! BEING KILLED BOOLEAN VALUES CHAT ! CLIENTS CONTROL COPYRIGHT ! COSTS CREDITS DBREFS ! DROP-TO ENACTOR EVALUATION ! EXECUTOR EXITS FAILURE ! FLAGS FUNCTIONS GENDER ! GLOBALS HERE HOMES ! INTERIORS LINKING LISTENING ! LISTS LOOPING MASTER ROOM ! MATCHING (continued in help topics2) & topics2 --- 75,91 ---- & topics Help is available on the following topics: ! ACTION LISTS ANCESTORS ATTRIB-OWNERSHIP ! ATTRIBUTES BEING KILLED BOOLEAN VALUES ! CHAT CLIENTS CONTROL ! COPYRIGHT COSTS CREDITS ! DBREFS DROP-TO ENACTOR ! EVALUATION EXECUTOR EXITS ! FAILURE FLAGS FUNCTIONS ! GENDER GLOBALS HERE ! HOMES INTERIORS LINKING ! LISTENING LISTS LOOPING ! MASTER ROOM MATCHING (continued in help topics2) & topics2 *************** *** 135,140 **** --- 135,165 ---- Test 3, success. See also: ATTRIBUTES, SUBSTITUTION, @asuccess, @dolist + & ANCESTORS + ANCESTORS + + Objects can inherit attributes from other objects through the + use of parents. An object's parent, its parent's parent, its + parent's parent's parent, etc. constitute the object's "parent chain" + and lookups work the way up the chain until an inheritance occurs. + + Ancestors are "virtual parents" that are assumed to be last on every + parent chain. There is one ancestor for each object type (room, exit, + thing, player), and @config lists the dbref of each ancestor object + (@config ancestor_room, etc.) Under normal circumstances, if an attribute + can't be retrieve from an object or any of its explicit parents, + the attribute will be looked on on the appropriate ancestor. + The ORPHAN flag may be set on an object to cause lookups on that + object to ignore ancestors (like the pre-ancestor behavior). + + Ancestors may themselves have parent chains, but these are (obviously) + not virtually terminated by ancestors. + + Note that the choice of which ancestor to look up is based on the + type of the *child* object, as is the check of the ORPHAN flag. + + See also: PARENTS, ORPHAN + & ATTRIB-OWNERSHIP ATTRIBUTE OWNERSHIP *** 1_7_7.366/game/txt/hlp/pennfunc.hlp Tue, 08 Apr 2003 17:03:09 -0500 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.5 600) --- 1_7_7.446(w)/game/txt/hlp/pennfunc.hlp Mon, 12 May 2003 16:45:29 -0500 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.3 600) *************** *** 163,173 **** dec() dist2d() dist3d() div() floordiv() inc() mod() ! These functions operate only with floating point numbers: acos() asin() atan() atan2() ceil() cos() ctu() e() exp() fdiv() ! floor() fmod() ln() log() pi() ! power() round() sin() sqrt() tan() These functions operate on n-dimensional vectors. A vector is a delimiter-separated list of numbers (space-separated, by default): --- 163,174 ---- dec() dist2d() dist3d() div() floordiv() inc() mod() ! These functions operate on floating point numbers: acos() asin() atan() atan2() ceil() cos() ctu() e() exp() fdiv() ! floor() fmod() fraction() ln() log() ! pi() power() root() round() sin() ! sqrt() tan() These functions operate on n-dimensional vectors. A vector is a delimiter-separated list of numbers (space-separated, by default): *************** *** 195,202 **** lpos() regmatch() repeat() reverse() right() rjust() scramble() secure() sha1() space() spellnum() squish() strcat() strinsert() stripaccents() ! stripansi() strlen() strmatch() switch() trim() ! ucstr() wrap() See also: STRINGS & Time functions --- 196,203 ---- lpos() regmatch() repeat() reverse() right() rjust() scramble() secure() sha1() space() spellnum() squish() strcat() strinsert() stripaccents() ! stripansi() strlen() strmatch() strreplace() switch() ! trim() ucstr() wrap() See also: STRINGS & Time functions *************** *** 1016,1027 **** @find. You must control the object in order to perform entrances() on it. & EQ() ! [eq(,)] ! Takes two integers, and returns 1 if they are equal, 0 otherwise. ! Will not work with characters. ! See also: neq(), match() & ESCAPE() escape() --- 1017,1027 ---- @find. You must control the object in order to perform entrances() on it. & EQ() ! eq(,) ! Takes two numbers, and returns 1 if they are equal, 0 otherwise. ! See also: neq() & ESCAPE() escape() *************** *** 1078,1091 **** Returns e to the power of . & EXTRACT() ! extract(,,[,]) ! Extract returns a string of length words, starting with the first ! word. Unlike letters, the first word in a string is number 1, ! instead of 0. ! ! A word is assumed to be defined as a string beginning and ending ! with a space, or a string w/o any interior spaces. For example: think extract(This is a test string,3,2) --- 1078,1087 ---- Returns e to the power of . & EXTRACT() ! extract(,,[,]) ! This function returns elements of a list, counting ! from the element. For example: think extract(This is a test string,3,2) *************** *** 1126,1136 **** object). & FIRST() ! first([,]) ! Returns the first word of a string, that is, everything to the left ! of the first space in the string, or the entire string if there are ! no spaces in the string. See REST, LAST. & FLAGS() flags() flags(/) --- 1122,1132 ---- object). & FIRST() ! first([,]) ! ! Returns the first element of a list. ! See also: rest(), last() & FLAGS() flags() flags(/) *************** *** 1270,1275 **** --- 1266,1286 ---- @nameaccent, if any. Related functions: NAME(), ACCNAME(), INAME() + & FRACTION() + fraction() + + This function returns a fraction representing the floating-point . + Since not all numbers can be expressed as a fraction, dividing the + numerator by the denominator of the results will not always return the + original , but something close to it. + + Examples: + > think fraction(.75) + 3/4 + > think fraction(pi()) + 348987/111086 + > think fraction(2) + 2 & FULLNAME() fullname() *************** *** 1284,1297 **** & FUNCTIONS() functions() ! Returns a space-separated list of all function names. & GET() get(/) ! The get function will be replaced by the string stored in the ! attribute of the object. You may get the attributes of objects you ! control, the attributes you control on other objects, and publicly ! accessible attributes. & GRAB() & REGRAB() --- 1295,1307 ---- & FUNCTIONS() functions() ! Returns a space-separated list of the names of all function. & GET() get(/) ! The get() function returns the string stored in an object's attribute. ! You may get the attributes of objects you control, the attributes you ! own on other objects, and publicly accessible attributes. & GRAB() & REGRAB() *************** *** 1306,1325 **** regrabi() is case-insensitive. Basically, this is a much more efficient way to do: ! extract(list, match(list, pattern, delimiter), 1) or ! extract(list, regmatch(list, regexp, delimiter), 1) See also: match(), extract(), element(), elements(), index(), regmatch() & GRABALL() & REGRABALL() & REGRABALLI() ! graball(,[,][,,[,][,,[,][, which match . If none match, an empty string is returned. Examples: --- 1316,1335 ---- regrabi() is case-insensitive. Basically, this is a much more efficient way to do: ! extract(list, match(list, pattern, delimiter), 1, delimiter) or ! the regular expression variation thereof. See also: match(), extract(), element(), elements(), index(), regmatch() & GRABALL() & REGRABALL() & REGRABALLI() ! graball(,[,][,,[,][,,[,][, which match . If none match, an empty string is returned. Examples: *************** *** 1356,1367 **** This function works exactly like grep or regrep, but is case-insensitive. See "help grep()" or "help regrep()" for details. & GT() ! [gt(,)] Takes two numbers, and returns 1 if and only if the first is greater than the second, and 0 otherwise. & GTE() ! [gte(,)] Takes two numbers, and returns 1 if and only if the first is greater than or equal to the second, and 0 otherwise. --- 1366,1377 ---- This function works exactly like grep or regrep, but is case-insensitive. See "help grep()" or "help regrep()" for details. & GT() ! gt(,) Takes two numbers, and returns 1 if and only if the first is greater than the second, and 0 otherwise. & GTE() ! gte(,) Takes two numbers, and returns 1 if and only if the first is greater than or equal to the second, and 0 otherwise. *************** *** 1505,1511 **** & INSERT() insert(,,[,]) ! This inserts before the item of at the . That means that then becomes the th element of . If a separator is not given, a space is assumed. Null items are counted when determining position, as in 'items()'. --- 1515,1521 ---- & INSERT() insert(,,[,]) ! This inserts before the item of at . That means that then becomes the th element of . If a separator is not given, a space is assumed. Null items are counted when determining position, as in 'items()'. *************** *** 1662,1672 **** > say [iter(red blue green,iter(fish shoe, [itext(1)]:[itext(0)]))] You say, "red:fish red:shoe blue:fish blue:shoe green:fish green:shoe" & LAST() ! last([,]) ! Returns the last word of a string, that is, everything to the right ! of the last space in the string, or the entire string if there are ! no spaces in the string. See FIRST, REST. & LATTR() lattr([/]) --- 1672,1682 ---- > say [iter(red blue green,iter(fish shoe, [itext(1)]:[itext(0)]))] You say, "red:fish red:shoe blue:fish blue:shoe green:fish green:shoe" & LAST() ! last([,]) ! Returns the last element of a list. ! ! See also: first(), rest() & LATTR() lattr([/]) *************** *** 2138,2146 **** This function works identically to the match() function, save that it returns all matches, not just the first: It returns the index numbers of ! all elements in the list which match . If none match, ! an empty string is returned. The resulting output is delimited by ! , if given, or else by the delimiter Examples: --- 2148,2156 ---- This function works identically to the match() function, save that it returns all matches, not just the first: It returns the index numbers of ! all elements in which match . If none match, an empty ! string is returned. The resulting output is delimited by , if ! given, or else by the delimiter. Examples: *************** *** 2171,2177 **** has no interior spaces. So ' hello ' would be one word, while 'hello there' would be two. See LISTS ! member() is case-sensitive. & MERGE() merge(, , ) --- 2181,2188 ---- has no interior spaces. So ' hello ' would be one word, while 'hello there' would be two. See LISTS ! member() is case-sensitive and requires an exact match. For wild ! card patterns, use match(). & MERGE() merge(, , ) *************** *** 2369,2377 **** object 1 is carrying object 2. You must control at least one of the objects. & NEQ() ! [neq(,)] ! Basically the same as [not(eq(,))]. See also: eq(), not() & NEXT() --- 2380,2388 ---- object 1 is carrying object 2. You must control at least one of the objects. & NEQ() ! neq(,) ! Basically the same as [not(eq(,))]. See also: eq(), not() & NEXT() *************** *** 2400,2406 **** See also: and(), or(), xor(), not() & NOT() ! [not()] Takes a boolean value, and returns its inverse. I.E. if the input is equivalent to true(1), it returns a 0, and if --- 2411,2417 ---- See also: and(), or(), xor(), not() & NOT() ! not() Takes a boolean value, and returns its inverse. I.E. if the input is equivalent to true(1), it returns a 0, and if *************** *** 2575,2580 **** --- 2586,2593 ---- power(,) Returns to the power of . + + See also: root() & POWERS() powers() powers(,) *************** *** 2674,2680 **** A word is defined as a string which contains no interior spaces (or 's if is used). If the word is not in the ! list, then the list is returned. To remove all occurrences of a word from a string, consider using edit(). --- 2687,2693 ---- A word is defined as a string which contains no interior spaces (or 's if is used). If the word is not in the ! list, then the list is returned. It is case-sensitive. To remove all occurrences of a word from a string, consider using edit(). *************** *** 2703,2714 **** You say, "blue|red|white|yellow" & REST() ! rest([,]) ! Rest takes a string, returns all the string except the first word, ! that is, everything to the right of the first space, or an empty ! string, or the empty string if there are no spaces in the string. ! See FIRST, LAST. & REVWORDS() revwords([,][, ]) --- 2716,2726 ---- You say, "blue|red|white|yellow" & REST() ! rest([,]) ! Returns a list minus its first element. ! ! See also: first(), last() & REVWORDS() revwords([,][, ]) *************** *** 2779,2784 **** --- 2791,2809 ---- are players; if is a player, the ROOM() function may be used to find the player's absolute location if the player is not set UNFINDABLE. + & ROOT() + root(, ) + + Returns the n-th root of . The 2nd root is the square root, + the 3rd the cube root, and so on. + + Example: + > think root(27, 3) + 3 + > think power(3, 3) + 27 + + See also: sqrt(), power() & ROUND() round(,) *************** *** 2966,2974 **** Performs a rightwards bit-shift on , shifting it times. This is equivalent to div(,power(2,), but much faster. & SHUFFLE() ! shuffle( <...wordN>[,][,]) ! This function shuffles the order of words in a list, returning a random permutation of its elements. "[shuffle(foo bar baz gleep)]" might evaluate to "baz foo gleep bar". --- 2991,2999 ---- Performs a rightwards bit-shift on , shifting it times. This is equivalent to div(,power(2,), but much faster. & SHUFFLE() ! shuffle(>[,][,]) ! This function shuffles the order of the items of a list, returning a random permutation of its elements. "[shuffle(foo bar baz gleep)]" might evaluate to "baz foo gleep bar". *************** *** 3107,3112 **** --- 3132,3139 ---- sqrt() Returns the square root of . cannot be negative. + + See also: root() & SQUISH() squish([, ]) *************** *** 3198,3204 **** & STRLEN() strlen() ! Returns the length of the string in a numerical string. & STRMATCH() strmatch(, ) --- 3225,3231 ---- & STRLEN() strlen() ! Returns the length of the string (The number of characters in it). & STRMATCH() strmatch(, ) *************** *** 3209,3215 **** strmatch(Foo bar baz,*Baz) will return 1. strmatch(Foo bar baz,*Foo) will return 0. strmatch(Foo bar baz,*o*a*) will return 1. ! & SUB() sub(, ) --- 3236,3251 ---- strmatch(Foo bar baz,*Baz) will return 1. strmatch(Foo bar baz,*Foo) will return 0. strmatch(Foo bar baz,*o*a*) will return 1. ! & STRREPLACE() ! strreplace(string, start, length, text) ! ! Returns string with the characters starting at replaced ! by . As with other string functions, the first character is at ! position 0. ! ! Example: ! > think strreplace(Fix teh typo, 4, 3, the) ! Fix the typo & SUB() sub(, ) *************** *** 3449,3454 **** --- 3485,3493 ---- is specified, characters are only trimmed from the right side. If you specify a trim style, you must also explicitly specify the character to trim, since the trim style must be the third argument to the function. + + If the tiny_trim_fun config option is "yes", the character and style + arguments are reversed. Examples: > say [trim( foo bar baz eek )] *************** *** 3764,3780 **** This function is equivalent to @wipe. It returns nothing. & WORDPOS() ! wordpos(, [, ]) ! Returns the number of the word within where the th character falls. Characters and words are numbered starting with 1, and spaces between words are treated as belonging to the word that follows them. If is not within the string, #-1 is returned. Example: wordpos(foo bar baz, 5) returns "2" & WORDS() ! words([,]) ! words() returns the number of words in a string. & WRAP() wrap(, [, [, ]) --- 3803,3819 ---- This function is equivalent to @wipe. It returns nothing. & WORDPOS() ! wordpos(, [, ]) ! Returns the number of the word within where the th character falls. Characters and words are numbered starting with 1, and spaces between words are treated as belonging to the word that follows them. If is not within the string, #-1 is returned. Example: wordpos(foo bar baz, 5) returns "2" & WORDS() ! words([,]) ! words() returns the number of elements in . & WRAP() wrap(, [, [, ]) *************** *** 3799,3805 **** on the use of this function. & XOR() ! [xor(,)] Takes two booleans, and returns a 1 if one, and only one of the two inputs is equivalent to true(1). See BOOLEAN VALUES. --- 3838,3844 ---- on the use of this function. & XOR() ! xor(,) Takes two booleans, and returns a 1 if one, and only one of the two inputs is equivalent to true(1). See BOOLEAN VALUES. *** 1_7_7.366/game/txt/hlp/pennflag.hlp Sat, 15 Mar 2003 20:28:58 -0600 dunemush (pennmush/17_pennflag.h 1.1.1.1.1.2.1.1.1.2.1.1.1.2.1.1.2.1.2.1.1.1.1.2.1.4.1.2.2.2 600) --- 1_7_7.446(w)/game/txt/hlp/pennflag.hlp Tue, 15 Apr 2003 12:28:04 -0500 dunemush (pennmush/17_pennflag.h 1.1.1.1.1.2.1.1.1.2.1.1.1.2.1.1.2.1.2.1.1.1.1.2.1.4.1.2.2.4 600) *************** *** 49,60 **** a - Audible b - Debug c - Connected d - Destroy_Ok e - Enter_Ok g - Gagged ! h - Halt j - Jury_Ok l - Light ! m - Myopic, Mistrust n - No_Command o - On-Vacation ! p - Puppet, Paranoid r - Royalty s - Suspect ! t - Transparent u - Uninspected v - Verbose ! w - No_Warn x - Terse, Cloudy ? - Unregistered ! ^ - Listen_Parent ~ - Noaccents --------------------------------------------------------------------- Some flags may not be enabled on some MUSHes. @flag/list will show which are available. --- 49,60 ---- a - Audible b - Debug c - Connected d - Destroy_Ok e - Enter_Ok g - Gagged ! h - Halt i - Orphan j - Jury_Ok ! l - Light m - Myopic, Mistrust n - No_Command ! o - On-Vacation p - Puppet, Paranoid r - Royalty ! s - Suspect t - Transparent u - Uninspected ! v - Verbose w - No_Warn x - Terse, Cloudy ! ? - Unregistered ^ - Listen_Parent ~ - Noaccents --------------------------------------------------------------------- Some flags may not be enabled on some MUSHes. @flag/list will show which are available. *************** *** 525,530 **** --- 525,539 ---- Meaningless for rooms. See also: TRANSPARENT, look + & ORPHAN + Flag: ORPHAN (all types) + + The ORPHAN flag severs the connection between an object and its + type ancestor, and prevents attributes from being + retrieved from the ancestor. It has no effect on the object's + true @parents, only on the its use of the ancestor. + + See also: @parent, ancestors & PLAYER Flag: PLAYER (player) *** 1_7_7.366/game/txt/hlp/penncmd.hlp Sun, 30 Mar 2003 22:36:13 -0600 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.6 600) --- 1_7_7.446(w)/game/txt/hlp/penncmd.hlp Tue, 13 May 2003 15:03:41 -0500 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.1.1.3 600) *************** *** 1525,1531 **** & @hook2 An example of @hook: ! > &top_line #3=pemit(%#, What follows is a the results of a look) > &bottom_line #3=pemit(%#, You're done looking.) > @hook/before look=#3, top_line > @hook/after look=#3, bottom_line --- 1525,1531 ---- & @hook2 An example of @hook: ! > &top_line #3=pemit(%#, What follows is the results of a look) > &bottom_line #3=pemit(%#, You're done looking.) > @hook/before look=#3, top_line > @hook/after look=#3, bottom_line *************** *** 2853,2864 **** --- 2853,2870 ---- & @stats @stats [] @stats/table + @stats/chunks + @stats/regions + @stats/paging In its first form, display the number of objects in the game broken down by object types. Wizards can supply a player name to count only objects owned by that player. In its second form, display statistics on internal tables. + + In the remaining forms, display statistics or histograms about the + chunk (attribute) memory system. & @success @success [=]. *** 1_7_7.366/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:26:00 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.2 660) --- 1_7_7.446(w)/game/txt/hlp/index.hlp Mon, 28 Apr 2003 22:12:58 -0500 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.3 660) *************** *** 64,87 **** -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-4 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) - 1.7.4p5 1.7.4p6 1.7.4p7 - 1.7.4p8 1.7.4p9 1.7.4p10 - 1.7.4p11 1.7.4p12 1.7.4p13 - 1.7.4p14 1.7.4p15 1.7.4p16 - 1.7.4p17 1.7.4p18 1.7.4p19 - 1.7.4p20 1.7.5p0 1.7.5p1 - 1.7.5p2 1.7.5p3 1.7.5p4 - 1.7.5p5 1.7.5p6 1.7.5p7 - 1.7.5p8 1.7.5p9 1.7.5p10 - 1.7.5p11 1.7.5p12 1.7.6p0 - 1.7.6p1 1.7.6p2 1.7.6p3 - 1.7.6p4 1.7.6p5 1.7.6p6 - 1.7.7p0 1.7.7p1 1.7.7p2 - 1.7.7p3 1.7.7p4 1.7.7p5 - 1.7.7p6 : ; - @-attributes @-building @-general - ======= 1.7.4p7 1.7.4p8 1.7.4p9 1.7.4p10 1.7.4p11 1.7.4p12 1.7.4p13 1.7.4p14 1.7.4p15 --- 64,69 ---- *************** *** 94,127 **** 1.7.6p0 1.7.6p1 1.7.6p2 1.7.6p3 1.7.6p4 1.7.6p5 1.7.6p6 1.7.6p7 1.7.6p8 ! : ; @-attributes ! @-building @-general @-wizard ! @@ @@() @aahear ! @aclone @aconnect @adeath ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-5 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-5 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! @-wizard @@ @@() ! @aahear @aclone @aconnect ! @adeath @adescribe @adestroy ! @adisconnect @adrop @aefail ! @aenter @afailure @afollow ! @agive @ahear @aidescribe ! @aleave @alfail @alias ! @allhalt @allquota @amhear ! @amove @apayment @areceive ! @asuccess @atport @atrchown ! @atrlock @attribute @aufail ! @aunfollow @ause @away ! @azenter @azleave @boot ! @break @cemit @channel ! @channel2 @channel3 @channel4 ! @channel5 @charges @chat ! ======= @adescribe @adestroy @adisconnect @adrop @aefail @aenter @afailure @afollow @agive --- 76,95 ---- 1.7.6p0 1.7.6p1 1.7.6p2 1.7.6p3 1.7.6p4 1.7.6p5 1.7.6p6 1.7.6p7 1.7.6p8 ! 1.7.6p9 1.7.7p0 1.7.7p1 ! 1.7.7p2 1.7.7p3 1.7.7p4 ! 1.7.7p5 1.7.7p6 1.7.7p7 ! 1.7.7p8 1.7.7p9 1.7.7p10 For more, see Entries-5 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-5 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! 1.7.7p11 1.7.7p12 1.7.7p13 ! : ; @-attributes ! @-building @-general @-wizard ! @@ @@() @aahear ! @aclone @aconnect @adeath @adescribe @adestroy @adisconnect @adrop @aefail @aenter @afailure @afollow @agive *************** *** 133,167 **** @attribute @aufail @aunfollow @ause @away @azenter @azleave @boot @break - @cemit @channel @channel2 - @channel3 @channel4 @channel5 - @charges @chat @chown - @chownall @chzone @chzone2 - @chzoneall @clock @clock2 - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-6 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-6 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! @chown @chownall @chzone ! @chzone2 @chzoneall @clock ! @clock2 @clone @command ! @comment @config @config attribs ! @config chat @config cmds @config cosmetic ! @config cosmetic2 @config costs @config db ! @config dump @config funcs @config limits ! @config limits2 @config log @config net ! @config parameters @config tiny @conformat ! @cost @cpattr @create ! @dbck @death @decompile ! @decompile2 @decompile3 @descformat ! @describe @destroy @destroy2 ! @dig @dig2 @disable ! @doing @dolist @drain ! @drop @dump @ealias ! ======= @clone @command @comment @config @config attribs @config chat @config cmds @config cosmetic @config cosmetic2 --- 101,116 ---- @attribute @aufail @aunfollow @ause @away @azenter @azleave @boot @break For more, see Entries-6 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-6 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! @cemit @channel @channel2 ! @channel3 @channel4 @channel5 ! @charges @chat @chown ! @chownall @chzone @chzone2 ! @chzoneall @clock @clock2 @clone @command @comment @config @config attribs @config chat @config cmds @config cosmetic @config cosmetic2 *************** *** 171,230 **** @config tiny @conformat @cost @cpattr @create @dbck @death @decompile @decompile2 ! @decompile3 @describe @destroy ! @destroy2 @dig @dig2 ! @disable @doing @dolist ! @drain @drop @dump ! @ealias @edit @efail ! @elock @emit @enable ! @enter @entrances @eunlock ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-7 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-7 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! @edit @efail @elock ! @emit @enable @enter ! @entrances @eunlock @exitformat ! @failure @filter @filter2 ! @find @firstexit @flag ! @flag2 @follow @force ! @force2 @forwardlist @function ! @function2 @function3 @function4 ! @gedit @give @grep2 ! @grep @halt @haven ! @hide @hook @hook2 ! @idescribe @idle @infilter ! @inprefix @kick @lalias ! @leave @lemit @lfail ! @link @list @listen ! @listen2 @listmotd @lock ! ======= ! @exitformat @failure @filter ! @filter2 @find @firstexit @follow @force @force2 @forwardlist @function @function2 @function3 @function4 @gedit @give @grep2 @grep @halt @haven @hide ! @hook @hook2 @idescribe ! @idle @infilter @inprefix ! @kick @lalias @leave ! @lemit @lfail @link ! @list @listen @listen2 ! @listmotd @lock @lock2 ! @lock3 @lock4 @lock5 ! @lock6 @lock7 @lock8 ! @lock9 @log @logwipe ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-8 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-8 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) @lock2 @lock3 @lock4 @lock5 @lock6 @lock7 @lock8 @lock9 @log --- 120,156 ---- @config tiny @conformat @cost @cpattr @create @dbck @death @decompile @decompile2 ! @decompile3 @descformat @describe ! @destroy @destroy2 @dig For more, see Entries-7 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-7 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! @dig2 @disable @doing ! @dolist @drain @drop ! @dump @ealias @edit ! @efail @elock @emit ! @enable @enter @entrances ! @eunlock @exitformat @failure ! @filter @filter2 @find ! @firstexit @flag @flag2 @follow @force @force2 @forwardlist @function @function2 @function3 @function4 @gedit @give @grep2 @grep @halt @haven @hide ! @hook @hook2 @idescformat ! @idescribe @idle @infilter ! @inprefix @kick @lalias For more, see Entries-8 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-8 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! @leave @lemit @lfail ! @link @list @listen ! @listen2 @listmotd @lock @lock2 @lock3 @lock4 @lock5 @lock6 @lock7 @lock8 @lock9 @log *************** *** 238,270 **** @odescribe @odrop @oefail @oemit @oenter @ofailure @ofollow @ogive @oidescribe - @oleave @olfail @omove - @opayment @open @oreceive - @osuccess @otport @oufail - ======= - @lset @map2 @mail - @malias @malias2 @malias3 - @malias4 @malias5 @map - @motd @move @mvattr - @name @nameaccent @nameformat - @newpassword @notify @nspemit - @nuke @odeath @odescribe - @odrop @oefail @oemit - @oenter @ofailure @ofollow - @ogive @oidescribe @oleave - @olfail @omove @opayment - @open @oreceive @osuccess - @otport @oufail @ounfollow - @ouse @oxenter @oxleave - @oxmove @oxtport @ozenter - @ozleave @parent @password - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-9 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-9 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) @ounfollow @ouse @oxenter @oxleave @oxmove @oxtport @ozenter @ozleave @parent --- 164,177 ---- @odescribe @odrop @oefail @oemit @oenter @ofailure @ofollow @ogive @oidescribe For more, see Entries-9 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-9 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! @oleave @olfail @omove ! @opayment @open @oreceive ! @osuccess @otport @oufail @ounfollow @ouse @oxenter @oxleave @oxmove @oxtport @ozenter @ozleave @parent *************** *** 278,310 **** @search @search2 @search3 @select @set @sex @shutdown @sitelock @sitelock2 - @sitelock3 @squota @startup - @stats @success @sweep - @switch @switch2 @teleport - ======= - @payment @pcreate @pemit - @pemit2 @poll @poor - @power @prefix @ps - @purge @quota @readcache - @receive @recycle @rejectmotd - @remit @restart @runout - @rwall @rwallemit @rwallpose - @scan @search @search2 - @search3 @select @set - @sex @shutdown @sitelock - @sitelock2 @sitelock3 @squota - @startup @stats @success - @sweep @switch @switch2 - @teleport @tport @trigger - @trigger2 @ufail @ulock - @undestroy @unfollow @unlink - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-10 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-10 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) @tport @trigger @trigger2 @ufail @ulock @undestroy @unfollow @unlink @unlock --- 185,198 ---- @search @search2 @search3 @select @set @sex @shutdown @sitelock @sitelock2 For more, see Entries-10 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-10 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! @sitelock3 @squota @startup ! @stats @success @sweep ! @switch @switch2 @teleport @tport @trigger @trigger2 @ufail @ulock @undestroy @unfollow @unlink @unlock *************** *** 313,369 **** @verb2 @verb3 @verb4 @version @vrml_url @wait @wait2 @wall @warnings - ======= - @unlock @unrecycle @uptime - @uptime2 @use @uunlock - @verb @verb2 @verb3 - @verb4 @version @vrml_url - @wait @wait2 @wall - @wallemit @wallpose @warnings - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) @warnings2 @warnings3 @wcheck - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) @whereis @wipe @wizmotd @wizwall @zemit @zenter @zleave \ ^ abode abs() accent() - accent2 accent3 accent4 - acos() action lists action2 - add() after() ahelp - ======= - @whereis @wipe @wizemit - @wizmotd @wizpose @wizwall - @zemit @zenter @zleave - \ ^ abode - abs() accent() accent2 - accent3 accent4 acos() - action lists action2 add() - after() ahelp alphamax() - alphamin() and() andflags() - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-11 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-11 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! alphamax() alphamin() and() ! andflags() andlflags() anews ! ansi ansi() aposs() ! art() asin() atan() ! atan2() atrlock() attrcnt() ! attrib-ownership attribute flags attribute functions ! attribute list attributes attributes list ! attributes2 attributes3 attributes4 ! audible audible2 band() ! beep() before() being killed ! bitwise functions bnand() bnot() ! boolean functions boolean values boolean2 ! boolean3 bor() bound() ! brackets() brief builder ! bxor() cand() capstr() ! case() caseall() cat() ! ======= anews ansi ansi() aposs() art() asin() atan() atan2() atrlock() --- 201,221 ---- @verb2 @verb3 @verb4 @version @vrml_url @wait @wait2 @wall @warnings @warnings2 @warnings3 @wcheck @whereis @wipe @wizmotd @wizwall @zemit @zenter @zleave \ ^ abode abs() accent() For more, see Entries-11 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-11 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! accent2 accent3 accent4 ! accname() acos() action lists ! action2 add() after() ! ahelp alphamax() alphamin() ! and() andflags() andlflags() anews ansi ansi() aposs() art() asin() atan() atan2() atrlock() *************** *** 375,432 **** being killed bitwise functions bnand() bnot() boolean functions boolean values boolean2 boolean3 bor() - bound() brackets() brief - builder bxor() cand() - capstr() case() caseall() - cat() cd ceil() - cemit() center() cflags() - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-12 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-12 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! cd ceil() cemit() ! center() cflags() ch ! changes channel functions channel-list ! channels() chat checkpass() ! chown_ok chr() clients ! clock() clone() cloudy ! code color commands ! communication functions comp() comsys ! con() config() conn() ! connected control controls() ! convsecs() convtime() convutcsecs() ! copyright copyrite cor() ! cos() costs create() ! credits ctime() ctitle() ! ctu() cv cwho() ! dark dark2 database ! ======= ch changes channel functions channel-list channels() chat ! checkpass() chown_ok chr() ! clients clock() clone() ! cloudy code color ! commands communication functions comp() ! comsys con() config() ! conn() connected control ! controls() convsecs() convtime() ! convutcsecs() copyright copyrite ! cor() cos() costs ! create() credits ctime() ! ctitle() ctu() cv ! cwho() dark dark2 ! database dbref # dbref functions ! dbref number dbref2 dbrefs ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-13 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-13 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) dbref # dbref functions dbref number dbref2 dbrefs debug debug2 dec() decrypt() --- 227,262 ---- being killed bitwise functions bnand() bnot() boolean functions boolean values boolean2 boolean3 bor() For more, see Entries-12 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-12 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! bound() brackets() brief ! builder bxor() cand() ! capstr() case() caseall() ! cat() cd ceil() ! cemit() center() cflags() ch changes channel functions channel-list channels() chat ! checkpass() children() chown_ok ! chr() clients clock() ! clone() cloudy code ! color commands communication functions ! comp() comsys con() ! config() conn() connected ! contact control controls() ! convsecs() convtime() convutcsecs() ! copyright copyrite cor() For more, see Entries-13 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-13 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! cos() costs create() ! credits ctime() ctitle() ! ctu() cv cwho() ! dark dark2 database dbref # dbref functions dbref number dbref2 dbrefs debug debug2 dec() decrypt() *************** *** 439,472 **** element() elements() elist() elock() emit() enactor encrypt() endtag() enter - enter_ok entrances() eq() - escape() etimefmt() etimefmt2 - eval() evaluation order evaluation2 - events examine executor - ======= - debug debug2 dec() - decrypt() default() delete() - desert destroy_ok die() - dig() dismiss dist2d() - dist3d() div() doing - doing() drop drop-tos - dynhelp() e() edefault() - edit() element() elements() - elist() elock() emit() - enactor encrypt() endtag() - enter enter_ok entrances() - eq() escape() etimefmt() - etimefmt2 eval() evaluation order - evaluation2 events examine - executor exit() exits - exits2 exp() extract() - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-14 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-14 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) exit() exits exits2 exp() extract() failure fdiv() filter() filterbool() --- 269,283 ---- element() elements() elist() elock() emit() enactor encrypt() endtag() enter For more, see Entries-14 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-14 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! enter_ok entrances() eq() ! escape() etimefmt() etimefmt2 ! eval() evaluation order evaluation2 ! events examine executor exit() exits exits2 exp() extract() failure fdiv() filter() filterbool() *************** *** 474,597 **** flag list flag permissions flags flags list flags() flags2 flip() floating floor() - ======= - failure fdiv() filter() - filterbool() findable() first() - fixed flag list flags - flags list flags() flags2 - flip() floating floor() - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) floordiv() fmod() fold() fold2 folderstats() follow followers() following() foreach() foreach2 fullname() function list functions functions() functions2 - gagged gender get - get() get_eval() give - global commands globals go - going goto grab() - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) - ======= - graball() grep() grepi() - gt() gte() halt - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-15 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-15 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) graball() grep() grepi() gt() gte() halt hasattr() hasattrp() hasattrpval() hasattrval() hasflag() haspower() hastype() haven height() - ======= - hasattr() hasattrp() hasattrpval() - hasattrval() hasflag() haspower() - hastype() haven help - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) here hidden() home home() homes html html functions html() i18n ! idle() idlesecs() if() ! ifelse() ilev() iname() ! inc() index index() ! info information functions inherit ! inherit2 insert() interiors ! interiors2 internationalization inum() ! inventory isdaylight() isdbref() ! isint() isnum() isword() ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! ======= ! itemize() items() iter() ! iter2 itext() judge ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-16 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-16 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! itemize() items() iter() ! iter2 itext() judge ! jump_ok jury_ok kill ! last & lastlogout last() lastip ! ======= ! jump_ok jury_ok kill ! last & lastlogout last() lastip ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) ! lastsite lattr() lcon() ! lcstr() ldelete() leave ! left() lemit() lexits() ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! lflags() license light ! link() link_ok linking ! list functions list() listen_parent ! listening listening2 listening3 ! lists lit() ljust() ! lmath() ln() lnum() ! loc() locale localize() ! locate() locate2 lock list ! lock types lock types2 lock() ! ======= ! license light link() ! link_ok linking list functions ! list() listen_parent listening ! listening2 listening3 lists ! lit() ljust() lmath() ! ln() lnum() loc() ! locale localize() locate() ! locate2 lock list lock types ! lock types2 lock() locking ! locklist locks locktypes ! locktypes2 log() logout ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-17 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-17 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! locking locklist locks ! locktypes locktypes2 log() ! logout look look2 ! looping lparent() lplayers() ! lpos() lsearch() lsearch2 ! lsearchr() lstats() lt() ! lte() lvcon() lvexits() ! lvplayers() lwho() macros ! macros2 mail mail functions ! mail() mail-admin mail-folders ! mail-other mail-reading mail-sending ! maildstats() mailfrom() mailfstats() ! mailstats() mailstatus() mailsubject() ! mailtime() map() master room ! match() matchall() matching ! math functions max() me ! ======= ! look look2 looping ! lparent() lplayers() lsearch() lsearch2 lsearchr() lstats() lt() lte() lvcon() lvexits() lvplayers() lwho() --- 285,351 ---- flag list flag permissions flags flags list flags() flags2 flip() floating floor() floordiv() fmod() fold() fold2 folderstats() follow followers() following() foreach() foreach2 fullname() function list functions functions() functions2 For more, see Entries-15 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-15 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! gagged gender get ! get() get_eval() give ! global commands globals go ! going goto grab() graball() grep() grepi() gt() gte() halt hasattr() hasattrp() hasattrpval() hasattrval() hasflag() haspower() hastype() haven height() here hidden() home home() homes html html functions html() i18n ! idle idle() idlesecs() ! if() ifelse() ilev() ! iname() inc() index ! index() info information functions For more, see Entries-16 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-16 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! inherit inherit2 insert() ! interiors interiors2 internationalization ! inum() inventory isdaylight() ! isdbref() isint() isnum() ! isword() itemize() items() ! iter() iter2 itext() ! judge jump_ok jury_ok ! kill last & lastlogout last() ! lastip lastsite lattr() ! lcon() lcstr() ldelete() ! leave left() lemit() ! lexits() lflags() license ! light link() link_ok ! linking list functions list() ! listen_parent listening listening2 ! listening3 lists lit() For more, see Entries-17 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-17 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! ljust() lmath() ln() ! lnum() loc() locale ! localize() locate() locate2 ! lock list lock types lock types2 ! lock() locking locklist ! locks locktypes locktypes2 ! log() logout look ! look2 looping lparent() ! lplayers() lpos() lsearch() lsearch2 lsearchr() lstats() lt() lte() lvcon() lvexits() lvplayers() lwho() *************** *** 599,764 **** mail functions mail() mail-admin mail-folders mail-other mail-reading mail-sending maildstats() mailfrom() mailfstats() mailstats() mailstatus() mailsubject() mailtime() map() master room match() matchall() matching math functions max() me mean() median() member() merge() mid() ! min() mix() mix2 ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) ! ! For more, see Entries-18 ! -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! & Entries-18 ! -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! mean() median() member() ! merge() mid() min() ! mix() mix2 mod() ! modulo() modulus() money ! money() monitor mortal ! move mtime() mudname() ! mul() munge() munge2 ! munge3 mushcode mushcode2 ! mwho() myopic name() ! nand() nattr() nearby() ! neq() new locks newbie ! newbie2 newbie3 news ! next() no_command no_leave ! no_tel no_warn noaccents ! noleave non-standard attributes nor() ! nospoof not() nowarn ! ======= ! mod() modulo() modulus() ! money money() monitor ! mortal move mtime() ! mudname() mul() munge() ! munge2 munge3 mushcode ! mushcode2 mwho() myopic ! name() nand() nattr() ! nearby() neq() new locks ! newbie newbie2 newbie3 ! news next() no_command ! no_leave no_tel no_warn ! noaccents noleave non-standard attributes ! nor() nospoof not() ! nowarn nspemit() null() ! num() obj() object parents ! objeval() objmem() oemit() ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-19 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-19 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! nspemit() null() num() ! obj() object parents objeval() ! objmem() oemit() on-vacation ! opaque open() or() ! ord() orflags() orlflags() ! ======= ! on-vacation opaque open() ! or() ord() orflags() ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) ! owner() page page2 ! paranoid parent parent() ! parents parents2 parents3 ! parents4 parents5 parse() ! patchlevels pcreate() pemit() ! pi() player playermem() ! pmatch() poll() ports() ! pos() pose pose2 ! poss() power() powers list ! powers list2 powers() powers2 ! pueblo pueblo features pueblo() ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! ======= ! puppet puppets puppets2 ! queue quiet quit ! quota() r() r-function ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-20 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-20 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! puppet puppets puppets2 ! queue quiet quit ! quota() r() r-function ! rand() read regedit() ! regeditall() regeditalli() regediti() ! ======= ! rand() read regedit() ! regeditall() regeditalli() regediti() ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) ! regexp regexp classes regexp classes2 ! regexp examples regexp syntax regexp syntax2 ! regexp syntax3 regexp syntax4 regexp syntax5 ! regexp syntax6 regexp syntax7 regexp syntax8 ! regexps regexps2 registers ! registers2 regmatch() regrab() ! regraball() regraballi() regrabi() ! regrep() regular expression functions ! remainder() remit() remove() ! repeat() replace() rest() ! restarts() restarttime() restrict ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! ======= ! restrict2 restrict3 reverse() ! revwords() right() rjust() ! rloc() rnum() room ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) For more, see Entries-21 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-21 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) ! restrict2 restrict3 reverse() ! revwords() right() rjust() ! rloc() rnum() room ! room() round() royalty ! rquota rules s() ! ======= ! room() round() royalty ! rquota rules s() ! >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) ! s-function safe say ! <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) score scramble() screenheight screenwidth search() search2 secs() secure() semaphores semaphores2 semaphores3 semaphores4 semaphores5 semaphores6 semipose session set() setdiff() setinter() setq() setq2 setq3 setr() setting-attributes setunion() sex sha1() shared shared players shared players2 - ======= - score scramble() search() - search2 secs() secure() - semaphores semaphores2 semaphores3 - semaphores4 semaphores5 semaphores6 - semipose session set() - setdiff() setinter() setq() - setq2 setq3 setr() - setting-attributes setunion() sex - sha1() shared shared players - shared players2 shl() shr() - shuffle() sign() sin() - slay softcode sort() - sortby() soundex() soundex2 - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) - - For more, see Entries-22 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - & Entries-22 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) shl() shr() shuffle() sign() sin() slay softcode sort() sortby() --- 353,453 ---- mail functions mail() mail-admin mail-folders mail-other mail-reading mail-sending maildstats() mailfrom() + + For more, see Entries-18 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + & Entries-18 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- mailfstats() mailstats() mailstatus() mailsubject() mailtime() map() master room match() matchall() matching math functions max() me mean() median() member() merge() mid() ! min() mistrust mix() ! mix2 mod() modulo() ! modulus() money money() ! monitor mortal move ! mtime() mudname() mul() ! munge() munge2 munge3 ! mushcode mushcode2 mwho() ! myopic name() nand() ! nattr() nearby() neq() ! new locks newbie newbie2 For more, see Entries-19 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-19 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! newbie3 news next() ! no_command no_leave no_tel ! no_warn noaccents noleave ! non-standard attributes nor() nospoof ! not() nowarn nspemit() ! null() num() obj() ! object parents objeval() objmem() ! oemit() on-vacation opaque ! open() or() ord() ! orflags() orlflags() outputprefix ! outputsuffix owner() page ! page2 paranoid parent ! parent() parents parents2 ! parents3 parents4 parents5 ! parse() patchlevels pcreate() ! pemit() pi() player For more, see Entries-20 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-20 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! playermem() pmatch() poll() ! ports() pos() pose ! pose2 poss() power() ! powers list powers list2 powers() ! powers2 pueblo pueblo features ! pueblo() puppet puppets ! puppets2 queue quiet ! quit quota() r() ! r-function rand() read ! regedit() regeditall() regeditalli() ! regediti() regexp regexp classes ! regexp classes2 regexp examples regexp syntax ! regexp syntax2 regexp syntax3 regexp syntax4 ! regexp syntax5 regexp syntax6 regexp syntax7 ! regexp syntax8 regexps regexps2 ! registers registers2 regmatch() For more, see Entries-21 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-21 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ! regrab() regraball() regraballi() ! regrabi() regrep() ! regular expression functions remainder() ! remit() remove() repeat() ! replace() rest() restarts() ! restarttime() restrict restrict2 ! restrict3 reverse() revwords() ! right() rjust() rloc() ! rnum() room room() ! round() royalty rquota ! rules s() s-function ! safe say scan() score scramble() screenheight screenwidth search() search2 secs() secure() semaphores semaphores2 semaphores3 semaphores4 + + For more, see Entries-22 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + & Entries-22 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- semaphores5 semaphores6 semipose session set() setdiff() setinter() setq() setq2 setq3 setr() setting-attributes setunion() sex sha1() shared shared players shared players2 shl() shr() shuffle() sign() sin() slay softcode sort() sortby() *************** *** 769,804 **** stats() stddev() step2 step() sticky strcat() string functions strings strinsert() stripaccents() stripansi() strlen() strmatch() sub() subj() substitutions substitutions2 substitutions3 substitutions4 success suspect switch wildcards switch() switch2 switchall() switches t() - ======= - soundlike() soundslike() space() - spellnum() splice() spoofing - sqrt() squish() stack - starttime() stats() stddev() - step2 step() sticky - strcat() string functions strings - strinsert() stripaccents() stripansi() - strlen() strmatch() sub() - subj() substitutions substitutions2 - substitutions3 substitutions4 success - suspect switch wildcards switch() - switch2 switchall() switches - t() table() tag() - tagwrap() take tan() - teach tel() tel_ok - temple terse textfile() - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) - - For more, see Entries-23 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - & Entries-23 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) table() tag() tagwrap() take tan() teach tel() tel_ok temple --- 458,474 ---- stats() stddev() step2 step() sticky strcat() string functions strings strinsert() + + For more, see Entries-23 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + & Entries-23 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- stripaccents() stripansi() strlen() strmatch() sub() subj() substitutions substitutions2 substitutions3 substitutions4 success suspect switch wildcards switch() switch2 switchall() switches t() table() tag() tagwrap() take tan() teach tel() tel_ok temple *************** *** 809,844 **** transparent2 trim() trunc() trust trust2 type() types of objects types2 u() u2 ucstr() udefault() ufun() ufun2 uldefault() ulocal() ulocal2 unfindable unfollow uninspected unregistered use user-defined commands user-defined2 utctime() utility functions v() - ======= - think time functions time() - timefmt() timefmt2 timestring() - topics topics2 translation - transparent transparent2 trim() - trunc() trust trust2 - type() types of objects types2 - u() u2 ucstr() - udefault() ufun() ufun2 - uldefault() ulocal() ulocal2 - unfindable unfollow uninspected - unregistered use user-defined commands - user-defined2 utctime() utility functions - v() v-function vadd() - val() valid() vdim() - vdot() verbose verbs - version() visible() visual - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) - - For more, see Entries-24 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - & Entries-24 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - <<<<<<< 1_7_7.312(w)/game/txt/hlp/index.hlp Tue, 14 Jan 2003 23:22:31 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.1.6 660) v-function vadd() val() valid() vdim() vdot() verbose verbs version() --- 479,495 ---- transparent2 trim() trunc() trust trust2 type() types of objects types2 u() + + For more, see Entries-24 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + & Entries-24 + -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- u2 ucstr() udefault() ufun() ufun2 uldefault() ulocal() ulocal2 unfindable unfollow uninspected unregistered use user-defined commands user-defined2 utctime() utility functions v() v-function vadd() val() valid() vdim() vdot() verbose verbs version() *************** *** 849,873 **** where() whisper whisper2 who width() wildcards wipe() with wizard - wordpos() words() wrap() - xget() xor() z_tel - zemit() zfun() zmo - zmp zmr zone - zone master objects zone master rooms zone masters - zone objects zone() zones For more, see Entries-25 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- & Entries-25 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - zones2 - ======= - vmag() vmax() vmin() - vmul() vrml vsub() - vunit() warnings warnings list - warnings list2 where() whisper - whisper2 who wildcards - wipe() with wizard wordpos() words() wrap() xget() xor() z_tel zemit() zfun() zmo --- 500,510 ---- *************** *** 875,881 **** zone master objects zone master rooms zone masters zone objects zone() zones zones2 - >>>>>>> 1_7_6.134/game/txt/hlp/index.hlp Wed, 26 Mar 2003 09:25:17 -0600 dunemush (pennmush/20_index.hlp 1.1.1.1.1.1.1.1.1.2.1.1.2.2.2.2.1.1.1.4.2.1.2.1 660) & &Entries -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- --- 512,517 ---- *** 1_7_7.366/game/restart Thu, 27 Mar 2003 11:45:38 -0600 dunemush (pennmush/39_restart 1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.2.2.4 700) --- 1_7_7.446(w)/game/restart Mon, 05 May 2003 20:47:54 -0500 dunemush (pennmush/39_restart 1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.2.2.4.1.1 700) *************** *** 25,30 **** --- 25,39 ---- # Example (uncomment to use): #LANG=fr_FR + # Time zone stuff + # If you want your MUSH to run in a different timezone than the one + # you're in, you need to identify the target time zone file in + # /usr/share/zoneinfo or /usr/lib/zoneinfo. Then uncomment the next + # two lines and set TZ to the desired timezone file, as shown, with + # an initial colon: + #TZ=:EST5EDT + #export TZ + # If netmush isn't here, they probably didn't make install # In any case, we'd better not proceed. if [ ! -e netmush ]; then *** 1_7_7.366/game/mushcnf.dst Sun, 16 Feb 2003 21:57:00 -0600 dunemush (pennmush/41_mushcnf.ds 1.1.1.19.1.1.1.2.1.1.1.8.1.1.1.1.1.9 600) --- 1_7_7.446(w)/game/mushcnf.dst Thu, 22 May 2003 11:36:52 -0500 dunemush (pennmush/41_mushcnf.ds 1.1.1.19.1.1.1.2.1.1.1.8.1.1.1.1.1.15 600) *************** *** 92,97 **** --- 92,119 ---- # See also: exits_connect_rooms base_room 0 + # The ancestor room. This dbref serves as an 'ultimate parent' to + # all rooms that aren't set ORPHAN. Set to -1 to disable. + ancestor_room -1 + + # The ancestor exit. This dbref serves as an 'ultimate parent' to + # all exits that aren't set ORPHAN. Set to -1 to disable. + ancestor_exit -1 + + # The ancestor thing. This dbref serves as an 'ultimate parent' to + # all things that aren't set ORPHAN. Set to -1 to disable. + ancestor_thing -1 + + # The ancestor player. This dbref serves as an 'ultimate parent' to + # all players that aren't set ORPHAN. Set to -1 to disable. + ancestor_player -1 + + # The default home. This is the room used when an object becomes + # homeless (usually due to its home getting destroyed). It's also + # the place where things get moved to if their location gets + # unsalvageably corrupted. + default_home 0 + # What's the filename of the @sitelock file, that controls # who can connect and who can't access_file access.cnf *************** *** 104,109 **** --- 126,166 ---- names_file names.cnf ### + ### Attribute (chunk) cache + ### + ### PennMUSH can swap rarely-referenced attribute text out to a disk + ### file, and cache often-used attribute text in memory. This + ### can result in substantial (typically 30-50%) savings + ### in process memory use, at the cost of a very small performance hit + ### and no longer being able to do forking dumps. + ### You can control the size of memory cache (or set it so large + ### that nothing is ever swapped to disk), as well as several less + ### important parameters here. + ### + + # The file to store the attribute data in, when not in memory. + # This is relative to the game/ directory. + chunk_swap_file data/chunkswap + + # The amount of memory allowed for the attribute cache, in bytes. + # The actual amount used will be a multiple of 64K slightly less + # the specified amount. You must give it at least 132000 bytes. + # If you want to use an 'infinite' cache, try setting this + # to 2000000000; you'll lose the memory benefits, but you'll still + # gain some locality benefits and overhead savings and you'll + # be able to do forking dumps. + chunk_cache_memory 1000000 + + # The number of attributes that may be moved at one time, with no + # net activity. The higher the value, the faster memory gets + # defragmented, but at a greater CPU cost. + chunk_migrate 500 + + # The number of attributes that may be moved at one time, with net + # activity. + active_chunk_migrate 200 + + ### ### Limits, costs, and other constants ### *************** *** 115,128 **** # If you want one, set idle_timeout to the # number of MINUTES # a player may idle before getting disconnected. # If you don't want a timeout, set it to 0. ! idle_timeout 0 # Should there be a limit on how long connections at the connect screen # (without an associated player) can be idle? # If you want one, set unconnected_idle_timeout to the # number of MINUTES # a connection may idle before getting disconnected. # If you don't want a timeout, set it to 0. ! unconnected_idle_timeout 5 # Should there be a limit on the number of logins the MUSH # can accept? If your operating system has a limited number of --- 172,185 ---- # If you want one, set idle_timeout to the # number of MINUTES # a player may idle before getting disconnected. # If you don't want a timeout, set it to 0. ! idle_timeout 0m # Should there be a limit on how long connections at the connect screen # (without an associated player) can be idle? # If you want one, set unconnected_idle_timeout to the # number of MINUTES # a connection may idle before getting disconnected. # If you don't want a timeout, set it to 0. ! unconnected_idle_timeout 5m # Should there be a limit on the number of logins the MUSH # can accept? If your operating system has a limited number of *************** *** 266,281 **** exit_cost 1 # How often should we run a purge to remove destroyed objects? (seconds) ! purge_interval 601 # How often should we run a dbck to check db consistency? (seconds) ! dbck_interval 599 # If you've got USE_WARNINGS defined, set this to the number # of seconds between MUSH-wide topology # warning checks. Default is 3600 (1 hour). If you set this to 0, # timed MUSH-wide checks will be disabled. ! warn_interval 3600 # If compiled with FLOATING_POINTS support, this controls the # decimal precision of numbers. Default is 6 digits after the --- 323,338 ---- exit_cost 1 # How often should we run a purge to remove destroyed objects? (seconds) ! purge_interval 10m1s # How often should we run a dbck to check db consistency? (seconds) ! dbck_interval 9m59s # If you've got USE_WARNINGS defined, set this to the number # of seconds between MUSH-wide topology # warning checks. Default is 3600 (1 hour). If you set this to 0, # timed MUSH-wide checks will be disabled. ! warn_interval 1h # If compiled with FLOATING_POINTS support, this controls the # decimal precision of numbers. Default is 6 digits after the *************** *** 300,313 **** # On a large MUSH, consider at most once every 3-6 hours. # This cannot be a multiple of any of the timer.c parameters # (so keep it an even number of hours). ! dump_interval 3600 # should I fork a concurrent process to do database dumps? # If I do, your memory requirements will double during the dump. # If I don't, the MUSH will pause while it dumps. # If you're low on memory, don't do this. # If you're on Win32, don't do this; fork() is not defined. ! forking_dump yes # If you're not forking, you get a bunch of messages that you # can set to warn players when the dump is 5 minutes away, --- 357,372 ---- # On a large MUSH, consider at most once every 3-6 hours. # This cannot be a multiple of any of the timer.c parameters # (so keep it an even number of hours). ! dump_interval 1h # should I fork a concurrent process to do database dumps? # If I do, your memory requirements will double during the dump. # If I don't, the MUSH will pause while it dumps. # If you're low on memory, don't do this. + # If you want to use the chunk-based memory allocator, you may not + # be able to do forking dumps, and it will ignore this. # If you're on Win32, don't do this; fork() is not defined. ! forking_dump no # If you're not forking, you get a bunch of messages that you # can set to warn players when the dump is 5 minutes away, *************** *** 375,381 **** # Changing this while using info_slave requires a @shutdown/reboot # to make it take effect. use_ident yes ! ident_timeout 5 ### ### Logging --- 434,440 ---- # Changing this while using info_slave requires a @shutdown/reboot # to make it take effect. use_ident yes ! ident_timeout 5s ### ### Logging *** 1_7_7.366/utils/mkcmds.sh.SH Wed, 25 Dec 2002 16:14:24 -0600 dunemush (pennmush/g/16_mkcmds.sh. 1.4 750) --- 1_7_7.446(w)/utils/mkcmds.sh.SH Thu, 22 May 2003 11:38:03 -0500 dunemush (pennmush/g/16_mkcmds.sh. 1.5 750) *************** *** 98,107 **** --- 98,109 ---- $echo $n "\"$s\", SWITCH_$s$c" >> ../src/temp.c $echo $n "}$c" >> ../src/temp.c $echo "," >> ../src/temp.c + $echo $n "." snum=`expr $snum + 1` done $echo "{NULL, 0}" >> ../src/temp.c $echo "};" >> ../src/temp.c + $echo "" # NUM_BYTES from command.h * 8. if [ $snum -gt 160 ]; then *************** *** 118,124 **** --- 120,128 ---- $echo "/* AUTOGENERATED FILE. DO NOT EDIT */" > ../hdrs/temp.h for c in `grep "^COMMAND *(" ../src/*.c | cut -f2 -d\( | cut -f1 -d\) | sort | uniq`; do $echo >>../hdrs/temp.h "COMMAND_PROTO($c);" + $echo $n "." done + $echo "" cmp -s ../hdrs/cmds.h ../hdrs/temp.h cmpstat=$? if [ $cmpstat -eq 0 ]; then *************** *** 138,144 **** --- 142,150 ---- $echo "/* AUTOGENERATED FILE. DO NOT EDIT */" > ../hdrs/temp.h for c in `grep "^FUNCTION *(" ../src/*.c | cut -f2 -d\( | cut -f1 -d\) | sort | uniq`; do $echo >>../hdrs/temp.h "FUNCTION_PROTO($c);" + $echo $n "." done + $echo "" cmp -s ../hdrs/funs.h ../hdrs/temp.h cmpstat=$? if [ $cmpstat -eq 0 ]; then *** 1_7_7.366/src/shs.c Sun, 30 Mar 2003 21:46:27 -0600 dunemush (pennmush/b/18_shs.c 1.8 660) --- 1_7_7.446(w)/src/shs.c Thu, 29 May 2003 10:01:35 -0500 dunemush (pennmush/b/18_shs.c 1.10 660) *************** *** 22,27 **** --- 22,29 ---- #include "shs.h" #include "confmagic.h" + #ifndef DOXYGEN_SHOULD_SKIP_THIS + /* The SHS f() functions */ #define f1(x, y, z) ( (x & y) | (~x & z) ) /* rounds 0-19 */ *************** *** 103,108 **** --- 105,112 ---- LONG h0, h1, h2, h3, h4; LONG A, B, C, D, E; + #endif /* DOXYGEN_SHOULD_SKIP_THIS */ + /* Initialise the SHS values */ /** Initialize SHS values. *** 1_7_7.366/src/local.dst Sat, 29 Mar 2003 14:37:53 -0600 dunemush (pennmush/b/19_local.dst 1.15 660) --- 1_7_7.446(w)/src/local.dst Thu, 29 May 2003 10:01:23 -0500 dunemush (pennmush/b/19_local.dst 1.16 660) *************** *** 184,198 **** switch (type) { case INTERACT_SEE: ! /* Someone standing in a room, or walking into a room, or doing * @verb type stuff that's @bar, @obar, and @abar ! /* Probably a good idea */ if (See_All(to)) return 1; break; case INTERACT_HEAR: /* People talking */ --- 184,209 ---- switch (type) { case INTERACT_SEE: ! /* Someone standing in a room, or doing * @verb type stuff that's @bar, @obar, and @abar + */ ! /* Probably a good idea */ if (See_All(to)) return 1; break; + case INTERACT_PRESENCE: + /* Someone arriving or leaving, connecting or disconnecting, + * and (for objects) growing or losing ears. + */ + + /* To prevent spying, always notice presence */ + return 1; + + break; + case INTERACT_HEAR: /* People talking */ *** 1_7_7.366/src/wiz.c Wed, 26 Mar 2003 15:33:41 -0600 dunemush (pennmush/b/23_wiz.c 1.44.1.1.1.1.1.2.1.7.1.1.1.1.1.1.1.1.1.1.1.8.1.2.2.2.1.2.1.1.1.1.1.1.1.2.1.1.1.2.2.6 660) --- 1_7_7.446(w)/src/wiz.c Thu, 29 May 2003 10:01:40 -0500 dunemush (pennmush/b/23_wiz.c 1.44.1.1.1.1.1.2.1.7.1.1.1.1.1.1.1.1.1.1.1.8.1.2.2.2.1.2.1.1.1.1.1.1.1.2.1.1.1.2.2.11 660) *************** *** 1,10 **** ! /* wiz.c */ #include "copyrite.h" #include "config.h" - /* Wizard-only commands */ - #ifdef I_UNISTD #include #endif --- 1,14 ---- ! /** ! * \file wiz.c ! * ! * \brief Wizard commands in PennMUSH. ! * ! * ! */ #include "copyrite.h" #include "config.h" #ifdef I_UNISTD #include #endif *************** *** 55,62 **** extern int paranoid_dump; extern int paranoid_checkpt; - extern int invok_counter; /* invocation limit */ - #ifndef WIN32 #ifdef I_SYS_FILE --- 59,64 ---- *************** *** 65,73 **** extern int reserved; #endif ! int tport_dest_ok(dbref player, dbref victim, dbref dest); ! int tport_control_ok(dbref player, dbref victim, dbref loc); static int mem_usage(dbref thing); #ifdef INFO_SLAVE void kill_info_slave(void); --- 67,78 ---- extern int reserved; #endif ! static int tport_dest_ok(dbref player, dbref victim, dbref dest); ! static int tport_control_ok(dbref player, dbref victim, dbref loc); static int mem_usage(dbref thing); + static int raw_search(dbref player, const char *owner, const char *class, + const char *restriction, const char *start, + const char *stop, dbref **result, PE_Info * pe_info); #ifdef INFO_SLAVE void kill_info_slave(void); *************** *** 76,86 **** extern char confname[BUFFER_LEN]; extern char errlog[BUFFER_LEN]; dbref ! do_pcreate(creator, player_name, player_password) ! dbref creator; ! const char *player_name; ! const char *player_password; { dbref player; --- 81,97 ---- extern char confname[BUFFER_LEN]; extern char errlog[BUFFER_LEN]; + /** Create a player by Wizard fiat. + * \verbatim + * This implements @pcreate. + * \endverbatim + * \param creator the enactor. + * \param player_name name of player to create. + * \param player_password password for player. + * \return dbref of created player object, or NOTHING if failure. + */ dbref ! do_pcreate(dbref creator, const char *player_name, const char *player_password) { dbref player; *************** *** 107,120 **** } #ifdef QUOTA void ! do_quota(player, arg1, arg2, set_q) ! dbref player; ! const char *arg1; ! const char *arg2; ! int set_q; /* 0 = @quota, 1 = @squota */ { - dbref who, thing; int owned, limit, adjust; --- 118,135 ---- } #ifdef QUOTA + /** Set or check a player's quota. + * \verbatim + * This implements @quota and @squota. + * \endverbatim + * \param player the enactor. + * \param arg1 name of player whose quota should be set or checked. + * \param arg2 amount to set or adjust quota, ignored if checking. + * \param seq_q if 1, set quota; if 0, check quota. + */ void ! do_quota(dbref player, const char *arg1, const char *arg2, int set_q) { dbref who, thing; int owned, limit, adjust; *************** *** 183,193 **** } void ! do_allquota(player, arg1, quiet) ! dbref player; ! const char *arg1; ! int quiet; { int oldlimit, limit, owned; dbref who, thing; --- 198,213 ---- } + /** Check or set quota globally. + * \verbatim + * This implements @allquota. + * \endverbatim + * \param player the enactor. + * \param arg1 new quota limit, as a string. + * \param quiet if 1, don't display every player's quota. + */ void ! do_allquota(dbref player, const char *arg1, int quiet) { int oldlimit, limit, owned; dbref who, thing; *************** *** 248,258 **** } #endif /* QUOTA */ ! int ! tport_dest_ok(player, victim, dest) ! dbref player; ! dbref victim; ! dbref dest; { /* can player legitimately send something to dest */ --- 268,275 ---- } #endif /* QUOTA */ ! static int ! tport_dest_ok(dbref player, dbref victim, dbref dest) { /* can player legitimately send something to dest */ *************** *** 279,289 **** return 0; } ! int ! tport_control_ok(player, victim, loc) ! dbref player; ! dbref victim; ! dbref loc; { /* can player legitimately move victim from loc */ --- 296,303 ---- return 0; } ! static int ! tport_control_ok(dbref player, dbref victim, dbref loc) { /* can player legitimately move victim from loc */ *************** *** 304,315 **** return 0; } void ! do_teleport(player, arg1, arg2, silent) ! dbref player; ! const char *arg1; ! const char *arg2; ! int silent; { dbref victim; dbref destination; --- 318,334 ---- return 0; } + /** Teleport something somewhere. + * \verbatim + * This implements @tel. + * \endverbatim + * \param player the enactor. + * \param arg1 the object to teleport (or location if no object given) + * \param arg2 the location to teleport to. + * \param silent if 1, don't trigger teleport messagse. + */ void ! do_teleport(dbref player, const char *arg1, const char *arg2, int silent) { dbref victim; dbref destination; *************** *** 527,537 **** } } void ! do_force(player, what, command) ! dbref player; ! const char *what; ! char *command; { dbref victim; int j; --- 546,561 ---- } } + /** Force an object to run a command. + * \verbatim + * This implements @force. + * \endverbatim + * \param player the enactor. + * \param what name of the object to force. + * \param command command to force the object to run. + */ void ! do_force(dbref player, const char *what, char *command) { dbref victim; int j; *************** *** 564,580 **** parse_que(victim, command, player); } int ! force_by_number(player, command) ! dbref player; ! char *command; ! { ! /* the command handler has given us something of the format ! * <#dbref> . Split it up and pass it to @force handler. ! * We can hack it up in-place, since we won't need it the input again. ! */ ! char *s; for (s = command; *s && !isspace((unsigned char) *s); s++) ; if (!*s) --- 588,606 ---- parse_que(victim, command, player); } + /** Parse a command of the format "#dbref command". + * Split it up and pass it to force handler. + * We can hack it up in-place, since we won't need it the input again. + * \param player the enactor. + * \param command command string to parse and pass on. + * \retval 0 failed to successfully parse. + * \retval 1 parsed and ran. + */ int ! force_by_number(dbref player, char *command) ! { char *s; + /* It's as easy to learn as your A, B, C's... */ for (s = command; *s && !isspace((unsigned char) *s); s++) ; if (!*s) *************** *** 587,595 **** extern struct db_stat_info current_state; struct db_stat_info * ! get_stats(owner) ! dbref owner; { dbref i; static struct db_stat_info si; --- 613,624 ---- extern struct db_stat_info current_state; + /** Count up the number of objects of each type owned. + * \param owner player to count for (or ANY_OWNER for all). + * \return pointer to a static db_stat_info structure. + */ struct db_stat_info * ! get_stats(dbref owner) { dbref i; static struct db_stat_info si; *************** *** 626,635 **** return &si; } void ! do_stats(player, name) ! dbref player; ! const char *name; { struct db_stat_info *si; dbref owner; --- 655,669 ---- return &si; } + /** The stats command. + * \verbatim + * This implements @stats. + * \endverbatim + * \param player the enactor. + * \param name name of player to check object stats for. + */ void ! do_stats(dbref player, const char *name) { struct db_stat_info *si; dbref owner; *************** *** 674,684 **** } } void ! do_newpassword(player, name, password) ! dbref player; ! const char *name; ! const char *password; { dbref victim; if ((victim = lookup_player(name)) == NOTHING) { --- 708,723 ---- } } + /** Reset a player's password. + * \verbatim + * This implements @newpassword. + * \endverbatim + * \param player the enactor. + * \param name the name of the player whose password is to be reset. + * \param password the new password for the player. + */ void ! do_newpassword(dbref player, const char *name, const char *password) { dbref victim; if ((victim = lookup_player(name)) == NOTHING) { *************** *** 698,712 **** } } void ! do_boot(player, name, flag) ! dbref player; ! const char *name; ! int flag; ! ! ! /* 0, normal boot. 1, descriptor boot */ ! /* 2, self boot */ { dbref victim; DESC *d = NULL; --- 737,752 ---- } } + /** Disconnect a player, forcibly. + * \verbatim + * This implements @boot. + * \endverbatim + * \param player the enactor. + * \param name name of the player or descriptor to boot. + * \param flag 0=normal, 1=boot by descriptor, 2=self-boot + */ void ! do_boot(dbref player, const char *name, int flag) { dbref victim; DESC *d = NULL; *************** *** 785,796 **** } } void ! do_chownall(player, name, target, preserve) ! dbref player; ! const char *name; ! const char *target; ! int preserve; { int i; dbref victim; --- 825,841 ---- } } + /** Chown all of a player's objects. + * \verbatim + * This implements @chownall + * \endverbatim + * \param player the enactor. + * \param name name of player whose objects are to be chowned. + * \param target name of new owner for objects. + * \param preserve if 1, keep privileges and don't halt objects. + */ void ! do_chownall(dbref player, const char *name, const char *target, int preserve) { int i; dbref victim; *************** *** 833,843 **** notify_format(player, T("Ownership changed for %d objects."), count); } void ! do_chzoneall(player, name, target) ! dbref player; ! const char *name; ! const char *target; { int i; dbref victim; --- 878,893 ---- notify_format(player, T("Ownership changed for %d objects."), count); } + /** Change the zone of all of a player's objects. + * \verbatim + * This implements @chzoneall. + * \endverbatim + * \param player the enactor. + * \param name name of player whose objects should be rezoned. + * \param target string containing new zone master for objects. + */ void ! do_chzoneall(dbref player, const char *name, const char *target) { int i; dbref victim; *************** *** 884,896 **** * Nasty management: @kick, examine/debug */ void ! do_kick(player, num) ! dbref player; ! const char *num; { - /* executes commands off the queue immediately */ - int n; if (!Wizard(player)) { --- 934,949 ---- * Nasty management: @kick, examine/debug */ + /** Execute a number of commands off the queue immediately. + * \verbatim + * This implements @kick, which is nasty. + * \endverbatim + * \param player the enactor. + * \param num string containing number of commands to run from the queue. + */ void ! do_kick(dbref player, const char *num) { int n; if (!Wizard(player)) { *************** *** 910,925 **** n = do_top(n); notify_format(player, T("%d commands executed."), n); - } void ! do_debug_examine(player, name) ! dbref player; ! const char *name; { #ifdef USE_MAILER ! struct mail *mp; #endif dbref thing; --- 963,981 ---- n = do_top(n); notify_format(player, T("%d commands executed."), n); } + /** examine/debug. + * This implements examine/debug, which provides some raw values for + * object structure elements of an examined object. + * \param player the enactor. + * \param name name of object to examine. + */ void ! do_debug_examine(dbref player, const char *name) { #ifdef USE_MAILER ! MAIL *mp; #endif dbref thing; *************** *** 969,974 **** --- 1025,1038 ---- * Powers stuff */ + /** Set a power on an object. + * \verbatim + * This implements @power. + * \endverbatim + * \param player the enactor. + * \param name name of the object on which to set the power. + * \param power name of the power to set. + */ void do_power(dbref player, const char *name, const char *power) { *************** *** 1027,1037 **** * Search functions */ ! ! int raw_search(dbref player, const char *owner, const char *class, ! const char *restriction, const char *start, const char *stop, ! dbref **result, PE_Info * pe_info); ! void do_search(dbref player, const char *arg1, char **arg3) { --- 1091,1104 ---- * Search functions */ ! /** User command to search the db for matching objects. ! * \verbatim ! * This implements @search. ! * \endverbatim ! * \param player the enactor. ! * \param arg1 name of player whose objects are to be searched. ! * \param arg3 additional search arguments. ! */ void do_search(dbref player, const char *arg1, char **arg3) { *************** *** 1240,1251 **** } ! enum search_class { S_OWNER, S_TYPE, S_PARENT, S_ZONE, S_FLAG, ! S_POWER, S_EVAL, S_NAME, S_LFLAG }; /* Does the actual searching */ ! int raw_search(dbref player, const char *owner, const char *class, const char *restriction, const char *start, const char *stop, dbref **result, PE_Info * pe_info) --- 1307,1327 ---- } ! /** Type of limitation to apply to a search of the db */ ! enum search_class { ! S_OWNER, /**< Limit to a single owner */ ! S_TYPE, /**< Limit to a single type */ ! S_PARENT, /**< Limit to objects with a given parent */ ! S_ZONE, /**< Limit to objects in a given zone */ ! S_FLAG, /**< Limit to objects with given flag characters */ ! S_POWER, /**< Limit to objects with given power */ ! S_EVAL, /**< Limit to objects for which an expression evals true */ ! S_NAME, /**< Limit to objects prefix-matching a given name */ ! S_LFLAG /**< Limit to objects with given flag names */ }; /* Does the actual searching */ ! static int raw_search(dbref player, const char *owner, const char *class, const char *restriction, const char *start, const char *stop, dbref **result, PE_Info * pe_info) *************** *** 1629,1642 **** } #endif void ! do_sitelock(player, site, opts, who, type) ! dbref player; ! const char *site; ! const char *opts; ! const char *who; ! int type; ! /* 0 = registration, 1 = siteban, 2 = names, 3 = remove, 4 = check */ { FILE *fp, *fptmp; char buffer[80]; --- 1705,1723 ---- } #endif + /** Modify access rules for a site or a player name. + * \verbatim + * This implements @sitelock. + * \endverbatim + * \param player the enactor. + * \param site name of site (or player name to forbid) + * \param opts access rules to apply. + * \param who string containing dbref of player to whom rule applies. + * \param type command switches: 0=/register, 1=/ban, 2=/name, 3=/remove, 4=/check. + */ void ! do_sitelock(dbref player, const char *site, const char *opts, const char *who, ! int type) { FILE *fp, *fptmp; char buffer[80]; *************** *** 1821,1828 **** */ static int ! mem_usage(thing) ! dbref thing; { int k; ATTR *m; --- 1902,1908 ---- */ static int ! mem_usage(dbref thing) { int k; ATTR *m; *************** *** 1903,1915 **** } ! /* ! * --------------------------------------------------------------------------- ! * do_reboot: Reboots the game w/o disconnecting players. ! */ void ! do_reboot(player, flag) ! dbref player; ! int flag; /* 0 = normal, 1 = paranoid */ { #ifdef macintosh notify(player, T("Alas, I can't reboot under MacOS. Sorry.")); --- 1983,1999 ---- } ! /** Reboot the game without disconnecting players. ! * \verbatim ! * This implements @shutdown/reboot, which performs a dump, saves ! * information about which player is associated with which socket, ! * and then re-execs the mush process without closing the sockets. ! * \endverbatim ! * \param player the enactor. ! * \param flag if 0, normal dump; if 1, paranoid dump. ! */ ! void ! do_reboot(dbref player, int flag) { #ifdef macintosh notify(player, T("Alas, I can't reboot under MacOS. Sorry.")); *** 1_7_7.366/src/wild.c Tue, 08 Apr 2003 22:26:53 -0500 dunemush (pennmush/b/24_wild.c 1.11.1.1.1.8.1.1.1.4.1.2.1.1 660) --- 1_7_7.446(w)/src/wild.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/24_wild.c 1.11.1.1.1.8.1.1.1.4.1.2.1.2 660) *************** *** 36,47 **** #include "pcre.h" #include "confmagic.h" #define FIXCASE(a) (DOWNCASE(a)) #define EQUAL(cs,a,b) ((cs) ? (a == b) : (FIXCASE(a) == FIXCASE(b))) #define NOTEQUAL(cs,a,b) ((cs) ? (a != b) : (FIXCASE(a) != FIXCASE(b))) #define NUMARGS (10) ! const unsigned char *tables = NULL; static char wspace[3 * BUFFER_LEN + NUMARGS]; /* argument return buffer */ /* big to match tprintf */ --- 36,51 ---- #include "pcre.h" #include "confmagic.h" + /** Force a char to be lowercase */ #define FIXCASE(a) (DOWNCASE(a)) + /** Check for equality of characters, maybe case-sensitive */ #define EQUAL(cs,a,b) ((cs) ? (a == b) : (FIXCASE(a) == FIXCASE(b))) + /** Check for inequality of characters, maybe case-sensitive */ #define NOTEQUAL(cs,a,b) ((cs) ? (a != b) : (FIXCASE(a) != FIXCASE(b))) + /** Maximum number of wildcarded arguments */ #define NUMARGS (10) ! const unsigned char *tables = NULL; /** Pointer to character tables */ static char wspace[3 * BUFFER_LEN + NUMARGS]; /* argument return buffer */ /* big to match tprintf */ *** 1_7_7.366/src/warnings.c Tue, 08 Apr 2003 22:26:53 -0500 dunemush (pennmush/b/25_warnings.c 1.21.1.3 660) --- 1_7_7.446(w)/src/warnings.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/25_warnings.c 1.21.1.1.1.5 660) *************** *** 24,46 **** #include "confmagic.h" ! #define W_UNLOCKED 0x1 ! #define W_LOCKED 0x2 ! #define W_EXIT_ONEWAY 0x1 ! #define W_EXIT_MULTIPLE 0x2 ! #define W_EXIT_MSGS 0x4 ! #define W_EXIT_DESC 0x8 ! #define W_EXIT_UNLINKED 0x10 /* Space for more exit stuff */ ! #define W_THING_MSGS 0x100 ! #define W_THING_DESC 0x200 /* Space for more thing stuff */ ! #define W_ROOM_DESC 0x1000 /* Space for more room stuff */ ! #define W_PLAYER_DESC 0x10000 ! #define W_LOCK_PROBS 0x100000 /* Groups of warnings */ #define W_NONE 0 --- 24,49 ---- #include "confmagic.h" ! /* We might check for both locked and unlocked warnings if we can't ! * figure out a lock. ! */ ! #define W_UNLOCKED 0x1 /**< Check for unlocked-object warnings */ ! #define W_LOCKED 0x2 /**< Check for locked-object warnings */ ! #define W_EXIT_ONEWAY 0x1 /**< Find one-way exits */ ! #define W_EXIT_MULTIPLE 0x2 /**< Find multiple exits to same place */ ! #define W_EXIT_MSGS 0x4 /**< Find exits without messages */ ! #define W_EXIT_DESC 0x8 /**< Find exits without descs */ ! #define W_EXIT_UNLINKED 0x10 /**< Find unlinked exits */ /* Space for more exit stuff */ ! #define W_THING_MSGS 0x100 /**< Find things without messages */ ! #define W_THING_DESC 0x200 /**< Find things without descs */ /* Space for more thing stuff */ ! #define W_ROOM_DESC 0x1000 /**< Find rooms without descs */ /* Space for more room stuff */ ! #define W_PLAYER_DESC 0x10000 /**< Find players without descs */ ! #define W_LOCK_PROBS 0x100000 /**< Find bad locks */ /* Groups of warnings */ #define W_NONE 0 *************** *** 49,62 **** #define W_EXTRA (W_NORMAL|W_THING_MSGS) #define W_ALL (W_EXTRA|W_EXIT_DESC) typedef long int warn_type; /** A structure representing a topology warning check. */ ! typedef struct a_tcheck tcheck; ! struct a_tcheck { const char *name; /**< Name of warning. */ warn_type flag; /**< Bitmask of warning. */ ! }; static int warning_lock_type(const struct boolexp *l); --- 52,65 ---- #define W_EXTRA (W_NORMAL|W_THING_MSGS) #define W_ALL (W_EXTRA|W_EXIT_DESC) + /** The type that stores the warning bitmask */ typedef long int warn_type; /** A structure representing a topology warning check. */ ! typedef struct a_tcheck { const char *name; /**< Name of warning. */ warn_type flag; /**< Bitmask of warning. */ ! } tcheck; static int warning_lock_type(const struct boolexp *l); *************** *** 230,236 **** complain(player, i, "exit-unlinked", T("Variable exit has no %s attribute"), var); else { ! const char *x = uncompress(AL_STR(a)); if (!x || !*x) complain(player, i, "exit-unlinked", T("Variable exit has empty %s attribute"), var); --- 233,239 ---- complain(player, i, "exit-unlinked", T("Variable exit has no %s attribute"), var); else { ! const char *x = atr_value(a); if (!x || !*x) complain(player, i, "exit-unlinked", T("Variable exit has empty %s attribute"), var); *************** *** 245,251 **** !atr_get(i, "SUCCESS"))) complain(player, i, "exit-msgs", T("possibly unlocked exit missing succ/osucc/odrop")); ! if ((lt & W_LOCKED) && !atr_get(i, "FAIL")) complain(player, i, "exit-msgs", T("possibly locked exit missing fail")); } --- 248,254 ---- !atr_get(i, "SUCCESS"))) complain(player, i, "exit-msgs", T("possibly unlocked exit missing succ/osucc/odrop")); ! if ((lt & W_LOCKED) && !atr_get(i, "FAILURE")) complain(player, i, "exit-msgs", T("possibly locked exit missing fail")); } *************** *** 306,312 **** !atr_get(i, "SUCCESS") || !atr_get(i, "DROP"))) complain(player, i, "thing-msgs", T("possibly unlocked thing missing succ/osucc/drop/odrop")); ! if ((lt & W_LOCKED) && !atr_get(i, "FAIL")) complain(player, i, "thing-msgs", T("possibly locked thing missing fail")); } --- 309,315 ---- !atr_get(i, "SUCCESS") || !atr_get(i, "DROP"))) complain(player, i, "thing-msgs", T("possibly unlocked thing missing succ/osucc/drop/odrop")); ! if ((lt & W_LOCKED) && !atr_get(i, "FAILURE")) complain(player, i, "thing-msgs", T("possibly locked thing missing fail")); } *** 1_7_7.366/src/utils.c Mon, 31 Mar 2003 12:17:08 -0600 dunemush (pennmush/b/27_utils.c 1.30.1.1.1.9 660) --- 1_7_7.446(w)/src/utils.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/27_utils.c 1.30.1.1.1.12 660) *************** *** 24,33 **** #ifdef I_SYS_STAT #include #endif - #define I_FCNTL - #ifdef I_FCNTL #include - #endif #ifdef I_UNISTD #include #endif --- 24,30 ---- *************** *** 493,499 **** return low + (n % x); } ! /* Return an object's name, but for exits, return just the first * component. We expect a valid object. * \param it dbref of object. * \return object's short name. --- 490,496 ---- return low + (n % x); } ! /** Return an object's name, but for exits, return just the first * component. We expect a valid object. * \param it dbref of object. * \return object's short name. *************** *** 542,550 **** /** Can one object interact with/perceive another in a given way? * This funtion checks to see if 'to' can perceive something from * 'from'. The types of interactions currently supported include: ! * INTERACT_SEE (will light rays from from reach to?), INTERACT_HEAR ! * (will sound from from reach to?), and INTERACT_MATCH (can 'to' ! * match the name of 'from'). * \param from object of interaction. * \param to subject of interaction, attempting to interact with from. * \param type type of interaction. --- 539,549 ---- /** Can one object interact with/perceive another in a given way? * This funtion checks to see if 'to' can perceive something from * 'from'. The types of interactions currently supported include: ! * INTERACT_SEE (will light rays from 'from' reach 'to'?), INTERACT_HEAR ! * (will sound from 'from' reach 'to'?), INTERACT_MATCH (can 'to' ! * match the name of 'from'?), and INTERACT_PRESENCE (will the arrival/ ! * departure/connection/disconnection/growing ears/losing ears of ! * 'from' be noticed by 'to'?). * \param from object of interaction. * \param to subject of interaction, attempting to interact with from. * \param type type of interaction. *** 1_7_7.366/src/unparse.c Mon, 06 Jan 2003 22:59:04 -0600 dunemush (pennmush/b/28_unparse.c 1.16.1.7.1.5 660) --- 1_7_7.446(w)/src/unparse.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/28_unparse.c 1.16.1.7.1.9 660) *************** *** 1,4 **** ! /* unparse.c */ #include "copyrite.h" #include "config.h" --- 1,11 ---- ! /** ! * \file unparse.c ! * ! * \brief Convert lots of things into strings for PennMUSH. ! * ! * ! */ ! #include "copyrite.h" #include "config.h" *************** *** 18,27 **** /* Hack added by Thorvald for object_header Pueblo */ static int couldunparse; const char * ! unparse_object(player, loc) ! dbref player; ! dbref loc; { static PUEBLOBUFF; const char *result; --- 25,40 ---- /* Hack added by Thorvald for object_header Pueblo */ static int couldunparse; + /** Format an object's name (and dbref and flags). + * This is a wrapper for real_unparse() that conditionally applies + * pueblo xch_cmd tags to make the object's name a hyperlink to examine + * it. + * \param player the looker. + * \param loc the object being looked at. + * \return static formatted object name string. + */ const char * ! unparse_object(dbref player, dbref loc) { static PUEBLOBUFF; const char *result; *************** *** 36,45 **** } } const char * ! unparse_object_myopic(player, loc) ! dbref player; ! dbref loc; { static PUEBLOBUFF; const char *result; --- 49,64 ---- } } + /** Format an object's name, obeying MYOPIC/ownership rules. + * This is a wrapper for real_unparse() that conditionally applies + * pueblo xch_cmd tags to make the object's name a hyperlink to examine + * it. + * \param player the looker. + * \param loc the object being looked at. + * \return static formatted object name string. + */ const char * ! unparse_object_myopic(dbref player, dbref loc) { static PUEBLOBUFF; const char *result; *************** *** 54,68 **** } } ! /* Like unparse_object, but tell real_unparse to use @NAMEFORMAT if present * This should only be used if we're looking at our container, to prevent * confusion in matching, since you can't match containers with their * names anyway, but only with 'here'. */ const char * ! unparse_room(player, loc) ! dbref player; ! dbref loc; { static PUEBLOBUFF; const char *result; --- 73,91 ---- } } ! /** Format an object's name, obeying MYOPIC/ownership and NAMEFORMAT. ! * \verbatim ! * Like unparse_object, but tell real_unparse to use @NAMEFORMAT if present * This should only be used if we're looking at our container, to prevent * confusion in matching, since you can't match containers with their * names anyway, but only with 'here'. + * \endverbatim + * \param player the looker. + * \param loc the object being looked at. + * \return static formatted object name string. */ const char * ! unparse_room(dbref player, dbref loc) { static PUEBLOBUFF; const char *result; *************** *** 77,82 **** --- 100,117 ---- } } + /** Format an object's name in several ways. + * This function does the real work of converting a dbref to a + * name, possibly including the dbref and flags, possibly using + * a nameformat or nameaccent if present. + * \param player the looker. + * \param loc dbref of the object being looked at. + * \param obey_myopic if 0, always show Name(#xxxFLAGS); if 1, don't + * do so if player is MYOPIC or doesn't own loc. + * \param use_nameformat if 1, apply a NAMEFORMAT attribute if available. + * \param use_nameaccent if 1, apply a NAMEACCENT attribute if available. + * \return address of a static buffer containing the formatted name. + */ const char * real_unparse(dbref player, dbref loc, int obey_myopic, int use_nameformat, int use_nameaccent) *************** *** 141,155 **** } } ! /* The name of loc as seen by a player inside it if nameformat is set */ ! /* This function needs to avoid using a static buffer, so pass in ! * a pointer to an allocated BUFFER_LEN array */ int ! nameformat(player, loc, tbuf1) ! dbref player; ! dbref loc; ! char *tbuf1; { ATTR *a; char *wsave[10], *rsave[NUMQ]; --- 176,193 ---- } } ! /** Build the name of loc as seen by a player inside it, but only ! * if it has a NAMEFORMAT. ! * This function needs to avoid using a static buffer, so pass in ! * a pointer to an allocated BUFFER_LEN array. ! * \param player the looker. ! * \param loc dbref of location being looked at. ! * \param tbuf1 address to store formatted name of loc. ! * \retval 1 a NAMEFORMAT was found, and tbuf1 contains formatted name. ! * \retval 0 no NAMEFORMAT on loc, tbuf1 is undefined. */ int ! nameformat(dbref player, dbref loc, char *tbuf1) { ATTR *a; char *wsave[10], *rsave[NUMQ]; *************** *** 171,177 **** renv[j][0] = '\0'; strcpy(arg, unparse_dbref(loc)); wenv[0] = arg; ! sp = save = safe_uncompress(a->value); bp = tbuf1; process_expression(tbuf1, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); --- 209,215 ---- renv[j][0] = '\0'; strcpy(arg, unparse_dbref(loc)); wenv[0] = arg; ! sp = save = safe_atr_value(a); bp = tbuf1; process_expression(tbuf1, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *************** *** 189,198 **** } } ! /* Give a string representation of a dbref */ char * ! unparse_dbref(num) ! dbref num; { /* Not BUFFER_LEN, but no dbref will come near this long */ static char str[SBUF_LEN]; --- 227,238 ---- } } ! /** Give a string representation of a dbref. ! * \param num value to stringify ! * \return address of static buffer containing stringified value. ! */ char * ! unparse_dbref(dbref num) { /* Not BUFFER_LEN, but no dbref will come near this long */ static char str[SBUF_LEN]; *************** *** 204,213 **** return str; } ! /* Give a string representation of an integer */ char * ! unparse_integer(num) ! int num; { static char str[SBUF_LEN]; char *strp; --- 244,255 ---- return str; } ! /** Give a string representation of an integer. ! * \param num value to stringify ! * \return address of static buffer containing stringified value. ! */ char * ! unparse_integer(int num) { static char str[SBUF_LEN]; char *strp; *************** *** 218,227 **** return str; } ! /* Give a string representation of an unsigned integer */ char * ! unparse_uinteger(num) ! unsigned int num; { static char str[16]; --- 260,271 ---- return str; } ! /** Give a string representation of an unsigned integer. ! * \param num value to stringify ! * \return address of static buffer containing stringified value. ! */ char * ! unparse_uinteger(unsigned int num) { static char str[16]; *************** *** 230,239 **** } ! /* Give a string representation of a number */ char * ! unparse_number(num) ! NVAL num; { static char str[100]; /* Should be large enough for even the HUGE floats */ char *p; --- 274,285 ---- } ! /** Give a string representation of a number. ! * \param num value to stringify ! * \return address of static buffer containing stringified value. ! */ char * ! unparse_number(NVAL num) { static char str[100]; /* Should be large enough for even the HUGE floats */ char *p; *************** *** 250,255 **** --- 296,306 ---- return str; } + /** Return the name of an object, applying NAMEACCENT if set. + * \param thing dbref of object. + * \return address of static buffer containing object name, with accents + * if object has a valid NAMEACCENT attribute. + */ const char * accented_name(dbref thing) { *************** *** 264,270 **** char *bp = fbuf; size_t len; ! strcpy(tbuf, uncompress(AL_STR(na))); len = strlen(Name(thing)); --- 315,321 ---- char *bp = fbuf; size_t len; ! strcpy(tbuf, atr_value(na)); len = strlen(Name(thing)); *** 1_7_7.366/src/timer.c Mon, 31 Mar 2003 12:17:08 -0600 dunemush (pennmush/b/29_timer.c 1.29.1.7.1.5 660) --- 1_7_7.446(w)/src/timer.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/29_timer.c 1.29.1.7.1.8 660) *************** *** 191,198 **** local_timer(); } ! sig_atomic_t cpu_time_limit_hit = 0; ! int cpu_limit_warning_sent = 0; #if defined(HAS_ITIMER) /** Handler for PROF signal. --- 191,198 ---- local_timer(); } ! sig_atomic_t cpu_time_limit_hit = 0; /** Was the cpu time limit hit? */ ! int cpu_limit_warning_sent = 0; /** Have we issued a cpu limit warning? */ #if defined(HAS_ITIMER) /** Handler for PROF signal. *************** *** 213,219 **** cpu_time_limit_hit = 1; } #endif ! int timer_set = 0; /** Start the cpu timer (before running a command). */ --- 213,219 ---- cpu_time_limit_hit = 1; } #endif ! int timer_set = 0; /**< Is a CPU timer set? */ /** Start the cpu timer (before running a command). */ *** 1_7_7.366/src/help.c Sat, 01 Mar 2003 23:51:32 -0600 dunemush (pennmush/f/32_help.c 1.4.1.2.1.1.1.3.2.2.1.1.2.1.1.2.1.2.1.2.1.3.1.10 660) --- 1_7_7.446(w)/src/help.c Thu, 29 May 2003 10:01:30 -0500 dunemush (pennmush/f/32_help.c 1.4.1.2.1.1.1.3.2.2.1.1.2.1.1.2.1.2.1.2.1.3.1.13 660) *************** *** 28,34 **** #include "PMInit.h" #endif ! HASHTAB help_files; static int help_init = 0; --- 28,34 ---- #include "PMInit.h" #endif ! HASHTAB help_files; /**< Help filenames hash table */ static int help_init = 0; *************** *** 43,58 **** char topic[TOPIC_NAME_LEN + 1]; /**< Name of topic */ struct TLIST *next; /**< Pointer to next list entry */ } tlist; - tlist *top = NULL; ! help_indx *topics = NULL; ! unsigned num_topics = 0; ! unsigned top_topics = 0; static void write_topic(long int p); ! #define TRUE 1 ! #define FALSE 0 COMMAND (cmd_helpcmd) { help_file *h; --- 43,59 ---- char topic[TOPIC_NAME_LEN + 1]; /**< Name of topic */ struct TLIST *next; /**< Pointer to next list entry */ } tlist; ! tlist *top = NULL; /**< Pointer to top of linked list of topic names */ ! ! help_indx *topics = NULL; /**< Pointer to linked list of topic indexes */ ! unsigned num_topics = 0; /**< Number of topics loaded */ ! unsigned top_topics = 0; /**< Maximum number of topics loaded */ static void write_topic(long int p); ! #define TRUE 1 /**< A true value */ ! #define FALSE 0 /**< A false value */ COMMAND (cmd_helpcmd) { help_file *h; *** 1_7_7.366/src/htab.c Mon, 03 Mar 2003 23:09:02 -0600 dunemush (pennmush/b/30_htab.c 1.8.1.10 660) --- 1_7_7.446(w)/src/htab.c Thu, 29 May 2003 10:01:30 -0500 dunemush (pennmush/b/30_htab.c 1.8.1.11 660) *************** *** 20,25 **** --- 20,26 ---- #include "confmagic.h" static HASHENT *hash_new(HASHTAB *htab, const char *key); + static int hash_val(register const char *k, int mask); /* --------------------------------------------------------------------------- * hash_val: Compute hash value of a string for a hash table. *************** *** 29,36 **** /* This hash function adapted from http://burtleburtle.net/bob/hash/evahash.html */ ! typedef unsigned long int u4; /* unsigned 4-byte type */ ! typedef unsigned char u1; /* unsigned 1-byte type */ /* The mixing step */ #define mix(a,b,c) \ --- 30,37 ---- /* This hash function adapted from http://burtleburtle.net/bob/hash/evahash.html */ ! typedef unsigned long int u4; /**< unsigned 4-byte type */ ! typedef unsigned char u1; /**< unsigned 1-byte type */ /* The mixing step */ #define mix(a,b,c) \ *************** *** 47,53 **** } /* The whole new hash function */ ! int hash_val(register const char *k, int mask) { register u4 a, b, c; /* the internal state */ --- 48,54 ---- } /* The whole new hash function */ ! static int hash_val(register const char *k, int mask) { register u4 a, b, c; /* the internal state */ *** 1_7_7.366/src/switchinc.c Wed, 09 Apr 2003 14:28:37 -0500 dunemush (pennmush/b/32_switchinc. 1.3.1.2.1.6.1.18.1.2.1.2.2.5.1.4.2.4.1.1.1.2.1.5.1.2.1.5.2.1.1.31.3.4.1.5.1.4.1.1.1.2 660) --- 1_7_7.446(w)/src/switchinc.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/32_switchinc. 1.3.1.2.1.6.1.18.1.2.1.2.2.5.1.4.2.4.1.1.1.2.1.5.1.2.1.5.2.1.1.31.3.4.1.5.1.4.1.1.1.1.1.4 660) *************** *** 14,19 **** --- 14,20 ---- {"BUFFER", SWITCH_BUFFER}, {"CHECK", SWITCH_CHECK}, {"CHOWN", SWITCH_CHOWN}, + {"CHUNKS", SWITCH_CHUNKS}, {"CLEAR", SWITCH_CLEAR}, {"CMD", SWITCH_CMD}, {"COMMANDS", SWITCH_COMMANDS}, *************** *** 79,84 **** --- 80,86 ---- {"ON", SWITCH_ON}, {"OUTSIDE", SWITCH_OUTSIDE}, {"OVERRIDE", SWITCH_OVERRIDE}, + {"PAGING", SWITCH_PAGING}, {"PANIC", SWITCH_PANIC}, {"PARANOID", SWITCH_PARANOID}, {"PLAYERS", SWITCH_PLAYERS}, *************** *** 92,97 **** --- 94,100 ---- {"READ", SWITCH_READ}, {"REBOOT", SWITCH_REBOOT}, {"RECALL", SWITCH_RECALL}, + {"REGIONS", SWITCH_REGIONS}, {"REGISTER", SWITCH_REGISTER}, {"REMOVE", SWITCH_REMOVE}, {"RENAME", SWITCH_RENAME}, *** 1_7_7.366/src/strutil.c Sat, 05 Apr 2003 17:37:13 -0600 dunemush (pennmush/b/33_strutil.c 1.28.1.3.1.3.1.7.2.1.1.2.1.1.1.1.1.1.1.21.1.2.1.2.1.2 660) --- 1_7_7.446(w)/src/strutil.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/33_strutil.c 1.28.1.3.1.3.1.7.2.1.1.2.1.1.1.1.1.1.1.21.1.2.1.2.1.4 660) *************** *** 554,563 **** } ! /* Macros used by the safe_XXX functions to append a ! * string c to the end of buff, starting at *bp. ! */ #define APPEND_ARGS int len, blen, clen #define APPEND_TO_BUF \ /* Trivial cases */ \ if (c[0] == '\0') \ --- 554,564 ---- } ! /** Define the args used in APPEND_TO_BUF */ #define APPEND_ARGS int len, blen, clen + /** Append a string c to the end of buff, starting at *bp. + * This macro is used by the safe_XXX functions. + */ #define APPEND_TO_BUF \ /* Trivial cases */ \ if (c[0] == '\0') \ *************** *** 886,891 **** --- 887,893 ---- return result; } + /** Standard replacer tokens for text and position */ const char *standard_tokens[2] = { "##", "#@" }; /* Replace two tokens in a string at once. All-around better than calling *************** *** 1554,1559 **** --- 1556,1562 ---- static int is_ansi_code(const char *s); static int is_start_html_code(const char *s) __attribute__ ((__unused__)); static int is_end_html_code(const char *s); + /** Is s a string that signifies the end of ANSI codes? */ #define is_end_ansi_code(s) (!strcmp((s),ANSI_NORMAL)) *** 1_7_7.366/src/speech.c Sun, 30 Mar 2003 22:39:28 -0600 dunemush (pennmush/b/35_speech.c 1.21.1.2.1.3.1.5.1.1.1.7.1.3.1.1.1.8.1.1.1.1.1.1.1.10.1.2.1.13.2.4.1.1.3.5 660) --- 1_7_7.446(w)/src/speech.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/b/35_speech.c 1.21.1.2.1.3.1.5.1.1.1.7.1.3.1.1.1.8.1.1.1.1.1.1.1.10.1.2.1.13.2.4.1.1.3.1.1.5 660) *************** *** 30,36 **** static void oemit_notify_except(dbref loc, dbref exc1, dbref exc2, const char *msg); - const char *reconstruct_message(char *arg1, char *arg2); int okay_pemit(dbref player, dbref target); static dbref speech_loc(dbref thing); void propagate_sound(dbref thing, const char *msg); --- 30,35 ---- *************** *** 135,141 **** recurse = 1; /* Protect use from recursive teach */ notify_except(Contents(loc), NOTHING, tprintf(T("%s types --> %s%s%s"), spname(player), ! ANSI_HILITE, tbuf1, ANSI_NORMAL), NA_INTERACTION); command = mush_strdup(tbuf1, "string"); /* process_command is destructive */ process_command(player, command, cause, 1); mush_free(command, "string"); --- 134,140 ---- recurse = 1; /* Protect use from recursive teach */ notify_except(Contents(loc), NOTHING, tprintf(T("%s types --> %s%s%s"), spname(player), ! ANSI_HILITE, tbuf1, ANSI_NORMAL), NA_INTER_HEAR); command = mush_strdup(tbuf1, "string"); /* process_command is destructive */ process_command(player, command, cause, 1); mush_free(command, "string"); *************** *** 167,173 **** notify_format(player, T("You say, \"%s\""), tbuf1); notify_except(Contents(loc), player, tprintf(T("%s says, \"%s\""), spname(player), tbuf1), ! NA_INTERACTION); } /** The oemit command. --- 166,172 ---- notify_format(player, T("You say, \"%s\""), tbuf1); notify_except(Contents(loc), player, tprintf(T("%s says, \"%s\""), spname(player), tbuf1), ! NA_INTER_HEAR); } /** The oemit command. *************** *** 588,597 **** /* notify everybody */ if (!space) notify_except(Contents(loc), NOTHING, ! tprintf("%s %s", spname(player), tbuf1), NA_INTERACTION); else notify_except(Contents(loc), NOTHING, ! tprintf("%s%s", spname(player), tbuf1), NA_INTERACTION); } /** The *wall commands. --- 587,596 ---- /* notify everybody */ if (!space) notify_except(Contents(loc), NOTHING, ! tprintf("%s %s", spname(player), tbuf1), NA_INTER_HEAR); else notify_except(Contents(loc), NOTHING, ! tprintf("%s%s", spname(player), tbuf1), NA_INTER_HEAR); } /** The *wall commands. *************** *** 737,743 **** } if (repage) { a = atr_get_noparent(player, "LASTPAGED"); ! if (!a || !*((hp = head = safe_uncompress(a->value)))) { notify(player, T("You haven't paged anyone since connecting.")); mush_free((Malloc_t) tbuf2, "string"); return; --- 736,742 ---- } if (repage) { a = atr_get_noparent(player, "LASTPAGED"); ! if (!a || !*((hp = head = safe_atr_value(a)))) { notify(player, T("You haven't paged anyone since connecting.")); mush_free((Malloc_t) tbuf2, "string"); return; *************** *** 914,926 **** ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2, Name(player), gap, message); else ! notify_format(player, T("You paged %s%s with '%s'."), ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2, message); /* Figure out the 'name' of the player */ if (PAGE_ALIASES && (a = atr_get_noparent(player, "ALIAS"))) ! current = tprintf("%s (%s)", Name(player), uncompress(a->value)); else current = (char *) Name(player); --- 913,925 ---- ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2, Name(player), gap, message); else ! notify_format(player, T("You paged %s%s with '%s'"), ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2, message); /* Figure out the 'name' of the player */ if (PAGE_ALIASES && (a = atr_get_noparent(player, "ALIAS"))) ! current = tprintf("%s (%s)", Name(player), atr_value(a)); else current = (char *) Name(player); *************** *** 1025,1031 **** if (!a) return matched; ! filter = safe_uncompress(a->value); temp = filter; for (i = 0; (i < MAX_ARG) && !matched; i++) { --- 1024,1030 ---- if (!a) return matched; ! filter = safe_atr_value(a); temp = filter; for (i = 0; (i < MAX_ARG) && !matched; i++) { *************** *** 1077,1083 **** } wenv[0] = (char *) msg; save_global_regs("prefix_save", preserve); ! asave = safe_uncompress(a->value); ap = asave; process_expression(tbuf1, &bp, &ap, thing, orator, orator, PE_DEFAULT, PT_DEFAULT, NULL); --- 1076,1082 ---- } wenv[0] = (char *) msg; save_global_regs("prefix_save", preserve); ! asave = safe_atr_value(a); ap = asave; process_expression(tbuf1, &bp, &ap, thing, orator, orator, PE_DEFAULT, PT_DEFAULT, NULL); *************** *** 1093,1099 **** return; } ! /* pass a message on, for AUDIBLE, prepending a prefix, unless the * message matches a filter pattern. * \param thing object to check for filter and prefix. * \param msg message to pass. --- 1092,1098 ---- return; } ! /** pass a message on, for AUDIBLE, prepending a prefix, unless the * message matches a filter pattern. * \param thing object to check for filter and prefix. * \param msg message to pass. *************** *** 1178,1184 **** pass[0] = loc; pass[1] = exception; ! notify_anything(orator, na_except, pass, &ns_esnotify, flags, msg); do_audible_stuff(loc, &pass[1], 1, msg); } --- 1177,1183 ---- pass[0] = loc; pass[1] = exception; ! notify_anything(orator, na_except, pass, ns_esnotify, flags, msg); do_audible_stuff(loc, &pass[1], 1, msg); } *************** *** 1189,1197 **** * \param exc1 dbref of one object not to notify, or NOTHING. * \param exc2 dbref of another object not to notify, or NOTHING. * \param msg message to send. */ void ! notify_except2(dbref first, dbref exc1, dbref exc2, const char *msg) { dbref loc; dbref pass[3]; --- 1188,1197 ---- * \param exc1 dbref of one object not to notify, or NOTHING. * \param exc2 dbref of another object not to notify, or NOTHING. * \param msg message to send. + * \param flags interaction flags to control type of interaction. */ void ! notify_except2(dbref first, dbref exc1, dbref exc2, const char *msg, int flags) { dbref loc; dbref pass[3]; *************** *** 1211,1217 **** pass[1] = exc1; pass[2] = exc2; ! notify_anything(orator, na_except2, pass, NULL, NA_INTERACTION, msg); do_audible_stuff(loc, &pass[1], 2, msg); } --- 1211,1217 ---- pass[1] = exc1; pass[2] = exc2; ! notify_anything(orator, na_except2, pass, NULL, flags, msg); do_audible_stuff(loc, &pass[1], 2, msg); } *** 1_7_7.366/src/set.c Sun, 30 Mar 2003 21:46:27 -0600 dunemush (pennmush/b/38_set.c 1.26.1.5.1.1.2.1.1.1.1.1.1.11.1.1.1.9 660) --- 1_7_7.446(w)/src/set.c Thu, 29 May 2003 10:01:35 -0500 dunemush (pennmush/b/38_set.c 1.26.1.5.1.1.2.1.1.1.1.1.1.11.1.1.1.1.1.1.1.6 660) *************** *** 279,285 **** } ! /* Actually change the ownership of something, and fix bits. * \param player the enactor. * \param thing object to change ownership of. * \param newowner new owner for thing. --- 279,285 ---- } ! /** Actually change the ownership of something, and fix bits. * \param player the enactor. * \param thing object to change ownership of. * \param newowner new owner for thing. *************** *** 644,650 **** return; } /* we can read it. Copy the value. */ ! text = safe_uncompress(a->value); /* now we loop through our new object pairs and copy, calling @set. */ for (i = 1; i < MAX_ARG && (newpair[i] != NULL); i++) { --- 644,650 ---- return; } /* we can read it. Copy the value. */ ! text = safe_atr_value(a); /* now we loop through our new object pairs and copy, calling @set. */ for (i = 1; i < MAX_ARG && (newpair[i] != NULL); i++) { *************** *** 695,703 **** char tbuf1[BUFFER_LEN], tbuf_ansi[BUFFER_LEN]; char *tbufp, *tbufap; char **argv = (char **) args; val = argv[1]; ! r = (argv[2]) ? argv[2] : ""; tbufp = tbuf1; tbufap = tbuf_ansi; --- 695,706 ---- char tbuf1[BUFFER_LEN], tbuf_ansi[BUFFER_LEN]; char *tbufp, *tbufap; char **argv = (char **) args; + size_t rlen, vlen; val = argv[1]; ! vlen = strlen(val); ! r = argv[2] ? argv[2] : ""; ! rlen = strlen(r); tbufp = tbuf1; tbufap = tbuf_ansi; *************** *** 710,718 **** notify(player, T("You need to control an attribute to edit it.")); return 0; } ! s = (char *) uncompress(a->value); /* warning: pointer to static buffer */ ! if (!strcmp(val, "$")) { /* append */ safe_str(s, tbuf1, &tbufp); safe_str(r, tbuf1, &tbufp); --- 713,721 ---- notify(player, T("You need to control an attribute to edit it.")); return 0; } ! s = (char *) atr_value(a); /* warning: pointer to static buffer */ ! if (vlen == 1 && *val == '$') { /* append */ safe_str(s, tbuf1, &tbufp); safe_str(r, tbuf1, &tbufp); *************** *** 720,726 **** if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", s, ANSI_HILITE, r, ANSI_NORMAL)) ansi_long_flag = 1; ! } else if (!strcmp(val, "^")) { /* prepend */ safe_str(r, tbuf1, &tbufp); safe_str(s, tbuf1, &tbufp); --- 723,729 ---- if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", s, ANSI_HILITE, r, ANSI_NORMAL)) ansi_long_flag = 1; ! } else if (vlen == 1 && *val == '^') { /* prepend */ safe_str(r, tbuf1, &tbufp); safe_str(s, tbuf1, &tbufp); *************** *** 728,760 **** if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", ANSI_HILITE, r, ANSI_NORMAL, s)) ansi_long_flag = 1; } else { /* find and replace */ ! char *p, *start = s; ! size_t vlen; int too_long = 0; ! /* This will have problems if val is ever "", but do_gedit makes sure ! * it isn't. ! */ ! ! vlen = strlen(val); ! while (*s && (p = strstr(s, val)) != NULL) { ! /* Copy from start to p */ ! *p = '\0'; ! if (safe_str(start, tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { ! if (safe_str(start, tbuf_ansi, &tbufap)) ansi_long_flag = 1; } ! s = p + vlen; ! start = s; /* Copy in r */ ! if (safe_str(r, tbuf1, &tbufp)) { too_long = 1; break; } --- 731,793 ---- if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", ANSI_HILITE, r, ANSI_NORMAL, s)) ansi_long_flag = 1; + } else if (!*val) { + /* insert replacement string between every character */ + ansi_string *haystack; + size_t last = 0; + int too_long = 0; + + haystack = parse_ansi_string(s); + + /* Add one at the start */ + if (!safe_strl(r, rlen, tbuf1, &tbufp)) { + for (last = 0; last < haystack->len; last++) { + /* Add the next character */ + if (safe_ansi_string(haystack, last, 1, tbuf1, &tbufp)) { + too_long = 1; + break; + } + if (!ansi_long_flag) { + if (safe_ansi_string(haystack, last, 1, tbuf_ansi, &tbufap)) + ansi_long_flag = 1; + } + /* Copy in r */ + if (safe_strl(r, rlen, tbuf1, &tbufp)) { + too_long = 1; + break; + } + if (!ansi_long_flag) { + if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r, + ANSI_NORMAL)) + ansi_long_flag = 1; + } + } + } + free_ansi_string(haystack); } else { /* find and replace */ ! ansi_string *haystack; ! size_t last = 0; ! char *p; int too_long = 0; ! haystack = parse_ansi_string(s); ! while (last < haystack->len ! && (p = strstr(haystack->text + last, val)) != NULL) { ! if (safe_ansi_string(haystack, last, p - (haystack->text + last), ! tbuf1, &tbufp)) { too_long = 1; break; } if (!ansi_long_flag) { ! if (safe_ansi_string(haystack, last, p - (haystack->text + last), ! tbuf_ansi, &tbufap)) ansi_long_flag = 1; } ! /* Copy in r */ ! if (safe_strl(r, rlen, tbuf1, &tbufp)) { too_long = 1; break; } *************** *** 763,777 **** ANSI_NORMAL)) ansi_long_flag = 1; } } ! /* No more val's in the string */ ! if (*s && !too_long) { ! safe_str(s, tbuf1, &tbufp); if (!ansi_long_flag) { ! if (safe_str(s, tbuf_ansi, &tbufap)) ansi_long_flag = 1; } } } *tbufp = '\0'; --- 796,811 ---- ANSI_NORMAL)) ansi_long_flag = 1; } + last = p - haystack->text + vlen; } ! if (last < haystack->len && !too_long) { ! safe_ansi_string(haystack, last, haystack->len, tbuf1, &tbufp); if (!ansi_long_flag) { ! if (safe_ansi_string(haystack, last, haystack->len, tbuf_ansi, &tbufap)) ansi_long_flag = 1; } } + free_ansi_string(haystack); } *tbufp = '\0'; *************** *** 966,972 **** } /* everything is okay, do the change */ Parent(thing) = parent; ! notify(player, T("Parent changed.")); } static int --- 1000,1007 ---- } /* everything is okay, do the change */ Parent(thing) = parent; ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify(player, T("Parent changed.")); } static int *** 1_7_7.366/src/rob.c Sat, 29 Mar 2003 14:11:11 -0600 dunemush (pennmush/b/42_rob.c 1.18.1.2.1.3.1.3.1.9 660) --- 1_7_7.446(w)/src/rob.c Thu, 29 May 2003 10:01:35 -0500 dunemush (pennmush/b/42_rob.c 1.18.1.2.1.3.1.3.1.13 660) *************** *** 205,211 **** Name(who)); *bp = '\0'; real_did_it(player, player, "GIVE", tbuf1, "OGIVE", NULL, ! "AGIVE", NOTHING, myenv); /* Notify the object that it's been given */ notify_format(thing, T("%s gave you to %s."), Name(player), Name(who)); --- 205,211 ---- Name(who)); *bp = '\0'; real_did_it(player, player, "GIVE", tbuf1, "OGIVE", NULL, ! "AGIVE", NOTHING, myenv, NA_INTER_SEE); /* Notify the object that it's been given */ notify_format(thing, T("%s gave you to %s."), Name(player), Name(who)); *************** *** 219,225 **** *bp = '\0'; sprintf(myenv[1], "#%d", player); real_did_it(who, who, "RECEIVE", tbuf1, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv); mush_free(myenv[0], "dbref"); mush_free(myenv[1], "dbref"); --- 219,225 ---- *bp = '\0'; sprintf(myenv[1], "#%d", player); real_did_it(who, who, "RECEIVE", tbuf1, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv, NA_INTER_SEE); mush_free(myenv[0], "dbref"); mush_free(myenv[1], "dbref"); *************** *** 251,262 **** if (!payfor(player, amount)) { notify_format(player, T("You don't have that many %s to give!"), MONIES); } else { /* objects work differently */ if (IsThing(who)) { int cost = 0; ATTR *a; a = atr_get(who, "COST"); ! if (a && (amount < (cost = atoi(uncompress(a->value))))) { notify(player, T("Feeling poor today?")); giveto(player, amount); return; --- 251,267 ---- if (!payfor(player, amount)) { notify_format(player, T("You don't have that many %s to give!"), MONIES); } else { + char *pay_env[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char paid[SBUF_LEN], *pb; + + pay_env[0] = pb = paid; + /* objects work differently */ if (IsThing(who)) { int cost = 0; ATTR *a; a = atr_get(who, "COST"); ! if (a && (amount < (cost = atoi(atr_value(a))))) { notify(player, T("Feeling poor today?")); giveto(player, amount); return; *************** *** 271,278 **** } giveto(player, amount - cost); giveto(who, cost); ! did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT", ! NOTHING); return; } else { /* he can do it */ --- 276,285 ---- } giveto(player, amount - cost); giveto(who, cost); ! safe_integer_sbuf(cost, paid, &pb); ! *pb = '\0'; ! real_did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT", ! NOTHING, pay_env, NA_INTER_SEE); return; } else { /* he can do it */ *************** *** 294,301 **** } } giveto(who, amount); ! did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT", ! NOTHING); } } } --- 301,310 ---- } } giveto(who, amount); ! safe_integer_sbuf(amount, paid, &pb); ! *pb = '\0'; ! real_did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT", ! NOTHING, pay_env, NA_INTER_SEE); } } } *** 1_7_7.366/src/predicat.c Sat, 29 Mar 2003 14:29:42 -0600 dunemush (pennmush/b/44_predicat.c 1.1.1.34.1.1.1.3.1.4.2.18 660) --- 1_7_7.446(w)/src/predicat.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/b/44_predicat.c 1.1.1.34.1.1.1.3.1.4.2.24 660) *************** *** 148,154 **** /* no charges set, just execute the action */ return queue_attribute(thing, awhat, player); } else { ! strcpy(tbuf2, uncompress(AL_STR(b))); num = atoi(tbuf2); if (num) { /* charges left, decrement and execute */ --- 148,154 ---- /* no charges set, just execute the action */ return queue_attribute(thing, awhat, player); } else { ! strcpy(tbuf2, atr_value(b)); num = atoi(tbuf2); if (num) { /* charges left, decrement and execute */ *************** *** 181,190 **** { /* Bunch o' nulls */ static char *myenv[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ! real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv); } /** Take an action on an object and trigger attributes. * \verbatim * executes the @attr, @oattr, @aattr for a command - gives a message --- 181,212 ---- { /* Bunch o' nulls */ static char *myenv[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ! real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv, ! NA_INTER_HEAR); } + /** A wrapper for did_it that can pass interaction flags. + * \param player the enactor. + * \param thing object being triggered. + * \param what message attribute for enactor. + * \param def default message to enactor. + * \param owhat message attribute for others. + * \param odef default message to others. + * \param awhat action attribute to trigger. + * \param loc location in which action is taking place. + * \param flags interaction flags to pass to real_did_it. + */ + void + did_it_interact(dbref player, dbref thing, const char *what, const char *def, + const char *owhat, const char *odef, const char *awhat, + dbref loc, int flags) + { + /* Bunch o' nulls */ + static char *myenv[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + real_did_it(player, thing, what, def, owhat, odef, awhat, loc, myenv, flags); + } + /** Take an action on an object and trigger attributes. * \verbatim * executes the @attr, @oattr, @aattr for a command - gives a message *************** *** 201,211 **** * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param myenv copy of the environment. */ void real_did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, ! char *myenv[10]) { ATTR *d; --- 223,234 ---- * \param awhat action attribute to trigger. * \param loc location in which action is taking place. * \param myenv copy of the environment. + * \param flags flags controlling type of interaction involved. */ void real_did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc, ! char *myenv[10], int flags) { ATTR *d; *************** *** 231,237 **** save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); ! asave = safe_uncompress(d->value); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, --- 254,260 ---- save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); ! asave = safe_atr_value(d); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, *************** *** 253,259 **** save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); ! asave = safe_uncompress(d->value); ap = asave; bp = buff; safe_str(Name(player), buff, &bp); --- 276,282 ---- save_global_env("did_it_save", preserves); } restore_global_env("did_it", myenv); ! asave = safe_atr_value(d); ap = asave; bp = buff; safe_str(Name(player), buff, &bp); *************** *** 263,274 **** PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; if (bp != sp) ! notify_except2(Contents(loc), player, thing, buff); free((Malloc_t) asave); } else { if (odef && *odef) { notify_except2(Contents(loc), player, thing, ! tprintf("%s %s", Name(player), odef)); } } } --- 286,297 ---- PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; if (bp != sp) ! notify_except2(Contents(loc), player, thing, buff, flags); free((Malloc_t) asave); } else { if (odef && *odef) { notify_except2(Contents(loc), player, thing, ! tprintf("%s %s", Name(player), odef), flags); } } } *************** *** 519,525 **** /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) ! return (atoi(uncompress(a->value))); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. --- 542,548 ---- /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) ! return (atoi(atr_value(a))); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. *************** *** 750,756 **** return 1; } ! /* The switch command. * \verbatim * For lack of better place the @switch code is here. * @switch expression=args --- 773,779 ---- return 1; } ! /** The switch command. * \verbatim * For lack of better place the @switch code is here. * @switch expression=args *************** *** 872,878 **** if (message && *message) { d = atr_get(target, message); if (d) { ! asave = safe_uncompress(d->value); ap = asave; bp = buff; process_expression(buff, &bp, &ap, target, player, player, --- 895,901 ---- if (message && *message) { d = atr_get(target, message); if (d) { ! asave = safe_atr_value(d); ap = asave; bp = buff; process_expression(buff, &bp, &ap, target, player, player, *************** *** 893,899 **** } } ! /* Returns the apparent location of object. * This is the location for players and things, source for exits, and * NOTHING for rooms. * \param thing object to get location of. --- 916,922 ---- } } ! /** Returns the apparent location of object. * This is the location for players and things, source for exits, and * NOTHING for rooms. * \param thing object to get location of. *************** *** 914,920 **** } } ! /* Are two objects near each other? * Returns 1 if obj1 is "nearby" object2. "Nearby" is a commutative * relation defined as: * obj1 is in the same room as obj2, obj1 is being carried by --- 937,943 ---- } } ! /** Are two objects near each other? * Returns 1 if obj1 is "nearby" object2. "Nearby" is a commutative * relation defined as: * obj1 is in the same room as obj2, obj1 is being carried by *************** *** 941,947 **** return 0; } ! /* User-defined verbs. * \verbatim * This implements the @verb command. * \endverbatim --- 964,970 ---- return 0; } ! /** User-defined verbs. * \verbatim * This implements the @verb command. * \endverbatim *************** *** 1006,1012 **** real_did_it(actor, victim, upcasestr(argv[2]), argv[3], upcasestr(argv[4]), argv[5], ! NULL, Location(actor), wenv); for (i = 0; i < 10; i++) wenv[i] = wsave[i]; --- 1029,1035 ---- real_did_it(actor, victim, upcasestr(argv[2]), argv[3], upcasestr(argv[4]), argv[5], ! NULL, Location(actor), wenv, NA_INTER_HEAR); for (i = 0; i < 10; i++) wenv[i] = wsave[i]; *************** *** 1039,1045 **** int found; char *s; ! s = (char *) uncompress(AL_STR(atr)); /* warning: static */ found = 0; while (*s && !found) { if ((!guh->insensitive && !strncmp(guh->lookfor, s, guh->len)) || --- 1062,1068 ---- int found; char *s; ! s = (char *) atr_value(atr); /* warning: static */ found = 0; while (*s && !found) { if ((!guh->insensitive && !strncmp(guh->lookfor, s, guh->len)) || *************** *** 1105,1111 **** found = 0; tbp = tbuf1; ! s = (char *) uncompress(AL_STR(atr)); /* warning: static */ while (s && *s) { if ((gh->insensitive && !strncasecmp(gh->lookfor, s, gh->len)) || (!gh->insensitive && !strncmp(gh->lookfor, s, gh->len))) { --- 1128,1134 ---- found = 0; tbp = tbuf1; ! s = (char *) atr_value(atr); /* warning: static */ while (s && *s) { if ((gh->insensitive && !strncasecmp(gh->lookfor, s, gh->len)) || (!gh->insensitive && !strncmp(gh->lookfor, s, gh->len))) { *** 1_7_7.366/src/plyrlist.c Thu, 27 Mar 2003 14:38:12 -0600 dunemush (pennmush/b/46_plyrlist.c 1.5.1.1.1.3.1.1 660) --- 1_7_7.446(w)/src/plyrlist.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/b/46_plyrlist.c 1.5.1.1.1.3.1.2 660) *************** *** 23,28 **** --- 23,29 ---- #include "confmagic.h" + /** Hash table of player names */ HASHTAB htab_player_list; static int hft_initialized = 0; *** 1_7_7.366/src/player.c Thu, 27 Mar 2003 11:13:51 -0600 dunemush (pennmush/b/47_player.c 1.15.1.1.1.1.1.4.1.6.1.10 660) --- 1_7_7.446(w)/src/player.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/b/47_player.c 1.15.1.1.1.1.1.4.1.6.1.14 660) *************** *** 74,80 **** if (!(a = atr_get_noparent(player, pword_attr))) return 1; /* No password attribute */ ! saved = strdup(uncompress(a->value)); if (!saved) return 0; --- 74,80 ---- if (!(a = atr_get_noparent(player, pword_attr))) return 1; /* No password attribute */ ! saved = strdup(atr_value(a)); if (!saved) return 0; *************** *** 193,199 **** return player; } ! /* Attempt to create a new player object. * \param name name of player to create. * \param password initial password of created player. * \param host host from which creation is attempted. --- 193,199 ---- return player; } ! /** Attempt to create a new player object. * \param name name of player to create. * \param password initial password of created player. * \param host host from which creation is attempted. *************** *** 231,236 **** --- 231,240 ---- #endif #ifdef HAS_SENDMAIL + + /** Size of the elems array */ + #define NELEMS (sizeof(elems)-1) + /** Attempt to register a new player at the connect screen. * If registration is allowed, a new player object is created with * a random password which is emailed to the registering player. *************** *** 248,254 **** char passwd[BUFFER_LEN]; static char elems[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - #define NELEMS (sizeof(elems)-1) int i, len; dbref player; FILE *fp; --- 252,257 ---- *************** *** 481,500 **** s[8] = '0'; /* compare to last connect see if player gets salary */ a = atr_get_noparent(player, "LAST"); ! if (a && (strncmp(uncompress(a->value), s, 10) != 0)) giveto(player, Paycheck(player)); /* tell the player where he last connected from */ h = atr_get_noparent(player, "LASTSITE"); if (h && a) { ! strcpy(last_place, uncompress(h->value)); ! strcpy(last_time, uncompress(a->value)); notify_format(player, T("Last connect was from %s on %s."), last_place, last_time); } /* How about last failed connection */ h = atr_get_noparent(player, "LASTFAILED"); if (h && a) { ! strcpy(last_place, uncompress(h->value)); if (strlen(last_place) > 2) notify_format(player, T("Last FAILED connect was from %s."), last_place); } --- 484,503 ---- s[8] = '0'; /* compare to last connect see if player gets salary */ a = atr_get_noparent(player, "LAST"); ! if (a && (strncmp(atr_value(a), s, 10) != 0)) giveto(player, Paycheck(player)); /* tell the player where he last connected from */ h = atr_get_noparent(player, "LASTSITE"); if (h && a) { ! strcpy(last_place, atr_value(h)); ! strcpy(last_time, atr_value(a)); notify_format(player, T("Last connect was from %s on %s."), last_place, last_time); } /* How about last failed connection */ h = atr_get_noparent(player, "LASTFAILED"); if (h && a) { ! strcpy(last_place, atr_value(h)); if (strlen(last_place) > 2) notify_format(player, T("Last FAILED connect was from %s."), last_place); } *** 1_7_7.366/src/parse.c Sun, 30 Mar 2003 21:53:14 -0600 dunemush (pennmush/b/48_parse.c 1.23.1.10.1.2.1.1.1.1.1.2.1.2.1.14 660) --- 1_7_7.446(w)/src/parse.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/b/48_parse.c 1.23.1.10.1.2.1.1.1.1.1.2.1.2.1.17 660) *************** *** 56,61 **** --- 56,63 ---- FUNCTION_PROTO(fun_gfun); + #ifndef DOXYGEN_SHOULD_SKIP_THIS + /* Common error messages */ char e_int[] = "#-1 ARGUMENT MUST BE INTEGER"; char e_ints[] = "#-1 ARGUMENTS MUST BE INTEGERS"; *************** *** 72,77 **** --- 74,81 ---- char e_disabled[] = "#-1 FUNCTION DISABLED"; char e_range[] = "#-1 OUT OF RANGE"; + #endif + #if 0 static void dummy_errors(void); *************** *** 753,759 **** temp[2] = '\0'; attrib = atr_get(executor, temp); if (attrib) ! safe_str(uncompress(attrib->value), buff, bp); break; default: /* just copy */ safe_chr(savec, buff, bp); --- 757,763 ---- temp[2] = '\0'; attrib = atr_get(executor, temp); if (attrib) ! safe_str(atr_value(attrib), buff, bp); break; default: /* just copy */ safe_chr(savec, buff, bp); *** 1_7_7.366/src/move.c Sun, 30 Mar 2003 21:53:14 -0600 dunemush (pennmush/b/51_move.c 1.1.1.18.1.5.1.13.1.3.1.9.1.1.1.1.1.2.1.1.1.1.1.16 660) --- 1_7_7.446(w)/src/move.c Thu, 29 May 2003 10:01:32 -0500 dunemush (pennmush/b/51_move.c 1.1.1.18.1.5.1.13.1.3.1.9.1.1.1.1.1.2.1.1.1.1.1.21 660) *************** *** 97,122 **** if ((where != NOTHING) && (old != where)) { did_it(what, what, NULL, NULL, "OXMOVE", NULL, NULL, old); if (Hearer(what)) { ! did_it(what, old, "LEAVE", NULL, "OLEAVE", T("has left."), "ALEAVE", ! old); /* If the player is leaving a zone, do zone messages */ /* The tricky bit here is that we only care about the zone of * the outermost contents */ if (GoodObject(absold) && GoodObject(Zone(absold)) && (Zone(absloc) != Zone(absold))) ! did_it(what, Zone(absold), "ZLEAVE", NULL, "OZLEAVE", NULL, "AZLEAVE", ! old); if (GoodObject(old) && !IsRoom(old)) ! did_it(what, old, NULL, NULL, "OXLEAVE", NULL, NULL, where); if (!IsRoom(where)) ! did_it(what, where, NULL, NULL, "OXENTER", NULL, NULL, old); /* If the player is entering a new zone, do zone messages */ if (!GoodObject(absold) || (GoodObject(Zone(absloc)) && (Zone(absloc) != Zone(absold)))) ! did_it(what, Zone(absloc), "ZENTER", NULL, "OZENTER", NULL, "AZENTER", ! where); ! did_it(what, where, "ENTER", NULL, "OENTER", T("has arrived."), ! "AENTER", where); } else { /* non-listeners only trigger the actions not the messages */ did_it(what, old, NULL, NULL, NULL, NULL, "ALEAVE", old); --- 97,124 ---- if ((where != NOTHING) && (old != where)) { did_it(what, what, NULL, NULL, "OXMOVE", NULL, NULL, old); if (Hearer(what)) { ! did_it_interact(what, old, "LEAVE", NULL, "OLEAVE", T("has left."), ! "ALEAVE", old, NA_INTER_PRESENCE); /* If the player is leaving a zone, do zone messages */ /* The tricky bit here is that we only care about the zone of * the outermost contents */ if (GoodObject(absold) && GoodObject(Zone(absold)) && (Zone(absloc) != Zone(absold))) ! did_it_interact(what, Zone(absold), "ZLEAVE", NULL, "OZLEAVE", NULL, ! "AZLEAVE", old, NA_INTER_SEE); if (GoodObject(old) && !IsRoom(old)) ! did_it_interact(what, old, NULL, NULL, "OXLEAVE", NULL, NULL, where, ! NA_INTER_SEE); if (!IsRoom(where)) ! did_it_interact(what, where, NULL, NULL, "OXENTER", NULL, NULL, old, ! NA_INTER_SEE); /* If the player is entering a new zone, do zone messages */ if (!GoodObject(absold) || (GoodObject(Zone(absloc)) && (Zone(absloc) != Zone(absold)))) ! did_it_interact(what, Zone(absloc), "ZENTER", NULL, "OZENTER", NULL, ! "AZENTER", where, NA_INTER_SEE); ! did_it_interact(what, where, "ENTER", NULL, "OENTER", T("has arrived."), ! "AENTER", where, NA_INTER_PRESENCE); } else { /* non-listeners only trigger the actions not the messages */ did_it(what, old, NULL, NULL, NULL, NULL, "ALEAVE", old); *************** *** 130,138 **** } } if (!nomovemsgs) ! did_it(what, what, "MOVE", NULL, "OMOVE", NULL, "AMOVE", where); } #define Dropper(thing) (Hearer(thing) && Connected(Owner(thing))) static void --- 132,142 ---- } } if (!nomovemsgs) ! did_it_interact(what, what, "MOVE", NULL, "OMOVE", NULL, "AMOVE", where, ! NA_INTER_SEE); } + /** A dropper is an object that can hear and has a connected owner */ #define Dropper(thing) (Hearer(thing) && Connected(Owner(thing))) static void *************** *** 326,332 **** a = atr_get(exit_obj, "EXITTO"); if (!a) return NOTHING; ! abuf = safe_uncompress(a->value); if (!abuf) return NOTHING; if (!*abuf) { --- 330,336 ---- a = atr_get(exit_obj, "EXITTO"); if (!a) return NOTHING; ! abuf = safe_atr_value(a); if (!abuf) return NOTHING; if (!*abuf) { *************** *** 365,371 **** if ((loc = Location(player)) != NOTHING && !Dark(player) && !Dark(loc)) { /* tell everybody else */ notify_except(Contents(loc), player, ! tprintf(T("%s goes home."), Name(player)), NA_INTERACTION); } /* give the player the messages */ notify(player, T("There's no place like home...")); --- 369,375 ---- if ((loc = Location(player)) != NOTHING && !Dark(player) && !Dark(loc)) { /* tell everybody else */ notify_except(Contents(loc), player, ! tprintf(T("%s goes home."), Name(player)), NA_INTER_SEE); } /* give the player the messages */ notify(player, T("There's no place like home...")); *************** *** 555,561 **** for (i = 1; i < 10; i++) myenv[i] = NULL; real_did_it(player, player, "RECEIVE", NULL, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv); mush_free(myenv[0], "dbref"); } else did_it(player, thing, "FAILURE", T("You can't take that from there."), --- 559,565 ---- for (i = 1; i < 10; i++) myenv[i] = NULL; real_did_it(player, player, "RECEIVE", NULL, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv, NA_INTER_SEE); mush_free(myenv[0], "dbref"); } else did_it(player, thing, "FAILURE", T("You can't take that from there."), *************** *** 602,608 **** for (i = 1; i < 10; i++) myenv[i] = NULL; real_did_it(player, player, "RECEIVE", NULL, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv); mush_free(myenv[0], "dbref"); } else did_it(player, thing, "FAILURE", T("You can't pick that up."), --- 606,612 ---- for (i = 1; i < 10; i++) myenv[i] = NULL; real_did_it(player, player, "RECEIVE", NULL, "ORECEIVE", NULL, ! "ARECEIVE", NOTHING, myenv, NA_INTER_SEE); mush_free(myenv[0], "dbref"); } else did_it(player, thing, "FAILURE", T("You can't pick that up."), *************** *** 804,811 **** --- 808,817 ---- /* Routines for dealing with the follow commands */ /** The follow command. + * \verbatim * follow tries to start following * follow alone lists who you're following + * \endverbatim * \param player the enactor. * \param arg name of object to follow. */ *************** *** 857,866 **** } /** The unfollow command. * unfollow removes someone from your following list * unfollow alone removes everyone from your following list. * \param player the enactor. ! * \parma arg object to stop following. */ void do_unfollow(dbref player, const char *arg) --- 863,874 ---- } /** The unfollow command. + * \verbatim * unfollow removes someone from your following list * unfollow alone removes everyone from your following list. + * \endverbatim * \param player the enactor. ! * \param arg object to stop following. */ void do_unfollow(dbref player, const char *arg) *************** *** 893,900 **** --- 901,910 ---- /** The dismiss command. + * \verbatim * dismiss removes someone from your followers list * dismiss alone removes everyone from your followers list. + * \endverbatim * \param player the enactor. * \param arg name of object to dismiss. */ *************** *** 924,931 **** --- 934,943 ---- } /** The desert command. + * \verbatim * desert removes someone from your followers and following list * desert alone removes everyone from both lists + * \endverbatim * \param player the enactor. * \param arg name of object to desert. */ *************** *** 970,976 **** (void) atr_add(leader, "FOLLOWERS", unparse_dbref(follower), GOD, NOTHING); } else { bp = tbuf1; ! safe_str(uncompress(AL_STR(a)), tbuf1, &bp); safe_chr(' ', tbuf1, &bp); safe_dbref(follower, tbuf1, &bp); *bp = '\0'; --- 982,988 ---- (void) atr_add(leader, "FOLLOWERS", unparse_dbref(follower), GOD, NOTHING); } else { bp = tbuf1; ! safe_str(atr_value(a), tbuf1, &bp); safe_chr(' ', tbuf1, &bp); safe_dbref(follower, tbuf1, &bp); *bp = '\0'; *************** *** 990,996 **** (void) atr_add(follower, "FOLLOWING", unparse_dbref(leader), GOD, NOTHING); } else { bp = tbuf1; ! safe_str(uncompress(AL_STR(a)), tbuf1, &bp); safe_chr(' ', tbuf1, &bp); safe_dbref(leader, tbuf1, &bp); *bp = '\0'; --- 1002,1008 ---- (void) atr_add(follower, "FOLLOWING", unparse_dbref(leader), GOD, NOTHING); } else { bp = tbuf1; ! safe_str(atr_value(a), tbuf1, &bp); safe_chr(' ', tbuf1, &bp); safe_dbref(leader, tbuf1, &bp); *bp = '\0'; *************** *** 1025,1031 **** return; /* No followers, so no deletion */ /* Let's take it apart and put it back together w/o follower */ strcpy(flwr, unparse_dbref(follower)); ! strcpy(tbuf1, uncompress(AL_STR(a))); (void) atr_add(leader, "FOLLOWERS", remove_word(tbuf1, flwr, ' '), GOD, NOTHING); } --- 1037,1043 ---- return; /* No followers, so no deletion */ /* Let's take it apart and put it back together w/o follower */ strcpy(flwr, unparse_dbref(follower)); ! strcpy(tbuf1, atr_value(a)); (void) atr_add(leader, "FOLLOWERS", remove_word(tbuf1, flwr, ' '), GOD, NOTHING); } *************** *** 1042,1048 **** return; /* Not following, so no deletion */ /* Let's take it apart and put it back together w/o leader */ strcpy(ldr, unparse_dbref(leader)); ! strcpy(tbuf1, uncompress(AL_STR(a))); (void) atr_add(follower, "FOLLOWING", remove_word(tbuf1, ldr, ' '), GOD, NOTHING); } --- 1054,1060 ---- return; /* Not following, so no deletion */ /* Let's take it apart and put it back together w/o leader */ strcpy(ldr, unparse_dbref(leader)); ! strcpy(tbuf1, atr_value(a)); (void) atr_add(follower, "FOLLOWING", remove_word(tbuf1, ldr, ' '), GOD, NOTHING); } *************** *** 1075,1081 **** a = atr_get_noparent(player, "FOLLOWERS"); if (!a) return (char *) ""; ! strcpy(tbuf1, uncompress(AL_STR(a))); bp = buff; s = trim_space_sep(tbuf1, ' '); while (s) { --- 1087,1093 ---- a = atr_get_noparent(player, "FOLLOWERS"); if (!a) return (char *) ""; ! strcpy(tbuf1, atr_value(a)); bp = buff; s = trim_space_sep(tbuf1, ' '); while (s) { *************** *** 1106,1112 **** a = atr_get_noparent(player, "FOLLOWING"); if (!a) return (char *) ""; ! strcpy(tbuf1, uncompress(AL_STR(a))); bp = buff; s = trim_space_sep(tbuf1, ' '); while (s) { --- 1118,1124 ---- a = atr_get_noparent(player, "FOLLOWING"); if (!a) return (char *) ""; ! strcpy(tbuf1, atr_value(a)); bp = buff; s = trim_space_sep(tbuf1, ' '); while (s) { *************** *** 1136,1142 **** a = atr_get_noparent(follower, "FOLLOWING"); if (!a) return 0; /* Following no one */ ! strcpy(tbuf1, uncompress(AL_STR(a))); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); --- 1148,1154 ---- a = atr_get_noparent(follower, "FOLLOWING"); if (!a) return 0; /* Following no one */ ! strcpy(tbuf1, atr_value(a)); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); *************** *** 1160,1166 **** a = atr_get_noparent(leader, "FOLLOWERS"); if (!a) return; /* No one's following me */ ! strcpy(tbuf1, uncompress(AL_STR(a))); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); --- 1172,1178 ---- a = atr_get_noparent(leader, "FOLLOWERS"); if (!a) return; /* No one's following me */ ! strcpy(tbuf1, atr_value(a)); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); *************** *** 1188,1194 **** a = atr_get_noparent(follower, "FOLLOWING"); if (!a) return; /* I'm not following anyone */ ! strcpy(tbuf1, uncompress(AL_STR(a))); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); --- 1200,1206 ---- a = atr_get_noparent(follower, "FOLLOWING"); if (!a) return; /* I'm not following anyone */ ! strcpy(tbuf1, atr_value(a)); s = trim_space_sep(tbuf1, ' '); while (s) { sp = split_token(&s, ' '); *************** *** 1219,1225 **** a = atr_get_noparent(leader, "FOLLOWERS"); if (!a) return; /* No followers */ ! strcpy(tbuf1, uncompress(AL_STR(a))); s = tbuf1; while (s) { sp = split_token(&s, ' '); --- 1231,1237 ---- a = atr_get_noparent(leader, "FOLLOWERS"); if (!a) return; /* No followers */ ! strcpy(tbuf1, atr_value(a)); s = tbuf1; while (s) { sp = split_token(&s, ' '); *** 1_7_7.366/src/memcheck.c Tue, 11 Mar 2003 18:16:12 -0600 dunemush (pennmush/c/1_memcheck.c 1.8.1.5 660) --- 1_7_7.446(w)/src/memcheck.c Thu, 29 May 2003 10:01:32 -0500 dunemush (pennmush/c/1_memcheck.c 1.8.1.6 660) *************** *** 120,128 **** --- 120,130 ---- { MEM *loop; + do_rawlog(LT_CHECK, "MEMCHECK dump starts"); for (loop = my_check; loop; loop = loop->next) { do_rawlog(LT_CHECK, "%s : %d", loop->ref_name, loop->ref_count); } + do_rawlog(LT_CHECK, "MEMCHECK dump ends"); } #endif /* MEM_CHECK */ *** 1_7_7.366/src/match.c Sat, 29 Mar 2003 09:46:22 -0600 dunemush (pennmush/c/2_match.c 1.24.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.20 660) --- 1_7_7.446(w)/src/match.c Thu, 29 May 2003 10:01:32 -0500 dunemush (pennmush/c/2_match.c 1.24.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.21 660) *************** *** 23,29 **** * MAT_CONTROL - do a control check after matching * MAT_ME - match "me" * MAT_HERE - match "here" ! * MAT_ABSOLUTE - match "#" * MAT_PLAYER - match a player's name * MAT_NEIGHBOR - match something in the same room * MAT_POSSESSION - match something I'm carrying --- 23,29 ---- * MAT_CONTROL - do a control check after matching * MAT_ME - match "me" * MAT_HERE - match "here" ! * MAT_ABSOLUTE - match "#dbref" * MAT_PLAYER - match a player's name * MAT_NEIGHBOR - match something in the same room * MAT_POSSESSION - match something I'm carrying *************** *** 61,71 **** static int parse_english(const char **name, long *flags); static dbref match_me(const dbref who, const char *name); static dbref match_here(const dbref who, const char *name); #define match_absolute(name) parse_dbref(name) static dbref match_player(const dbref matcher, const char *match_name); static dbref choose_thing(const dbref match_who, const int preferred_type, long int flags, dbref thing1, dbref thing2); ! extern int check_alias(const char *command, const char *list); /* game.c */ /** A wrapper for returning a match, AMBIGUOUS, or NOTHING. --- 61,72 ---- static int parse_english(const char **name, long *flags); static dbref match_me(const dbref who, const char *name); static dbref match_here(const dbref who, const char *name); + /** Macro for an 'absolute dbref match' function */ #define match_absolute(name) parse_dbref(name) static dbref match_player(const dbref matcher, const char *match_name); static dbref choose_thing(const dbref match_who, const int preferred_type, long int flags, dbref thing1, dbref thing2); ! int check_alias(const char *command, const char *list); /* game.c */ /** A wrapper for returning a match, AMBIGUOUS, or NOTHING. *************** *** 183,200 **** * d. If we got multiple partial matches, complain * e. If we got no matches, complain */ ! #define MATCH_NONE 0x0 ! #define MATCH_EXACT 0x1 ! #define MATCH_PARTIAL 0x2 #define MATCH_FUNC_PROTO(fun_name) \ /* ARGSUSED */ /* try to keep lint happy */ \ static int fun_name(const dbref who, const char *name, const int type, \ const long flags, dbref first, \ dbref *match, int *exact_matches_to_go, int *matches_to_go) #define MATCH_FUNC(fun_name) \ static int fun_name(const dbref who, const char *name, const int type, \ const long flags, dbref first __attribute__ ((__unused__)), \ dbref *match, int *exact_matches_to_go, int *matches_to_go) #define RUN_MATCH_FUNC(fun,first) \ { \ result = fun(who, name, type, flags, first, &match, \ --- 184,205 ---- * d. If we got multiple partial matches, complain * e. If we got no matches, complain */ ! ! #define MATCH_NONE 0x0 /**< No matches were found */ ! #define MATCH_EXACT 0x1 /**< At least one exact match found */ ! #define MATCH_PARTIAL 0x2 /**< At least one partial match found, no exact */ ! /** Prototype for matching functions */ #define MATCH_FUNC_PROTO(fun_name) \ /* ARGSUSED */ /* try to keep lint happy */ \ static int fun_name(const dbref who, const char *name, const int type, \ const long flags, dbref first, \ dbref *match, int *exact_matches_to_go, int *matches_to_go) + /** Common declaration for matching functions */ #define MATCH_FUNC(fun_name) \ static int fun_name(const dbref who, const char *name, const int type, \ const long flags, dbref first __attribute__ ((__unused__)), \ dbref *match, int *exact_matches_to_go, int *matches_to_go) + /** Macro to execute matching and store some results */ #define RUN_MATCH_FUNC(fun,first) \ { \ result = fun(who, name, type, flags, first, &match, \ *** 1_7_7.366/src/malias.c Tue, 11 Mar 2003 17:43:44 -0600 dunemush (pennmush/c/3_malias.c 1.36.1.5 660) --- 1_7_7.446(w)/src/malias.c Thu, 29 May 2003 10:01:32 -0500 dunemush (pennmush/c/3_malias.c 1.36.1.6 660) *************** *** 33,39 **** * \endverbatim */ ! #define MA_INC 3 #include "config.h" #include "copyrite.h" --- 33,39 ---- * \endverbatim */ ! #define MA_INC 3 /**< How many maliases we malloc at a time */ #include "config.h" #include "copyrite.h" *** 1_7_7.366/src/look.c Wed, 26 Mar 2003 14:31:41 -0600 dunemush (pennmush/c/4_look.c 1.21.1.2.1.9.1.1.1.1.1.5 660) --- 1_7_7.446(w)/src/look.c Thu, 29 May 2003 10:01:32 -0500 dunemush (pennmush/c/4_look.c 1.21.1.2.1.9.1.1.1.1.1.7 660) *************** *** 107,113 **** } *bp = '\0'; wenv[0] = arg; ! sp = save = safe_uncompress(a->value); bp = buff; process_expression(buff, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); --- 107,113 ---- } *bp = '\0'; wenv[0] = arg; ! sp = save = safe_atr_value(a); bp = buff; process_expression(buff, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *************** *** 257,263 **** } *bp = '\0'; wenv[0] = arg; ! sp = save = safe_uncompress(a->value); bp = buff; process_expression(buff, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); --- 257,263 ---- } *bp = '\0'; wenv[0] = arg; ! sp = save = safe_atr_value(a); bp = buff; process_expression(buff, &bp, &sp, loc, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *************** *** 322,328 **** "%s [#%d%s] is veiled", AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf); } else { ! r = safe_uncompress(AL_STR(atr)); if (ShowAnsi(player)) notify_format(player, "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr), --- 322,328 ---- "%s [#%d%s] is veiled", AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf); } else { ! r = safe_atr_value(atr); if (ShowAnsi(player)) notify_format(player, "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr), *************** *** 347,353 **** !strcmp(AL_NAME(atr), "DESCRIBE") && !strcmp(pattern, "*")) return 0; strcpy(fbuf, privs_to_letters(attr_privs, AL_FLAGS(atr))); ! r = safe_uncompress(AL_STR(atr)); if (ShowAnsi(player)) notify_format(player, "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr), --- 347,353 ---- !strcmp(AL_NAME(atr), "DESCRIBE") && !strcmp(pattern, "*")) return 0; strcpy(fbuf, privs_to_letters(attr_privs, AL_FLAGS(atr))); ! r = safe_atr_value(atr); if (ShowAnsi(player)) notify_format(player, "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr), *************** *** 426,432 **** if (SUPPORT_PUEBLO && style == LOOK_AUTO) { a = atr_get(loc, "VRML_URL"); if (a) { ! tag(tprintf("IMG XCH_GRAPH=LOAD HREF=\"%s\"", uncompress(a->value))); } else { tag("IMG XCH_GRAPH=HIDE"); } --- 426,432 ---- if (SUPPORT_PUEBLO && style == LOOK_AUTO) { a = atr_get(loc, "VRML_URL"); if (a) { ! tag(tprintf("IMG XCH_GRAPH=LOAD HREF=\"%s\"", atr_value(a))); } else { tag("IMG XCH_GRAPH=HIDE"); } *************** *** 500,506 **** a = atr_get(thing, descname); if (a) { /* We have a DESCRIBE, evaluate it into buff */ ! asave = safe_uncompress(a->value); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, --- 500,506 ---- a = atr_get(thing, descname); if (a) { /* We have a DESCRIBE, evaluate it into buff */ ! asave = safe_atr_value(a); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, *************** *** 513,519 **** /* We have a DESCFORMAT, evaluate it into fbuff and use it */ /* If we have a DESCRIBE, pass the evaluated version as %0 */ wenv[0] = a ? buff : NULL; ! asave = safe_uncompress(f->value); ap = asave; fbp = fbuff; process_expression(fbuff, &fbp, &ap, thing, player, player, --- 513,519 ---- /* We have a DESCFORMAT, evaluate it into fbuff and use it */ /* If we have a DESCRIBE, pass the evaluated version as %0 */ wenv[0] = a ? buff : NULL; ! asave = safe_atr_value(f); ap = asave; fbp = fbuff; process_expression(fbuff, &fbp, &ap, thing, player, player, *************** *** 716,722 **** if (EX_PUBLIC_ATTRIBS && (brief != 1)) { a = atr_get_noparent(thing, "DESCRIBE"); if (a) { ! r = safe_uncompress(a->value); notify(player, r); free((Malloc_t) r); } --- 716,722 ---- if (EX_PUBLIC_ATTRIBS && (brief != 1)) { a = atr_get_noparent(thing, "DESCRIBE"); if (a) { ! r = safe_atr_value(a); notify(player, r); free((Malloc_t) r); } *************** *** 1253,1259 **** safe_chr(' ', msg, &bp); safe_str(dh->name, msg, &bp); safe_chr('=', msg, &bp); ! safe_str(uncompress(AL_STR(atr)), msg, &bp); *bp = '\0'; notify(player, msg); /* Now deal with attribute flags, if not FugueEditing */ --- 1253,1259 ---- safe_chr(' ', msg, &bp); safe_str(dh->name, msg, &bp); safe_chr('=', msg, &bp); ! safe_str(atr_value(atr), msg, &bp); *bp = '\0'; notify(player, msg); /* Now deal with attribute flags, if not FugueEditing */ *************** *** 1378,1385 **** case DEC_TF: if (((a = atr_get_noparent(player, "TFPREFIX")) != NULL) && AL_STR(a) && *AL_STR(a)) { ! decompile_atrs(player, thing, dbnum, attrib, uncompress(AL_STR(a)), ! skipdef); } else decompile_atrs(player, thing, dbnum, attrib, "FugueEdit > ", skipdef); break; --- 1378,1384 ---- case DEC_TF: if (((a = atr_get_noparent(player, "TFPREFIX")) != NULL) && AL_STR(a) && *AL_STR(a)) { ! decompile_atrs(player, thing, dbnum, attrib, atr_value(a), skipdef); } else decompile_atrs(player, thing, dbnum, attrib, "FugueEdit > ", skipdef); break; *** 1_7_7.366/src/log.c Mon, 03 Mar 2003 23:05:05 -0600 dunemush (pennmush/c/5_log.c 1.10.1.2.2.4.1.1.2.2.1.3 660) --- 1_7_7.446(w)/src/log.c Thu, 29 May 2003 10:01:31 -0500 dunemush (pennmush/c/5_log.c 1.10.1.2.2.4.1.1.2.2.1.4 660) *************** *** 39,49 **** #endif /* log file pointers */ ! FILE *connlog_fp; ! FILE *checklog_fp; ! FILE *wizlog_fp; ! FILE *tracelog_fp; ! FILE *cmdlog_fp; static char * --- 39,49 ---- #endif /* log file pointers */ ! FILE *connlog_fp; /**< Connect log */ ! FILE *checklog_fp; /**< Checkpoint log */ ! FILE *wizlog_fp; /**< Wizard log */ ! FILE *tracelog_fp; /**< Trace log */ ! FILE *cmdlog_fp; /**< Command log */ static char * *** 1_7_7.366/src/lock.c Mon, 03 Mar 2003 23:07:09 -0600 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.8 660) --- 1_7_7.446(w)/src/lock.c Thu, 29 May 2003 10:01:31 -0500 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.13 660) *************** *** 57,86 **** /* If any lock_type ever contains the character '|', reading in locks * from the db will break. */ ! const lock_type Basic_Lock = "Basic"; ! const lock_type Enter_Lock = "Enter"; ! const lock_type Use_Lock = "Use"; ! const lock_type Zone_Lock = "Zone"; ! const lock_type Page_Lock = "Page"; ! const lock_type Tport_Lock = "Teleport"; ! const lock_type Speech_Lock = "Speech"; ! const lock_type Listen_Lock = "Listen"; ! const lock_type Command_Lock = "Command"; ! const lock_type Parent_Lock = "Parent"; ! const lock_type Link_Lock = "Link"; ! const lock_type Leave_Lock = "Leave"; ! const lock_type Drop_Lock = "Drop"; ! const lock_type Give_Lock = "Give"; ! const lock_type Mail_Lock = "Mail"; ! const lock_type Follow_Lock = "Follow"; ! const lock_type Examine_Lock = "Examine"; ! const lock_type Chzone_Lock = "Chzone"; ! const lock_type Forward_Lock = "Forward"; ! const lock_type Control_Lock = "Control"; ! const lock_type Dropto_Lock = "Dropto"; ! const lock_type Destroy_Lock = "Destroy"; /* Define new lock types here. */ const lock_list lock_types[] = { {"Basic", NULL, GOD, LF_PRIVATE, NULL}, {"Enter", NULL, GOD, LF_PRIVATE, NULL}, --- 57,87 ---- /* If any lock_type ever contains the character '|', reading in locks * from the db will break. */ ! const lock_type Basic_Lock = "Basic"; /**< Name of basic lock */ ! const lock_type Enter_Lock = "Enter"; /**< Name of enter lock */ ! const lock_type Use_Lock = "Use"; /**< Name of use lock */ ! const lock_type Zone_Lock = "Zone"; /**< Name of zone lock */ ! const lock_type Page_Lock = "Page"; /**< Name of page lock */ ! const lock_type Tport_Lock = "Teleport"; /**< Name of teleport lock */ ! const lock_type Speech_Lock = "Speech"; /**< Name of speech lock */ ! const lock_type Listen_Lock = "Listen"; /**< Name of listen lock */ ! const lock_type Command_Lock = "Command"; /**< Name of command lock */ ! const lock_type Parent_Lock = "Parent"; /**< Name of parent lock */ ! const lock_type Link_Lock = "Link"; /**< Name of link lock */ ! const lock_type Leave_Lock = "Leave"; /**< Name of leave lock */ ! const lock_type Drop_Lock = "Drop"; /**< Name of drop lock */ ! const lock_type Give_Lock = "Give"; /**< Name of give lock */ ! const lock_type Mail_Lock = "Mail"; /**< Name of mail lock */ ! const lock_type Follow_Lock = "Follow"; /**< Name of follow lock */ ! const lock_type Examine_Lock = "Examine"; /**< Name of examine lock */ ! const lock_type Chzone_Lock = "Chzone"; /**< Name of chzone lock */ ! const lock_type Forward_Lock = "Forward"; /**< Name of forward lock */ ! const lock_type Control_Lock = "Control"; /**< Name of control lock */ ! const lock_type Dropto_Lock = "Dropto"; /**< Name of dropto lock */ ! const lock_type Destroy_Lock = "Destroy"; /**< Name of destroy lock */ /* Define new lock types here. */ + /** Table of lock names and permissions */ const lock_list lock_types[] = { {"Basic", NULL, GOD, LF_PRIVATE, NULL}, {"Enter", NULL, GOD, LF_PRIVATE, NULL}, *************** *** 108,113 **** --- 109,115 ---- {NULL, NULL, GOD, 0, NULL} }; + /** Table of lock permissions */ PRIV lock_privs[] = { {"visual", 'v', LF_VISUAL, LF_VISUAL}, {"no_inherit", 'i', LF_PRIVATE, LF_PRIVATE}, *************** *** 127,133 **** static lock_list *getlockstruct(dbref thing, lock_type type); static lock_list *getlockstruct_noparent(dbref thing, lock_type type); ! /* Assuming 4096 byte pages */ #define LOCKS_PER_PAGE 200 static lock_list *free_list = NULL; --- 129,135 ---- static lock_list *getlockstruct(dbref thing, lock_type type); static lock_list *getlockstruct_noparent(dbref thing, lock_type type); ! /** Number of locks to store in a page, assuming 4096 byte pages */ #define LOCKS_PER_PAGE 200 static lock_list *free_list = NULL; *************** *** 608,619 **** } if ((thing = match_controlled(player, name)) != NOTHING) { if ((real_type = check_lock_type(player, thing, type)) != NULL) { ! if (getlock(thing, real_type) == TRUE_BOOLEXP) ! notify_format(player, T("%s(%s) - %s (already) unlocked."), Name(thing), ! unparse_dbref(thing), real_type); ! else if (delete_lock(player, thing, real_type)) { ! notify_format(player, T("%s(%s) - %s unlocked."), Name(thing), ! unparse_dbref(thing), real_type); ModTime(thing) = mudtime; } else notify(player, T("Permission denied.")); --- 610,623 ---- } if ((thing = match_controlled(player, name)) != NOTHING) { if ((real_type = check_lock_type(player, thing, type)) != NULL) { ! if (getlock(thing, real_type) == TRUE_BOOLEXP) { ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify_format(player, T("%s(%s) - %s (already) unlocked."), ! Name(thing), unparse_dbref(thing), real_type); ! } else if (delete_lock(player, thing, real_type)) { ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify_format(player, T("%s(%s) - %s unlocked."), Name(thing), ! unparse_dbref(thing), real_type); ModTime(thing) = mudtime; } else notify(player, T("Permission denied.")); *************** *** 676,683 **** if ((real_type = check_lock_type(player, thing, type)) != NULL) { /* everything ok, do it */ if (add_lock(player, thing, real_type, key, -1)) { ! notify_format(player, T("%s(%s) - %s locked."), Name(thing), ! unparse_dbref(thing), real_type); ModTime(thing) = mudtime; } else notify(player, T("Permission denied.")); --- 680,688 ---- if ((real_type = check_lock_type(player, thing, type)) != NULL) { /* everything ok, do it */ if (add_lock(player, thing, real_type, key, -1)) { ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify_format(player, T("%s(%s) - %s locked."), Name(thing), ! unparse_dbref(thing), real_type); ModTime(thing) = mudtime; } else notify(player, T("Permission denied.")); *************** *** 783,789 **** else L_FLAGS(l) |= flag; ! notify_format(player, "%s/%s - %s.", Name(thing), L_TYPE(l), ! unset ? T("lock flags unset") : T("lock flags set")); ModTime(thing) = mudtime; } --- 788,795 ---- else L_FLAGS(l) |= flag; ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify_format(player, "%s/%s - %s.", Name(thing), L_TYPE(l), ! unset ? T("lock flags unset") : T("lock flags set")); ModTime(thing) = mudtime; } *** 1_7_7.366/src/info_slave.c Sat, 01 Mar 2003 23:51:32 -0600 dunemush (pennmush/c/7_info_slave 1.9.1.4.1.4 660) --- 1_7_7.446(w)/src/info_slave.c Thu, 29 May 2003 10:01:31 -0500 dunemush (pennmush/c/7_info_slave 1.9.1.4.1.5 660) *************** *** 42,48 **** --- 42,50 ---- #include "confmagic.h" /* Duplicate these, rather than trying to include strutil.o... */ + /** Arguments for functions that call APPEND_TO_BUF */ #define APPEND_ARGS int len, blen, clen + /** Add string c to buffer buff of max length mlen */ #define APPEND_TO_BUF(mlen) \ /* Trivial cases */ \ if (c[0] == '\0') \ *** 1_7_7.366/src/game.c Wed, 26 Mar 2003 09:17:08 -0600 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.3 660) --- 1_7_7.446(w)/src/game.c Thu, 29 May 2003 10:01:30 -0500 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.1.1.2 660) *************** *** 81,86 **** --- 81,87 ---- #include "confmagic.h" #ifdef HAS_WAITPID + /** Return value of wait* functions */ #define WAIT_TYPE int #else #ifdef UNION_WAIT *************** *** 91,112 **** #endif /* declarations */ ! char dumpfile[200]; ! time_t start_time; /* MUSH start time (since process exec'd) */ ! time_t first_start_time = 0; /* MUSH start time (since last shutdown) */ ! time_t last_dump_time = 0; /* Time of last successful db save */ ! int reboot_count = 0; static int epoch = 0; ! int reserved; ! int depth = 0; /* excessive recursion prevention */ ! extern int invok_counter; /* function recursion prevention */ extern dbref cplr; extern char ccom[]; extern void initialize_mt(void); ! int paranoid_dump = 0; /* if paranoid, scan before dumping */ ! int paranoid_checkpt = 0; /* write out an okay message every x objs */ extern long indb_flags; extern void conf_default_set(void); static int dump_database_internal(void); --- 92,112 ---- #endif /* declarations */ ! char dumpfile[200]; /**< File name to dump database to */ ! time_t start_time; /**< MUSH start time (since process exec'd) */ ! time_t first_start_time = 0; /**< MUSH start time (since last shutdown) */ ! time_t last_dump_time = 0; /**< Time of last successful db save */ ! int reboot_count = 0; /**< Number of reboots so far */ static int epoch = 0; ! int reserved; /**< Reserved file descriptor */ ! int depth = 0; /**< excessive recursion prevention */ extern dbref cplr; extern char ccom[]; extern void initialize_mt(void); ! int paranoid_dump = 0; /**< if paranoid, scan before dumping */ ! int paranoid_checkpt = 0; /**< write out an okay message every x objs */ extern long indb_flags; extern void conf_default_set(void); static int dump_database_internal(void); *************** *** 114,121 **** static FILE *db_open_write(const char *filename); static void db_close(FILE * f); void do_readcache(dbref player); - char **argv_hack(dbref player, dbref cause, char const *arg, - char *fargs[], int eflags); int check_alias(const char *command, const char *list); int list_check(dbref thing, dbref player, char type, char end, char *str, int just_match); --- 114,119 ---- *************** *** 131,137 **** dbref cause, unsigned int flags); void do_uptime(dbref player, int mortal); static char *make_new_epoch_file(const char *basename, int the_epoch); - void dest_info(dbref thing, dbref tt); #ifdef HAS_GETRUSAGE void rusage_stats(void); #endif --- 129,134 ---- *************** *** 142,148 **** void do_timestring(char *buff, char **bp, const char *format, unsigned long secs); ! dbref orator = NOTHING; #ifdef COMP_STATS extern void compress_stats(long *entries, --- 139,145 ---- void do_timestring(char *buff, char **bp, const char *format, unsigned long secs); ! dbref orator = NOTHING; /**< Last dbref to issue a speech command */ #ifdef COMP_STATS extern void compress_stats(long *entries, *************** *** 508,513 **** --- 505,518 ---- nofork = 1; else nofork = !forking || (paranoid_dump == 2); /* Don't fork for dump/debug */ + if (!nofork && chunk_num_swapped()) { + /* Ack, can't fork, 'cause we have stuff on disk... */ + do_log(LT_ERR, 0, 0, + "fork_and_dump: Attributes are swapped to disk, so nonforking dumps will be used."); + flag_broadcast("WIZARD", 0, + "DUMP: Attributes are swapped to disk, so nonforking dumps will be used."); + nofork = 1; + } #ifdef WIN32 nofork = 1; #endif *************** *** 585,591 **** if (IsPlayer(thing)) { if ((s = atr_get_noparent(thing, "ALIAS")) != NULL) { bp = buf; ! safe_str(uncompress(s->value), buf, &bp); *bp = '\0'; add_player(thing, buf); } --- 590,596 ---- if (IsPlayer(thing)) { if ((s = atr_get_noparent(thing, "ALIAS")) != NULL) { bp = buf; ! safe_str(atr_value(s), buf, &bp); *bp = '\0'; add_player(thing, buf); } *************** *** 665,670 **** --- 670,678 ---- /* Load all the config file stuff except restrict_* */ conf_default_set(); config_file_startup(conf, 0); + + /* Initialize the attribute chunk storage */ + chunk_init(); } /** Post-db-load configuration. *************** *** 773,778 **** --- 781,791 ---- if (!GoodObject(MASTER_ROOM) || (!IsRoom(MASTER_ROOM))) do_rawlog(LT_ERR, T("WARNING: Master room (#%d) is NOT a room."), MASTER_ROOM); + if (!GoodObject(BASE_ROOM) || (!IsRoom(BASE_ROOM))) + do_rawlog(LT_ERR, T("WARNING: Base room (#%d) is NOT a room."), BASE_ROOM); + if (!GoodObject(DEFAULT_HOME) || (!IsRoom(DEFAULT_HOME))) + do_rawlog(LT_ERR, T("WARNING: Default home (#%d) is NOT a room."), + DEFAULT_HOME); if (!GoodObject(GOD) || (!IsPlayer(GOD))) do_rawlog(LT_ERR, T("WARNING: God (#%d) is NOT a player."), GOD); *************** *** 854,860 **** --- 867,875 ---- help_reindex(player); } + /** Check each attribute on each object in x for a $command matching cptr */ #define list_match(x) list_check(x, player, '$', ':', cptr, 0) + /** Check each attribute on x for a $command matching cptr */ #define cmd_match(x) atr_comm_match(x, player, '$', ':', cptr, 0, NULL, NULL); /** Attempt to match and execute a command. *************** *** 1135,1141 **** while (thing != NOTHING) { a = atr_get_noparent(thing, type); if (a) { ! strcpy(alias, uncompress(a->value)); if (check_alias(command, alias) != 0) return thing; /* matched an alias */ } --- 1150,1156 ---- while (thing != NOTHING) { a = atr_get_noparent(thing, type); if (a) { ! strcpy(alias, atr_value(a)); if (check_alias(command, alias) != 0) return thing; /* matched an alias */ } *************** *** 1159,1165 **** char alias[BUFFER_LEN]; a = atr_get_noparent(loc, type); if (a) { ! strcpy(alias, uncompress(a->value)); return (check_alias(command, alias)); } else return 0; --- 1174,1180 ---- char alias[BUFFER_LEN]; a = atr_get_noparent(loc, type); if (a) { ! strcpy(alias, atr_value(a)); return (check_alias(command, alias)); } else return 0; *************** *** 1182,1191 **** if (Connected(thing) || Puppet(thing)) return 1; for (ptr = List(thing); ptr; ptr = AL_NEXT(ptr)) { - #ifndef VISIBLE_EMPTY_ATTRS - if (!*AL_STR(ptr)) - continue; - #endif if (Audible(thing) && (strcmp(AL_NAME(ptr), "FORWARDLIST") == 0)) return 1; cmp = strcoll(AL_NAME(ptr), "LISTEN"); --- 1197,1202 ---- *************** *** 1211,1220 **** ALIST *ptr; for (ptr = List(thing); ptr; ptr = AL_NEXT(ptr)) { - #ifndef VISIBLE_EMPTY_ATTRS - if (!*AL_STR(ptr)) - continue; - #endif if (AL_FLAGS(ptr) & AF_COMMAND && !(AL_FLAGS(ptr) & AF_NOPROG)) return (1); } --- 1222,1227 ---- *************** *** 1314,1319 **** --- 1321,1327 ---- mush_free(command, "strip_braces.buff"); } + /** Would the scan command find an matching attribute on x for player p? */ #define ScanFind(p,x) \ (Can_Examine(p,x) && \ ((num = atr_comm_match(x, p, '$', ':', command, 1, atrname, &ptr)) != 0)) *************** *** 1559,1567 **** } } ! #define DOL_MAP 1 ! #define DOL_NOTIFY 2 ! #define DOL_DELIM 4 /** Execute a command for each element of a list. * \verbatim --- 1567,1575 ---- } } ! #define DOL_MAP 1 /**< The map command */ ! #define DOL_NOTIFY 2 /**< Add a notify after a dolist */ ! #define DOL_DELIM 4 /**< Specify a delimiter to a dolist */ /** Execute a command for each element of a list. * \verbatim *************** *** 2034,2040 **** } } ! /* List various goodies. * \verbatim * This function implements @list. * \endverbatim --- 2042,2048 ---- } } ! /** List various goodies. * \verbatim * This function implements @list. * \endverbatim *** 1_7_7.366/src/funufun.c Mon, 03 Mar 2003 21:59:06 -0600 dunemush (pennmush/c/11_funufun.c 1.15 660) --- 1_7_7.446(w)/src/funufun.c Thu, 29 May 2003 10:01:30 -0500 dunemush (pennmush/c/11_funufun.c 1.17 660) *************** *** 122,128 **** for (; j < 10; j++) wenv[j] = NULL; ! tp = tbuf = safe_uncompress(attrib->value); process_expression(buff, bp, &tp, obj, executor, enactor, PE_DEFAULT, PT_DEFAULT, pe_info); free(tbuf); --- 122,128 ---- for (; j < 10; j++) wenv[j] = NULL; ! tp = tbuf = safe_atr_value(attrib); process_expression(buff, bp, &tp, obj, executor, enactor, PE_DEFAULT, PT_DEFAULT, pe_info); free(tbuf); *** 1_7_7.366/src/funstr.c Sat, 05 Apr 2003 17:37:13 -0600 dunemush (pennmush/c/13_funstr.c 1.28.1.1.1.2.1.4.1.6.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.1.5.1.3.1.2.1.1.1.1.1.2 660) --- 1_7_7.446(w)/src/funstr.c Thu, 29 May 2003 10:01:30 -0500 dunemush (pennmush/c/13_funstr.c 1.28.1.1.1.2.1.4.1.6.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.1.5.1.3.1.2.1.1.1.1.1.1.1.1.1.7 660) *************** *** 37,43 **** #define LC_MESSAGES 6 #endif ! HASHTAB htab_tag; static int wraplen(char *str, int maxlen); void init_tag_hashtab(void); --- 37,43 ---- #define LC_MESSAGES 6 #endif ! HASHTAB htab_tag; /**< Hash table of safe html tags */ static int wraplen(char *str, int maxlen); void init_tag_hashtab(void); *************** *** 60,66 **** if (!a) return 0; ! switch (*uncompress(a->value)) { case 'T': case 't': case 'P': --- 60,66 ---- if (!a) return 0; ! switch (*atr_value(a)) { case 'T': case 't': case 'P': *************** *** 79,88 **** } } ! char *subj[4]; ! char *poss[4]; ! char *obj[4]; ! char *absp[4]; /** Initialize the pronoun translation strings. * This function sets up the values of the arrays of subjective, --- 79,91 ---- } } ! char *subj[4]; /**< Subjective pronouns */ ! char *poss[4]; /**< Possessive pronouns */ ! char *obj[4]; /**< Objective pronouns */ ! char *absp[4]; /**< Absolute possessive pronouns */ ! ! /** Macro to set a pronoun entry based on whether we're translating or not */ ! #define SET_PRONOUN(p,v,u) p = strdup((translate) ? (v) : (u)) /** Initialize the pronoun translation strings. * This function sets up the values of the arrays of subjective, *************** *** 100,106 **** translate = 1; } #endif - #define SET_PRONOUN(p,v,u) p = strdup((translate) ? (v) : (u)) SET_PRONOUN(subj[0], T("pronoun:neuter,subjective"), "it"); SET_PRONOUN(subj[1], T("pronoun:feminine,subjective"), "she"); SET_PRONOUN(subj[2], T("pronoun:masculine,subjective"), "he"); --- 103,108 ---- *************** *** 117,125 **** SET_PRONOUN(absp[1], T("pronoun:feminine,absolute possessive"), "hers"); SET_PRONOUN(absp[2], T("pronoun:masculine,absolute possessive"), "his"); SET_PRONOUN(absp[3], T("pronoun:plural,absolute possessive "), "theirs"); - #undef SET_PRONOUN } /* ARGSUSED */ FUNCTION(fun_isword) --- 119,127 ---- SET_PRONOUN(absp[1], T("pronoun:feminine,absolute possessive"), "hers"); SET_PRONOUN(absp[2], T("pronoun:masculine,absolute possessive"), "his"); SET_PRONOUN(absp[3], T("pronoun:plural,absolute possessive "), "theirs"); } + #undef SET_PRONOUN /* ARGSUSED */ FUNCTION(fun_isword) *************** *** 418,423 **** --- 420,460 ---- } /* ARGSUSED */ + FUNCTION(fun_strreplace) + { + ansi_string *as, *anew; + int start, len, end; + + if (!is_integer(args[1]) || !is_integer(args[2])) { + safe_str(T(e_ints), buff, bp); + return; + } + + start = parse_integer(args[1]); + len = parse_integer(args[2]); + + if (start < 0 || len < 0) { + safe_str(T(e_range), buff, bp); + return; + } + + as = parse_ansi_string(args[0]); + anew = parse_ansi_string(args[3]); + + safe_ansi_string(as, 0, start, buff, bp); + safe_ansi_string(anew, 0, anew->len, buff, bp); + + end = start + len; + + if ((size_t) end < as->len) + safe_ansi_string(as, end, as->len - end, buff, bp); + + free_ansi_string(as); + free_ansi_string(anew); + + } + + /* ARGSUSED */ FUNCTION(fun_comp) { char type = 'A'; *************** *** 846,852 **** return; strcpy(place, "0"); ! asave = safe_uncompress(attrib->value); /* save our stack */ tptr[0] = wenv[0]; --- 883,889 ---- return; strcpy(place, "0"); ! asave = safe_atr_value(attrib); /* save our stack */ tptr[0] = wenv[0]; *************** *** 1235,1274 **** } } ! #define COL_FLASH (1) ! #define COL_HILITE (2) ! #define COL_INVERT (4) ! #define COL_UNDERSCORE (8) ! ! #define VAL_FLASH (5) ! #define VAL_HILITE (1) ! #define VAL_INVERT (7) ! #define VAL_UNDERSCORE (4) ! ! #define COL_BLACK (30) ! #define COL_RED (31) ! #define COL_GREEN (32) ! #define COL_YELLOW (33) ! #define COL_BLUE (34) ! #define COL_MAGENTA (35) ! #define COL_CYAN (36) ! #define COL_WHITE (37) typedef struct { ! char flags; ! char fore; ! char back; } ansi_data; static void dump_ansi_codes(ansi_data * ad, char *buff, char **bp); #define EDGE_UP(x,y,z) (((y) & (z)) && !((x) & (z))) static void ! dump_ansi_codes(ad, buff, bp) ! ansi_data *ad; ! char *buff; ! char **bp; { static ansi_data old_ad = { 0, 0, 0 }; int f = 0; --- 1272,1310 ---- } } ! #define COL_FLASH (1) /**< ANSI flash attribute bit */ ! #define COL_HILITE (2) /**< ANSI hilite attribute bit */ ! #define COL_INVERT (4) /**< ANSI inverse attribute bit */ ! #define COL_UNDERSCORE (8) /**< ANSI underscore attribute bit */ ! ! #define VAL_FLASH (5) /**< ANSI flag attribute value */ ! #define VAL_HILITE (1) /**< ANSI hilite attribute value */ ! #define VAL_INVERT (7) /**< ANSI inverse attribute value */ ! #define VAL_UNDERSCORE (4) /**< ANSI underscore attribute value */ ! ! #define COL_BLACK (30) /**< ANSI color black */ ! #define COL_RED (31) /**< ANSI color red */ ! #define COL_GREEN (32) /**< ANSI color green */ ! #define COL_YELLOW (33) /**< ANSI color yellow */ ! #define COL_BLUE (34) /**< ANSI color blue */ ! #define COL_MAGENTA (35) /**< ANSI color magenta */ ! #define COL_CYAN (36) /**< ANSI color cyan */ ! #define COL_WHITE (37) /**< ANSI color white */ + /** The ansi attributes associated with a character. */ typedef struct { ! char flags; /**< Ansi text attributes */ ! char fore; /**< Ansi foreground color */ ! char back; /**< Ansi background color */ } ansi_data; static void dump_ansi_codes(ansi_data * ad, char *buff, char **bp); + /** If we're adding y to x, do we need to add z as well? */ #define EDGE_UP(x,y,z) (((y) & (z)) && !((x) & (z))) static void ! dump_ansi_codes(ansi_data * ad, char *buff, char **bp) { static ansi_data old_ad = { 0, 0, 0 }; int f = 0; *************** *** 1459,1517 **** /* ARGSUSED */ FUNCTION(fun_edit) { - int len; - char *str, *f, *r; int i; ! char prebuf[BUFFER_LEN], *prep; ! char postbuf[BUFFER_LEN], *postp; ! postp = postbuf; ! safe_str(args[0], postbuf, &postp); ! *postp = '\0'; for (i = 1; i < nargs - 1; i += 2) { ! /* Old postbuf is new prebuf */ ! prep = prebuf; ! safe_str(postbuf, prebuf, &prep); ! *prep = '\0'; ! str = prebuf; /* The current string */ f = args[i]; /* find this */ r = args[i + 1]; /* replace it with this */ ! postp = postbuf; /* Check for nothing to avoid infinite loop */ if (!*f && !*r) continue; ! if (!strcmp(f, "$")) { /* append */ ! safe_str(str, postbuf, &postp); ! safe_str(r, postbuf, &postp); ! } else if (!strcmp(f, "^")) { /* prepend */ ! safe_str(r, postbuf, &postp); ! safe_str(str, postbuf, &postp); } else { ! len = strlen(f); ! while (*str) { ! if (!strncmp(str, f, len)) { ! safe_str(r, postbuf, &postp); ! if (len) ! str += len; ! else { ! safe_chr(*str, postbuf, &postp); ! str++; ! } ! } else { ! safe_chr(*str, postbuf, &postp); ! str++; ! } } ! if (!*f) ! safe_str(r, postbuf, &postp); } *postp = '\0'; } ! safe_str(postbuf, buff, bp); } FUNCTION(fun_brackets) --- 1495,1557 ---- /* ARGSUSED */ FUNCTION(fun_edit) { int i; ! char *f, *r, *raw; ! ansi_string *prebuf; ! char postbuf[BUFFER_LEN], lastbuf[BUFFER_LEN], *postp; ! size_t rlen, flen; ! prebuf = parse_ansi_string(args[0]); ! raw = args[0]; for (i = 1; i < nargs - 1; i += 2) { ! ! postp = postbuf; f = args[i]; /* find this */ r = args[i + 1]; /* replace it with this */ ! flen = arglens[i]; ! rlen = arglens[i + 1]; /* Check for nothing to avoid infinite loop */ if (!*f && !*r) continue; ! if (flen == 1 && *f == '$') { /* append */ ! safe_str(raw, postbuf, &postp); ! safe_strl(r, rlen, postbuf, &postp); ! } else if (flen == 1 && *f == '^') { /* prepend */ ! safe_strl(r, rlen, postbuf, &postp); ! safe_str(raw, postbuf, &postp); ! } else if (!*f) { ! /* insert between every character */ ! size_t last; ! safe_strl(r, rlen, postbuf, &postp); ! for (last = 0; last < prebuf->len; last++) { ! safe_ansi_string(prebuf, last, 1, postbuf, &postp); ! safe_strl(r, rlen, postbuf, &postp); ! } } else { ! char *p; ! size_t last = 0; ! ! while (last < prebuf->len && (p = strstr(prebuf->text + last, f)) != NULL) { ! safe_ansi_string(prebuf, last, p - (prebuf->text + last), ! postbuf, &postp); ! safe_strl(r, rlen, postbuf, &postp); ! last = p - prebuf->text + flen; } ! if (last < prebuf->len) ! safe_ansi_string(prebuf, last, prebuf->len, postbuf, &postp); } *postp = '\0'; + free_ansi_string(prebuf); + prebuf = parse_ansi_string(postbuf); + strcpy(lastbuf, postbuf); + raw = lastbuf; } ! safe_ansi_string(prebuf, 0, prebuf->len, buff, bp); ! free_ansi_string(prebuf); } FUNCTION(fun_brackets) *************** *** 1592,1598 **** return (maxlen ? maxlen : -1); } ! /* The integer in string a will be stored in v, * if a is not an integer then d (efault) is stored in v. */ #define initint(a, v, d) \ --- 1632,1638 ---- return (maxlen ? maxlen : -1); } ! /** The integer in string a will be stored in v, * if a is not an integer then d (efault) is stored in v. */ #define initint(a, v, d) \ *** 1_7_7.366/src/funmath.c Sat, 29 Mar 2003 14:20:11 -0600 dunemush (pennmush/c/15_funmath.c 1.39.1.1.1.1.1.1.1.10.1.2 660) --- 1_7_7.446(w)/src/funmath.c Thu, 29 May 2003 10:01:28 -0500 dunemush (pennmush/c/15_funmath.c 1.39.1.1.1.1.1.1.1.10.1.5 660) *************** *** 32,42 **** static int nval_sort(const void *, const void *); static NVAL find_median(NVAL *, int); #define MATH_FUNC(func) static void func(char **ptr, int nptr, char *buff, char **bp) #define MATH_PROTO(func) static void func (char **ptr, int nptr, char *buff, char **bp) ! HASHTAB htab_math; /** A math function. */ typedef struct { --- 32,44 ---- static int nval_sort(const void *, const void *); static NVAL find_median(NVAL *, int); + /** Declaration macro for math functions */ #define MATH_FUNC(func) static void func(char **ptr, int nptr, char *buff, char **bp) + /** Prototype macro for math functions */ #define MATH_PROTO(func) static void func (char **ptr, int nptr, char *buff, char **bp) ! HASHTAB htab_math; /**< Math function hash table */ /** A math function. */ typedef struct { *************** *** 48,53 **** --- 50,57 ---- static MATH *math_hash_lookup(char *); static NVAL angle_to_rad(NVAL angle, const char *from); static NVAL rad_to_angle(NVAL angle, const char *to); + static double frac(double v, int *RESTRICT n, int *RESTRICT d, double error); + void init_math_hashtab(void); MATH_PROTO(math_add); MATH_PROTO(math_sub); *************** *** 1026,1037 **** } num = parse_number(args[0]); if (num < 0) { ! safe_str(T("#-1 SQUARE ROOT OF NEGATIVE"), buff, bp); return; } safe_number(sqrt(num), buff, bp); } FUNCTION(fun_isint) { safe_boolean(is_strict_integer(args[0]), buff, bp); --- 1030,1203 ---- } num = parse_number(args[0]); if (num < 0) { ! safe_str(T("#-1 IMAGINARY NUMBER"), buff, bp); return; } safe_number(sqrt(num), buff, bp); } + FUNCTION(fun_root) + { + NVAL root, x; + int n; + int sign = 0; + + if (!is_number(args[0])) { + safe_str(T(e_num), buff, bp); + return; + } + if (!is_integer(args[1])) { + safe_str(T(e_int), buff, bp); + return; + } + x = parse_number(args[0]); + n = parse_integer(args[1]); + + if (n < 0) { + safe_str(T("#-1 ROOT OUT OF RANGE"), buff, bp); + return; + } + + if (x < 0) { + if (n & 1) { /* Odd root */ + sign = 1; + x = fabs(x); + } else { /* Even */ + safe_str(T("#-1 IMAGINARY NUMBER"), buff, bp); + return; + } + } + + switch (n) { + case 0: + root = 0; + break; + case 1: + root = x; + break; + case 2: + root = sqrt(x); + break; + case 3: + /* TO-DO: Configure check for cbrt()? */ + default: + /* Spiffy logarithm trick */ + root = exp(log(x) / n); + } + + if (sign) + root = -root; + + safe_number(root, buff, bp); + } + + + /** Calculates the numerator and denominator for a fraction representing + * a floating point number. Only works for positive numbers! + * \param v the number + * \param n pointer to the numerator + * \param d pointer to the denominator + * \param error accuracy to which the fraction should represent the original number + * \return -1.0 if (v < MIN || v > MAX || error < 0.0) | (v - n/d) / v | otherwise. + */ + static double + frac(double v, int *RESTRICT n, int *RESTRICT d, double error) + { + + /* Based on a routine found in netlib (http://www.netlib.org) by + + Robert J. Craig + AT&T Bell Laboratories + 1200 East Naperville Road + Naperville, IL 60566-7045 + + though I have no idea if that address is still valid. + + Rewritten by Raevnos to use modern C, and get rid of stupid gotos. + + reference: Jerome Spanier and Keith B. Oldham, "An Atlas + of Functions," Springer-Verlag, 1987, pp. 665-7. + */ + + + int D, N, t; + int first = 1; + double epsilon, r = 0.0, m; + + if (v < 0 || error < 0.0) + return -1.0; + *d = D = 1; + *n = (int) v; + N = *n + 1; + + do { + if (!first) { + if (r <= 1.0) + r = 1.0 / r; + N += *n * (int) r; + D += *d * (int) r; + *n += N; + *d += D; + } else + first = 0; + r = 0.0; + if (v * (*d) != *n) { + r = (N - v * D) / (v * (*d) - *n); + if (r <= 1.0) { + t = N; + N = *n; + *n = t; + t = D; + D = *d; + *d = t; + } + } + epsilon = fabs(1.0 - *n / (v * (*d))); + if (epsilon <= error) + return epsilon; + m = 1.0; + do { + m *= 10.0; + } while (m * epsilon < 1.0); + epsilon = 1.0 / m * ((int) (0.5 + m * epsilon)); + if (epsilon <= error) + return epsilon; + } while (r != 0.0); + return epsilon; + } + + FUNCTION(fun_fraction) + { + int num = 0, denom = 0; + NVAL n; + int sign = 0; + + if (!is_number(args[0])) { + safe_str(T(e_num), buff, bp); + return; + } + + n = parse_number(args[0]); + + if (n < 0) { + n = fabs(n); + sign = 1; + } else if (n == 0) { + safe_chr('0', buff, bp); + return; + } + + frac(n, &num, &denom, 1.0e-10); + + if (sign) + safe_chr('-', buff, bp); + + if (denom == 1) + safe_integer(num, buff, bp); + else + safe_format(buff, bp, "%d/%d", num, denom); + } + FUNCTION(fun_isint) { safe_boolean(is_strict_integer(args[0]), buff, bp); *************** *** 1115,1127 **** math_xor(args, nargs, buff, bp); } static void ! do_spellnum(num, len, buff, bp) ! char *num; /* string containing an integer. */ ! unsigned int len; /* The string length must be a multiple of 3, ! with a maximum of 15. */ ! char **buff; /* output is the integer spelled out */ ! char ***bp; { static const char *bigones[] = { "", "thousand", "million", "billion", "trillion" }; --- 1281,1294 ---- math_xor(args, nargs, buff, bp); } + /** Return the spelled-out version of an integer. + * \param num string containing an integer to spell out. + * \param len length of num, which must be a multiple of 3, max 15. + * \param buff pointer to address of output buffer. + * \param bp pointer to pointer to insertion point in buff. + */ static void ! do_spellnum(char *num, unsigned int len, char **buff, char ***bp) { static const char *bigones[] = { "", "thousand", "million", "billion", "trillion" }; *************** *** 2023,2028 **** --- 2190,2196 ---- safe_number(sqrt(s / (nptr - 1)), buff, bp); } + /** A list of MATH_FUNCs that are suitable for lmath() */ MATH mlist[] = { {"ADD", math_add} , *************** *** 2079,2085 **** return (MATH *) hashfind(strupper(name), &htab_math); } - void init_math_hashtab(void); static void math_hash_insert(const char *name, MATH *func) --- 2247,2252 ---- *************** *** 2087,2092 **** --- 2254,2260 ---- hashadd(name, (void *) func, &htab_math); } + /** Initialize the math function hash table. */ void init_math_hashtab(void) { *** 1_7_7.366/src/funlist.c Thu, 27 Feb 2003 22:05:20 -0600 dunemush (pennmush/c/16_funlist.c 1.3.1.1.1.5.1.2.1.1.1.1.1.4.1.2.1.2.1.19.1.2.1.1.1.2.1.5.1.1.1.1.1.1.1.1.1.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.1.1.12.1.6 660) --- 1_7_7.446(w)/src/funlist.c Thu, 29 May 2003 10:01:28 -0500 dunemush (pennmush/c/16_funlist.c 1.3.1.1.1.5.1.2.1.1.1.1.1.4.1.2.1.2.1.19.1.2.1.1.1.2.1.5.1.1.1.1.1.1.1.1.1.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.1.1.12.1.13 660) *************** *** 26,32 **** #include "lock.h" #include "confmagic.h" ! #define MAX_SORTSIZE (BUFFER_LEN / 2) static char *next_token(char *str, char sep); static list_type autodetect_list(char **ptrs, int nptrs); --- 26,32 ---- #include "lock.h" #include "confmagic.h" ! #define MAX_SORTSIZE (BUFFER_LEN / 2) /**< Maximum number of elements to sort */ static char *next_token(char *str, char sep); static list_type autodetect_list(char **ptrs, int nptrs); *************** *** 40,55 **** static int u_comp(const void *s1, const void *s2); static int regrep_helper(dbref who, dbref what, char const *name, ATTR *atr, void *args); typedef int (*comp_func) (const void *, const void *); static void sane_qsort(void **array, int left, int right, comp_func compare); static void do_itemfuns(char *buff, char **bp, char *str, char *num, char *word, char *sep, int flag); static int qparse_dbref(const char *s); ! char *iter_rep[MAX_ITERS]; ! int iter_place[MAX_ITERS]; ! int inum = 0; ! int inum_limit = 0; extern const unsigned char *tables; static char * --- 40,56 ---- static int u_comp(const void *s1, const void *s2); static int regrep_helper(dbref who, dbref what, char const *name, ATTR *atr, void *args); + /** Type definition for a qsort comparison function */ typedef int (*comp_func) (const void *, const void *); static void sane_qsort(void **array, int left, int right, comp_func compare); static void do_itemfuns(char *buff, char **bp, char *str, char *num, char *word, char *sep, int flag); static int qparse_dbref(const char *s); ! char *iter_rep[MAX_ITERS]; /**< itext values */ ! int iter_place[MAX_ITERS]; /**< inum numbers */ ! int inum = 0; /**< iter depth */ ! int inum_limit = 0; /**< limit of iter depth */ extern const unsigned char *tables; static char * *************** *** 382,388 **** if (!result || !rsave) panic("Unable to allocate memory in fun_fold"); ! abuf = safe_uncompress(attrib->value); /* save our stack */ tptr[0] = wenv[0]; --- 383,389 ---- if (!result || !rsave) panic("Unable to allocate memory in fun_fold"); ! abuf = safe_atr_value(attrib); /* save our stack */ tptr[0] = wenv[0]; *************** *** 525,531 **** if (!CanEval(executor, thing)) return; ! abuf = safe_uncompress(attrib->value); tptr = wenv[0]; --- 526,532 ---- if (!CanEval(executor, thing)) return; ! abuf = safe_atr_value(attrib); tptr = wenv[0]; *************** *** 657,662 **** --- 658,666 ---- case 'A': case 'a': return ALPHANUM_LIST; + case 'I': + case 'i': + return INSENS_ALPHANUM_LIST; case 'D': case 'd': return DBREF_LIST; *************** *** 683,688 **** --- 687,695 ---- case 'A': case 'a': return ALPHANUM_LIST; + case 'I': + case 'i': + return INSENS_ALPHANUM_LIST; case 'D': case 'd': return DBREF_LIST; *************** *** 711,724 **** return strcasecoll(*(char *const *) s1, *(char *const *) s2); } - typedef struct i_record i_rec; /** An integer, for sorting purposes. We store both the string and * the int forms to make some things more efficient. */ ! struct i_record { char *str; /**< string representation */ int num; /**< integer representation */ ! }; /** Integer comparison routine for sorts. * \param s1 void pointer to an i_record. --- 718,730 ---- return strcasecoll(*(char *const *) s1, *(char *const *) s2); } /** An integer, for sorting purposes. We store both the string and * the int forms to make some things more efficient. */ ! typedef struct i_record { char *str; /**< string representation */ int num; /**< integer representation */ ! } i_rec; /** Integer comparison routine for sorts. * \param s1 void pointer to an i_record. *************** *** 737,750 **** return 0; } - typedef struct f_record f_rec; /** A double, for sorting purposes. We store both the string and * the NVAL forms to make some things more efficient. */ ! struct f_record { char *str; /**< string representation */ NVAL num; /**< numeric representation */ ! }; static int f_comp(const void *s1, const void *s2) --- 743,755 ---- return 0; } /** A double, for sorting purposes. We store both the string and * the NVAL forms to make some things more efficient. */ ! typedef struct f_record { char *str; /**< string representation */ NVAL num; /**< numeric representation */ ! } f_rec; static int f_comp(const void *s1, const void *s2) *************** *** 1044,1050 **** if (!CanEval(executor, thing)) return; up = ucomp_buff; ! safe_str(uncompress(attrib->value), ucomp_buff, &up); *up = '\0'; ucomp_executor = thing; --- 1049,1055 ---- if (!CanEval(executor, thing)) return; up = ucomp_buff; ! safe_str(atr_value(attrib), ucomp_buff, &up); *up = '\0'; ucomp_executor = thing; *************** *** 1232,1238 **** } safe_strl(osep, osepl, buff, bp); safe_str(a1[x1], buff, bp); ! while (!strcmp(a1[x1], a2[x2])) { x1++; if (x1 >= n1) { mush_free((Malloc_t) a1, "ptrarray"); --- 1237,1243 ---- } safe_strl(osep, osepl, buff, bp); safe_str(a1[x1], buff, bp); ! while (!gencomp(a1[x1], a2[x2], sort_type)) { x1++; if (x1 >= n1) { mush_free((Malloc_t) a1, "ptrarray"); *************** *** 1357,1362 **** --- 1362,1369 ---- mush_free((Malloc_t) a2, "ptrarray"); } + #define CACHE_SIZE 8 /**< Maximum size of the lnum cache */ + /* ARGSUSED */ FUNCTION(fun_lnum) { *************** *** 1365,1372 **** NVAL end; int istart, iend, k; char const *osep = " "; - - #define CACHE_SIZE 8 static NVAL cstart[CACHE_SIZE]; static NVAL cend[CACHE_SIZE]; static char csep[CACHE_SIZE][BUFFER_LEN]; --- 1372,1377 ---- *************** *** 1794,1808 **** safe_str(s, buff, bp); } static void ! do_itemfuns(buff, bp, str, num, word, sep, flag) ! char *buff; /* the return buffer */ ! char **bp; /* the active point in the return buffer */ ! char *str; /* the original string */ ! char *num; /* the element number */ ! char *word; /* word to insert or replace */ ! char *sep; /* the separator */ ! int flag; /* op -- 0 delete, 1 replace, 2 insert */ { char c; int el, count; --- 1799,1816 ---- safe_str(s, buff, bp); } + /** Functions that operate on items - delete, replace, insert. + * \param buff return buffer. + * \param bp pointer to insertion point in buff. + * \param str original string. + * \param num string containing the element number to operate on. + * \param word string to insert/delete/replace. + * \param sep separator string. + * \param flag operation to perform: 0 - delete, 1 - replace, 2 - insert + */ static void ! do_itemfuns(char *buff, char **bp, char *str, char *num, char *word, ! char *sep, int flag) { char c; int el, count; *************** *** 2257,2263 **** if (!CanEval(executor, thing)) return; ! asave = safe_uncompress(attrib->value); /* save our stack */ save_global_env("step", preserve); --- 2265,2271 ---- if (!CanEval(executor, thing)) return; ! asave = safe_atr_value(attrib); /* save our stack */ save_global_env("step", preserve); *************** *** 2344,2350 **** return; strcpy(place, "1"); ! asave = safe_uncompress(attrib->value); /* save our stack */ tptr[0] = wenv[0]; --- 2352,2358 ---- return; strcpy(place, "1"); ! asave = safe_atr_value(attrib); /* save our stack */ tptr[0] = wenv[0]; *************** *** 2417,2423 **** if (!CanEval(executor, thing)) return; ! asave = safe_uncompress(attrib->value); /* save our stack */ save_global_env("fun_mix", tptr); --- 2425,2431 ---- if (!CanEval(executor, thing)) return; ! asave = safe_atr_value(attrib); /* save our stack */ save_global_env("fun_mix", tptr); *************** *** 2577,2585 **** /* string, regexp, replacement string. Acts like sed or perl's s///g, //with an ig version */ ! int re_subpatterns = -1; ! int *re_offsets; ! char *re_from = NULL; FUNCTION(fun_regreplace) { pcre *re; --- 2585,2593 ---- /* string, regexp, replacement string. Acts like sed or perl's s///g, //with an ig version */ ! int re_subpatterns = -1; /**< Number of subpatterns in regexp */ ! int *re_offsets; /**< Array of offsets to subpatterns */ ! char *re_from = NULL; /**< Pointer to last match position */ FUNCTION(fun_regreplace) { pcre *re; *************** *** 2719,2726 **** safe_str(postbuf, buff, bp); } ! extern signed char qreg_indexes[UCHAR_MAX + 1]; FUNCTION(fun_regmatch) { /* --------------------------------------------------------------------------- --- 2727,2735 ---- safe_str(postbuf, buff, bp); } ! /** array of indexes for %q registers during regexp matching */ extern signed char qreg_indexes[UCHAR_MAX + 1]; + FUNCTION(fun_regmatch) { /* --------------------------------------------------------------------------- *************** *** 2797,2808 **** } struct regrep_data { ! pcre *re; ! pcre_extra *study; ! char *buff; ! char **bp; ! int first; }; /* Like grep(), but using a regexp pattern. This same function handles --- 2806,2818 ---- } + /** Structure to hold data for regrep */ struct regrep_data { ! pcre *re; /**< Pointer to compiled regular expression */ ! pcre_extra *study; /**< Pointer to studied data about re */ ! char *buff; /**< Buffer to store regrep results */ ! char **bp; /**< Pointer to address of insertion point in buff */ ! int first; /**< Is this the first match or a later match? */ }; /* Like grep(), but using a regexp pattern. This same function handles *************** *** 2870,2887 **** } static int ! regrep_helper(who, what, name, atr, args) ! dbref who __attribute__ ((__unused__)); ! dbref what __attribute__ ((__unused__)); ! char const *name __attribute__ ((__unused__)); ! ATTR *atr; ! void *args; { struct regrep_data *reharg = args; char const *str; int offsets[99]; ! str = uncompress(AL_STR(atr)); if (pcre_exec(reharg->re, reharg->study, str, strlen(str), 0, 0, offsets, 99) >= 0) { if (reharg->first != 0) --- 2880,2895 ---- } static int ! regrep_helper(dbref who __attribute__ ((__unused__)), ! dbref what __attribute__ ((__unused__)), ! char const *name __attribute__ ((__unused__)), ! ATTR *atr, void *args) { struct regrep_data *reharg = args; char const *str; int offsets[99]; ! str = atr_value(atr); if (pcre_exec(reharg->re, reharg->study, str, strlen(str), 0, 0, offsets, 99) >= 0) { if (reharg->first != 0) *** 1_7_7.366/src/fundb.c Thu, 27 Mar 2003 21:05:56 -0600 dunemush (pennmush/c/17_fundb.c 1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.7.1.3.1.3.1.3.1.2.1.2.1.3.2.1.2.1.2.1.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.3.1.1.2.2.2.1.1.1.1.1.1.9 660) --- 1_7_7.446(w)/src/fundb.c Thu, 29 May 2003 10:01:27 -0500 dunemush (pennmush/c/17_fundb.c 1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.7.1.3.1.3.1.3.1.2.1.2.1.3.2.1.2.1.2.1.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.3.1.1.2.2.2.1.1.1.1.1.1.13 660) *************** *** 38,45 **** extern PRIV attr_privs[]; static const char *do_get_attrib(dbref executor, dbref thing, const char *attrib); - dbref next_con(dbref player, dbref this); - dbref next_exit(dbref player, dbref this); static lock_type get_locktype(char *str); extern struct db_stat_info *get_stats(dbref owner); static int lattr_helper(dbref player, dbref thing, char const *pattern, --- 38,43 ---- *************** *** 58,64 **** a = atr_get(thing, strupper(attrib)); if (a) { if (Can_Read_Attr(executor, thing, a)) { ! if (strlen(value = uncompress(a->value)) < BUFFER_LEN) return value; else return T("#-1 ATTRIBUTE LENGTH TOO LONG"); --- 56,62 ---- a = atr_get(thing, strupper(attrib)); if (a) { if (Can_Read_Attr(executor, thing, a)) { ! if (strlen(value = atr_value(a)) < BUFFER_LEN) return value; else return T("#-1 ATTRIBUTE LENGTH TOO LONG"); *************** *** 76,85 **** return ""; } struct lh_args { ! int first; ! char *buff; ! char **bp; }; /* this function produces a space-separated list of attributes that are --- 74,84 ---- return ""; } + /** Structure containing arguments for lattr_helper */ struct lh_args { ! int first; /**< Is this is the first attribute, or later? */ ! char *buff; /**< Buffer to store output */ ! char **bp; /**< Pointer to address of insertion point in buff */ }; /* this function produces a space-separated list of attributes that are *************** *** 87,98 **** */ /* ARGSUSED */ static int ! lattr_helper(player, thing, pattern, atr, args) ! dbref player __attribute__ ((__unused__)); ! dbref thing __attribute__ ((__unused__)); ! char const *pattern __attribute__ ((__unused__)); ! ATTR *atr; ! void *args; { struct lh_args *lh = args; if (lh->first) --- 86,95 ---- */ /* ARGSUSED */ static int ! lattr_helper(dbref player __attribute__ ((__unused__)), ! dbref thing __attribute__ ((__unused__)), ! char const *pattern __attribute__ ((__unused__)), ! ATTR *atr, void *args) { struct lh_args *lh = args; if (lh->first) *************** *** 231,237 **** parse_attrib(executor, mstr, &thing, &attrib); if (GoodObject(thing) && attrib && Can_Read_Attr(executor, thing, attrib)) { /* Ok, we've got it */ ! dp = safe_uncompress(attrib->value); safe_str(dp, buff, bp); free(dp); return; --- 228,234 ---- parse_attrib(executor, mstr, &thing, &attrib); if (GoodObject(thing) && attrib && Can_Read_Attr(executor, thing, attrib)) { /* Ok, we've got it */ ! dp = safe_atr_value(attrib); safe_str(dp, buff, bp); free(dp); return; *************** *** 264,270 **** safe_str(T(e_perm), buff, bp); return; } ! tp = tbuf = safe_uncompress(a->value); #ifdef MEM_CHECK add_check("fun_eval.attr_value"); #endif --- 261,267 ---- safe_str(T(e_perm), buff, bp); return; } ! tp = tbuf = safe_atr_value(a); #ifdef MEM_CHECK add_check("fun_eval.attr_value"); #endif *************** *** 306,312 **** safe_str(T(e_perm), buff, bp); return; } ! tp = tbuf = safe_uncompress(a->value); #ifdef MEM_CHECK add_check("fun_eval.attr_value"); #endif --- 303,309 ---- safe_str(T(e_perm), buff, bp); return; } ! tp = tbuf = safe_atr_value(a); #ifdef MEM_CHECK add_check("fun_eval.attr_value"); #endif *************** *** 345,351 **** return; } /* Ok, we've got it */ ! sp = sbuf = safe_uncompress(attrib->value); #ifdef MEM_CHECK add_check("fun_edefault.attr_value"); #endif --- 342,348 ---- return; } /* Ok, we've got it */ ! sp = sbuf = safe_atr_value(attrib); #ifdef MEM_CHECK add_check("fun_edefault.attr_value"); #endif *************** *** 571,579 **** } /* ! * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next, and next_exit were all ! * re-coded by d'mike, 7/12/91. next_con was added at this time as well. ! * * The function behavior was changed by Amberyl, to remove what she saw * as a security problem, since mortals could get the contents of rooms * they didn't control, thus (if they were willing to go through the trouble) --- 568,576 ---- } /* ! * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next were all ! * re-coded by d'mike, 7/12/91. ! * * The function behavior was changed by Amberyl, to remove what she saw * as a security problem, since mortals could get the contents of rooms * they didn't control, thus (if they were willing to go through the trouble) *************** *** 1918,1924 **** a = atr_get_noparent(thing, "FOLLOWERS"); if (!a) return; ! s = uncompress(a->value); res = trim_space_sep(s, ' '); safe_str(res, buff, bp); return;; --- 1915,1921 ---- a = atr_get_noparent(thing, "FOLLOWERS"); if (!a) return; ! s = atr_value(a); res = trim_space_sep(s, ' '); safe_str(res, buff, bp); return;; *************** *** 1940,1946 **** a = atr_get_noparent(thing, "FOLLOWING"); if (!a) return; ! s = uncompress(a->value); res = trim_space_sep(s, ' '); safe_str(res, buff, bp); return;; --- 1937,1943 ---- a = atr_get_noparent(thing, "FOLLOWING"); if (!a) return; ! s = atr_value(a); res = trim_space_sep(s, ' '); safe_str(res, buff, bp); return;; *** 1_7_7.366/src/function.c Sun, 30 Mar 2003 22:39:28 -0600 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.11 660) --- 1_7_7.446(w)/src/function.c Thu, 29 May 2003 10:01:27 -0500 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.16 660) *************** *** 34,42 **** static void func_hash_insert(const char *name, FUN *func); extern void local_functions(void); ! USERFN_ENTRY *userfn_tab; ! HASHTAB htab_function; ! HASHTAB htab_user_function; /* -------------------------------------------------------------------------* * Utilities. --- 34,42 ---- static void func_hash_insert(const char *name, FUN *func); extern void local_functions(void); ! USERFN_ENTRY *userfn_tab; /**< Table of user-defined functions */ ! HASHTAB htab_function; /**< Function hash table */ ! HASHTAB htab_user_function; /**< User-defined function hash table */ /* -------------------------------------------------------------------------* * Utilities. *************** *** 131,137 **** * \param nfargs number of arguments to the function. * \param fargs array of function arguments. * \param sep_arg index of the argument to check for a delimiter. ! * \param set pointer to separator character, used to return separator. * \retval 0 illegal separator argument. * \retval 1 successfully returned a separator (maybe the default one). */ --- 131,137 ---- * \param nfargs number of arguments to the function. * \param fargs array of function arguments. * \param sep_arg index of the argument to check for a delimiter. ! * \param sep pointer to separator character, used to return separator. * \retval 0 illegal separator argument. * \retval 1 successfully returned a separator (maybe the default one). */ *************** *** 162,177 **** /** An entry in the function table. * This structure represents a function's entry in the function table. */ ! struct fun_tab { const char *name; /**< Name of the function, uppercase. */ function_func fun; /**< Pointer to code to call for this function. */ int minargs; /**< Minimum args required. */ int maxargs; /**< Maximum args, or INT_MAX. If <0, last arg may have commas */ int flags; /**< Flags to control how the function is parsed. */ ! }; - typedef struct fun_tab FUNTAB; FUNTAB flist[] = { {"@@", fun_atat, 1, -1, FN_NOPARSE}, {"ABS", fun_abs, 1, 1, FN_REG}, --- 162,179 ---- /** An entry in the function table. * This structure represents a function's entry in the function table. */ ! typedef struct fun_tab { const char *name; /**< Name of the function, uppercase. */ function_func fun; /**< Pointer to code to call for this function. */ int minargs; /**< Minimum args required. */ int maxargs; /**< Maximum args, or INT_MAX. If <0, last arg may have commas */ int flags; /**< Flags to control how the function is parsed. */ ! } FUNTAB; + /** The function table. Functions can also be added at runtime with + * add_function(). + */ FUNTAB flist[] = { {"@@", fun_atat, 1, -1, FN_NOPARSE}, {"ABS", fun_abs, 1, 1, FN_REG}, *************** *** 271,276 **** --- 273,279 ---- {"FOLLOWERS", fun_followers, 1, 1, FN_REG}, {"FOLLOWING", fun_following, 1, 1, FN_REG}, {"FOREACH", fun_foreach, 2, 4, FN_REG}, + {"FRACTION", fun_fraction, 1, 1, FN_REG}, {"FUNCTIONS", fun_functions, 0, 0, FN_REG}, {"FULLNAME", fun_fullname, 1, 1, FN_REG}, {"GET", fun_get, 1, 1, FN_REG}, *************** *** 433,438 **** --- 436,442 ---- {"RLOC", fun_rloc, 2, 2, FN_REG}, {"RNUM", fun_rnum, 2, 2, FN_REG}, {"ROOM", fun_room, 1, 1, FN_REG}, + {"ROOT", fun_root, 2, 2, FN_REG}, {"S", fun_s, 1, -1, FN_REG}, {"SCAN", fun_scan, 2, -2, FN_REG}, {"SCRAMBLE", fun_scramble, 1, -1, FN_REG}, *************** *** 465,470 **** --- 469,475 ---- {"STRIPANSI", fun_stripansi, 1, -1, FN_REG}, {"STRLEN", fun_strlen, 1, -1, FN_REG}, {"STRMATCH", fun_strmatch, 2, 2, FN_REG}, + {"STRREPLACE", fun_strreplace, 4, 4, FN_REG}, {"SUB", fun_sub, 2, 2, FN_REG}, {"SUBJ", fun_subj, 1, 1, FN_REG}, {"SWITCH", fun_switch, 3, INT_MAX, FN_NOPARSE}, *************** *** 607,615 **** } static void ! func_hash_insert(name, func) ! const char *name; ! FUN *func; { hashadd(name, (void *) func, &htab_function); } --- 612,618 ---- } static void ! func_hash_insert(const char *name, FUN *func) { hashadd(name, (void *) func, &htab_function); } *************** *** 675,681 **** /** Add an alias to a function. * This function adds an alias to a function in the hash table. * \param function name of function to alias. ! * \param function name of the alias to add. * \retval 0 failure (alias exists, or function doesn't, or is a user fun). * \retval 1 success. */ --- 678,684 ---- /** Add an alias to a function. * This function adds an alias to a function in the hash table. * \param function name of function to alias. ! * \param alias name of the alias to add. * \retval 0 failure (alias exists, or function doesn't, or is a user fun). * \retval 1 success. */ *** 1_7_7.366/src/flags.c Wed, 26 Mar 2003 14:57:24 -0600 dunemush (pennmush/c/20_flags.c 1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.2.1.1.1.1.1.2.2.2.2.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.3.1.9.1.2.2.1.1.2.1.41 660) --- 1_7_7.446(w)/src/flags.c Thu, 29 May 2003 10:01:27 -0500 dunemush (pennmush/c/20_flags.c 1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.2.1.1.1.1.1.2.2.2.2.1.2.1.1.3.1.2.1.1.1.1.1.1.1.1.1.3.1.9.1.2.2.1.1.2.1.49 660) *************** *** 60,69 **** static char *list_aliases(FLAG *given); static void realloc_object_flag_bitmasks(int numbytes); ! PTAB ptab_flag; /* Table of flags by name, inc. aliases */ ! FLAG **flags = NULL; /* A variable-length array of pointers, ! * to canonical flags, indexed by bit ! * position. Aliases not inclued. */ static int flagbits = 0; /* The current length of the flags array */ extern PTAB ptab_command; /* Uses flag bitmasks */ --- 60,69 ---- static char *list_aliases(FLAG *given); static void realloc_object_flag_bitmasks(int numbytes); ! PTAB ptab_flag; /**< Table of flags by name, inc. aliases */ ! FLAG **flags = NULL; /**< A variable-length array of pointers, ! * to canonical flags, indexed by bit ! * position. Aliases not inclued. */ static int flagbits = 0; /* The current length of the flags array */ extern PTAB ptab_command; /* Uses flag bitmasks */ *************** *** 610,615 **** --- 610,616 ---- { add_flag("MISTRUST", 'm', TYPE_THING | TYPE_EXIT | TYPE_ROOM, F_INHERIT, F_INHERIT); + add_flag("ORPHAN", 'i', NOTYPE, F_ANY, F_ANY); local_flags(); } *************** *** 676,681 **** --- 677,683 ---- return bitmask; } + /** Macro to detrmine if flag f's name is n */ #define is_flag(f,n) (!strcmp(f->name,n)) /* Given a single character, return the matching flag definition */ *************** *** 702,709 **** * Functions for managing bitmasks */ ! /* Locate a specific byte and bit given a bit position */ #define FlagByte(x) (x / 8) #define FlagBit(x) (7 - (x % 8)) /** Allocate a new flag bitmask. --- 704,712 ---- * Functions for managing bitmasks */ ! /** Locate a specific byte given a bit position */ #define FlagByte(x) (x / 8) + /** Locate a specific bit within a byte given a bit position */ #define FlagBit(x) (7 - (x % 8)) /** Allocate a new flag bitmask. *************** *** 1136,1142 **** } ! /* Set or clear flags on an object, with full permissions/hear checking. * \verbatim * This function is used to set and clear flags through @set and the like. * It does permission checking and handles the "is now listening" messages. --- 1139,1145 ---- } ! /** Set or clear flags on an object, with full permissions/hear checking. * \verbatim * This function is used to set and clear flags through @set and the like. * It does permission checking and handles the "is now listening" messages. *************** *** 1190,1203 **** tp = tbuf1; safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(Location(thing)), NOTHING, tbuf1, NA_INTERACTION); notify_except(Contents(thing), NOTHING, tbuf1, 0); } if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !Listener(thing)) { tp = tbuf1; safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(thing), NOTHING, tbuf1, NA_INTERACTION); } if (is_flag(f, "AUDIBLE")) { switch (Typeof(thing)) { --- 1193,1207 ---- tp = tbuf1; safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(Location(thing)), NOTHING, tbuf1, ! NA_INTER_PRESENCE); notify_except(Contents(thing), NOTHING, tbuf1, 0); } if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !Listener(thing)) { tp = tbuf1; safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(thing), NOTHING, tbuf1, NA_INTER_PRESENCE); } if (is_flag(f, "AUDIBLE")) { switch (Typeof(thing)) { *************** *** 1252,1258 **** tp = tbuf1; safe_format(tbuf1, &tp, T("%s is now listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(Location(thing)), NOTHING, tbuf1, NA_INTERACTION); notify_except(Contents(thing), NOTHING, tbuf1, 0); } if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !listener) { --- 1256,1263 ---- tp = tbuf1; safe_format(tbuf1, &tp, T("%s is now listening."), Name(thing)); *tp = '\0'; ! notify_except(Contents(Location(thing)), NOTHING, tbuf1, ! NA_INTER_PRESENCE); notify_except(Contents(thing), NOTHING, tbuf1, 0); } if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !listener) { *************** *** 1490,1496 **** * needs to add its own flags. It will not add the same flag twice. * \endverbatim * \param name flag name. ! * \param letter flag character (or '\0') * \param type mask of object types to which the flag applies. * \param perms mask of permissions to see/set the flag. * \param negate_perms mask of permissions to clear the flag. --- 1495,1501 ---- * needs to add its own flags. It will not add the same flag twice. * \endverbatim * \param name flag name. ! * \param letter flag character (or ascii 0) * \param type mask of object types to which the flag applies. * \param perms mask of permissions to see/set the flag. * \param negate_perms mask of permissions to clear the flag. *************** *** 1670,1676 **** letter = *args_right[1]; /* Do we have a type? */ if (args_right[2]) { ! if (*args_right[2]) type = string_to_privs(type_privs, args_right[2], 0); if (type < 0) { notify(player, T("I don't understand the list of types.")); --- 1675,1681 ---- letter = *args_right[1]; /* Do we have a type? */ if (args_right[2]) { ! if (*args_right[2] && strcasecmp(args_right[2], "any")) type = string_to_privs(type_privs, args_right[2], 0); if (type < 0) { notify(player, T("I don't understand the list of types.")); *************** *** 1861,1867 **** free((Malloc_t) f); } ! /* Enable a disabled flag. * \verbatim * This function implements @flag/enable. * Only God can do this, and it reverses /disable. --- 1866,1872 ---- free((Malloc_t) f); } ! /** Enable a disabled flag. * \verbatim * This function implements @flag/enable. * Only God can do this, and it reverses /disable. *** 1_7_7.366/src/extmail.c Thu, 26 Dec 2002 14:24:59 -0600 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.2 660) --- 1_7_7.446(w)/src/extmail.c Thu, 29 May 2003 10:01:27 -0500 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.1.1.3 660) *************** *** 1,4 **** ! /*--------------------------------------------------------------- * extmail.c - Javelin's improved @mail system * Based on Amberyl's linked list mailer * --- 1,10 ---- ! /** ! * \file extmail.c ! * ! * \brief The PennMUSH built-in mail system. ! * ! * \verbatim ! *--------------------------------------------------------------- * extmail.c - Javelin's improved @mail system * Based on Amberyl's linked list mailer * *************** *** 47,53 **** * instead of scanning the whole maildb to find the insertion point, * we start the scan from the chain of the connected player with the * closest db# to the target player. This scales up very well. ! *--------------------------------------------------------------------*/ #include "config.h" #include "copyrite.h" --- 53,61 ---- * instead of scanning the whole maildb to find the insertion point, * we start the scan from the chain of the connected player with the * closest db# to the target player. This scales up very well. ! *-------------------------------------------------------------------- ! * \endverbatim ! */ #include "config.h" #include "copyrite.h" *************** *** 85,90 **** --- 93,100 ---- #ifdef USE_MAILER extern jmp_buf db_err; + + /** Evaluate a function, and jump on error */ #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0) extern int do_convtime(char *str, struct tm *ttm); /* funtime.c */ *************** *** 92,101 **** static void do_mail_flags (dbref player, const char *msglist, mail_flag flag, int negate); static char *mail_list_time(const char *the_time, int flag); ! static struct mail *mail_fetch(dbref player, int num); ! static struct mail *real_mail_fetch(dbref player, int num, int folder); ! static struct mail *mailfun_fetch(dbref player, ! int nargs, char *arg1, char *arg2); static void count_mail(dbref player, int folder, int *rcount, int *ucount, int *ccount); static void send_mail(dbref player, --- 102,110 ---- static void do_mail_flags (dbref player, const char *msglist, mail_flag flag, int negate); static char *mail_list_time(const char *the_time, int flag); ! static MAIL *mail_fetch(dbref player, int num); ! static MAIL *real_mail_fetch(dbref player, int num, int folder); ! static MAIL *mailfun_fetch(dbref player, int nargs, char *arg1, char *arg2); static void count_mail(dbref player, int folder, int *rcount, int *ucount, int *ccount); static void send_mail(dbref player, *************** *** 107,138 **** char *message, mail_flag flags, int silent, int nosig); #endif ! static struct mail *find_insertion_point(dbref player); static int get_folder_number(dbref player, char *name); static char *get_folder_name(dbref player, int fld); static int player_folder(dbref player); static int parse_folder(dbref player, char *folder_string); ! static int mail_match ! (dbref player, struct mail *mp, struct mail_selector ms, int num); static int parse_msglist (const char *msglist, struct mail_selector *ms, dbref player); static int parse_message_spec (dbref player, const char *s, int *msglow, int *msghigh, int *folder); ! static char *status_chars(struct mail *mp); ! static char *status_string(struct mail *mp); static int sign(int x); ! static char *get_subject(struct mail *mp); ! struct mail *maildb; ! struct mail *tail_ptr; ! #define HEAD maildb ! #define TAIL tail_ptr #define DASH_LINE \ "-----------------------------------------------------------------------------" ! int mdb_top = 0; /* total number of messages */ /*-------------------------------------------------------------------------* * User mail functions (these are called from game.c) --- 116,149 ---- char *message, mail_flag flags, int silent, int nosig); #endif ! static MAIL *find_insertion_point(dbref player); static int get_folder_number(dbref player, char *name); static char *get_folder_name(dbref player, int fld); static int player_folder(dbref player); static int parse_folder(dbref player, char *folder_string); ! static int mail_match(dbref player, MAIL *mp, struct mail_selector ms, int num); static int parse_msglist (const char *msglist, struct mail_selector *ms, dbref player); static int parse_message_spec (dbref player, const char *s, int *msglow, int *msghigh, int *folder); ! static char *status_chars(MAIL *mp); ! static char *status_string(MAIL *mp); static int sign(int x); ! static char *get_subject(MAIL *mp); ! static char *get_sender(MAIL *mp, int full); ! static int was_sender(dbref player, MAIL *mp); ! MAIL *maildb; /**< The head of the mail list */ ! MAIL *tail_ptr; /**< The end of the mail list */ ! #define HEAD maildb /**< The head of the mail list */ ! #define TAIL tail_ptr /**< The end of the mail list */ + /** A line of...dashes! */ #define DASH_LINE \ "-----------------------------------------------------------------------------" ! int mdb_top = 0; /**< total number of messages in mail db */ /*-------------------------------------------------------------------------* * User mail functions (these are called from game.c) *************** *** 153,160 **** /* Return the subject of a mail message, or (no subject) */ static char * ! get_subject(mp) ! struct mail *mp; { static char sbuf[SUBJECT_LEN + 1]; char *p; --- 164,170 ---- /* Return the subject of a mail message, or (no subject) */ static char * ! get_subject(MAIL *mp) { static char sbuf[SUBJECT_LEN + 1]; char *p; *************** *** 176,188 **** return sbuf; } ! /* Change or rename a folder */ void ! do_mail_change_folder(player, fld, newname) ! dbref player; ! char *fld; ! char *newname; { int pfld; char *p; --- 186,234 ---- return sbuf; } + /* Return the name of the mail sender. */ + static char * + get_sender(MAIL *mp, int full) + { + static char tbuf1[BUFFER_LEN], *bp; + bp = tbuf1; + if (!GoodObject(mp->from)) + safe_str("!Purged!", tbuf1, &bp); + else if (!was_sender(mp->from, mp)) + safe_str("!Purged!", tbuf1, &bp); + else if (IsPlayer(mp->from) || !full) + safe_str(Name(mp->from), tbuf1, &bp); + else + safe_format(tbuf1, &bp, "%s (owner: %s)", Name(mp->from), + Name(Owner(mp->from))); + *bp = '\0'; + return tbuf1; + } + + /* Was this player the sender of this message? */ + static int + was_sender(dbref player, MAIL *mp) + { + /* If the dbrefs don't match, fail. */ + if (mp->from != player) + return 0; + /* If we don't know the creation time of the sender, succeed. */ + if (!mp->from_ctime) + return 1; + /* Succeed if and only if the creation times match. */ + return (mp->from_ctime == CreTime(player)); + } ! /** Change folders or rename a folder. ! * \verbatim ! * This implements @mail/folder ! * \endverbatim ! * \param player the enactor. ! * \param fld string containing folder number or name. ! * \param newname string containing folder name, if renaming. ! */ void ! do_mail_change_folder(dbref player, char *fld, char *newname) { int pfld; char *p; *************** *** 225,235 **** } } ! /* Remove a folder name from MAILFOLDERS */ void ! do_mail_unfolder(player, fld) ! dbref player; ! char *fld; { int pfld; --- 271,285 ---- } } ! /** Remove a folder name. ! * \verbatim ! * This implements @mail/unfolder ! * \endverbatim ! * \param player the enactor. ! * \param fld string containing folder number or name. ! */ void ! do_mail_unfolder(dbref player, char *fld) { int pfld; *************** *** 247,289 **** } void ! do_mail_tag(player, msglist) ! dbref player; ! char *msglist; { do_mail_flags(player, msglist, M_TAG, 0); } void do_mail_clear(dbref player, const char *msglist) { do_mail_flags(player, msglist, M_CLEARED, 0); } void ! do_mail_untag(player, msglist) ! dbref player; ! char *msglist; { do_mail_flags(player, msglist, M_TAG, 1); } void ! do_mail_unclear(player, msglist) ! dbref player; ! char *msglist; { do_mail_flags(player, msglist, M_CLEARED, 1); } static void do_mail_flags(dbref player, const char *msglist, mail_flag flag, int negate) { ! /* Set (negate=0) or clear (negate=1) a flag on a player's messages */ ! ! struct mail *mp; struct mail_selector ms; int j, folder; folder_array i; --- 297,353 ---- } + /** Tag a set of mail messages. + * \param player the enactor. + * \param msglist string specifying messages to tag. + */ void ! do_mail_tag(dbref player, const char *msglist) { do_mail_flags(player, msglist, M_TAG, 0); } + /** Clear a set of mail messages. + * \param player the enactor. + * \param msglist string specifying messages to clear. + */ void do_mail_clear(dbref player, const char *msglist) { do_mail_flags(player, msglist, M_CLEARED, 0); } + /** Untag a set of mail messages. + * \param player the enactor. + * \param msglist string specifying messages to untag. + */ void ! do_mail_untag(dbref player, const char *msglist) { do_mail_flags(player, msglist, M_TAG, 1); } + /** Unclear a set of mail messages. + * \param player the enactor. + * \param msglist string specifying messages to unclear. + */ void ! do_mail_unclear(dbref player, const char *msglist) { do_mail_flags(player, msglist, M_CLEARED, 1); } + /** Set or clear a flag on a set of messages. + * \param player the enactor. + * \param msglist string representing list of messages to operate on. + * \param flag flag to set or clear. + * \param negate if 1, clear the flag; if 0, set the flag. + */ static void do_mail_flags(dbref player, const char *msglist, mail_flag flag, int negate) { ! MAIL *mp; struct mail_selector ms; int j, folder; folder_array i; *************** *** 354,367 **** return; } void ! do_mail_file(player, msglist, folder) ! dbref player; ! char *msglist; ! char *folder; { ! /* Change a message's folder */ ! struct mail *mp; struct mail_selector ms; int j, foldernum, origfold; folder_array i; --- 418,435 ---- return; } + /** File messages into a folder. + * \verbatim + * This implements @mail/file. + * \endverbatim + * \param player the enactor. + * \param msglist list of messages to file. + * \param folder name or number of folder to put messages in. + */ void ! do_mail_file(dbref player, char *msglist, char *folder) { ! MAIL *mp; struct mail_selector ms; int j, foldernum, origfold; folder_array i; *************** *** 384,389 **** --- 452,458 ---- if (mail_match(player, mp, ms, i[Folder(mp)])) { j++; mp->read &= M_FMASK; /* Clear the folder */ + mp->read &= ~M_CLEARED; /* Unclear it if it was marked cleared */ mp->read |= FolderBit(foldernum); if (All(ms)) { if (!notified) { *************** *** 407,421 **** return; } void ! do_mail_read(player, msglist) ! dbref player; ! char *msglist; { ! /* print a mail message(s) */ ! ! struct mail *mp; ! char tbuf1[BUFFER_LEN], *bp; char folderheader[BUFFER_LEN]; struct mail_selector ms; int j, folder; --- 476,491 ---- return; } + /** Read mail messages. + * This displays the contents of a set of mail messages. + * \param player the enactor. + * \param msglist list of messages to read. + */ void ! do_mail_read(dbref player, char *msglist) { ! MAIL *mp; ! char tbuf1[BUFFER_LEN]; char folderheader[BUFFER_LEN]; struct mail_selector ms; int j, folder; *************** *** 443,459 **** } else strcpy(folderheader, T("Folder:")); notify(player, DASH_LINE); ! bp = tbuf1; ! if (IsPlayer(mp->from)) ! safe_str(Name(mp->from), tbuf1, &bp); ! else ! safe_format(tbuf1, &bp, "%s (owner: %s)", Name(mp->from), ! Name(Owner(mp->from))); ! *bp = '\0'; notify_format(player, T ("From: %-55s %s\nDate: %-25s %s %2d Message: %d\nStatus: %s"), ! tbuf1, (IsPlayer(mp->from) && Connected(mp->from) && (!hidden(mp->from) || Priv_Who(player))) ? " (Conn)" : " ", uncompress(mp->time), folderheader, Folder(mp), --- 513,524 ---- } else strcpy(folderheader, T("Folder:")); notify(player, DASH_LINE); ! strcpy(tbuf1, get_sender(mp, 1)); notify_format(player, T ("From: %-55s %s\nDate: %-25s %s %2d Message: %d\nStatus: %s"), ! tbuf1, ((*tbuf1 != '!') && IsPlayer(mp->from) ! && Connected(mp->from) && (!hidden(mp->from) || Priv_Who(player))) ? " (Conn)" : " ", uncompress(mp->time), folderheader, Folder(mp), *************** *** 482,495 **** } void ! do_mail_list(player, msglist) ! dbref player; ! const char *msglist; { - /* print a mail message(s) */ char subj[30]; ! struct mail *mp; struct mail_selector ms; int j, folder; folder_array i; --- 547,563 ---- } + /** List the flags, number, sender, subject, and date of messages in a + * concise format. + * \param player the enactor. + * \param msglist list of messages to list. + */ void ! do_mail_list(dbref player, const char *msglist) { char subj[30]; ! char sender[30]; ! MAIL *mp; struct mail_selector ms; int j, folder; folder_array i; *************** *** 519,531 **** TAG_START, Folder(mp), i[Folder(mp)], i[Folder(mp)], Folder(mp), TAG_END)); strcpy(subj, chopstr(get_subject(mp), 28)); notify_format(player, "[%s] %2d:%-3d %c%-12s %-*s %s", status_chars(mp), Folder(mp), i[Folder(mp)], ! ((Connected(mp->from) && ! (!hidden(mp->from) || Priv_Who(player))) ! ? '*' : ' '), ! chopstr(Name(mp->from), 12), ! 30, subj, mail_list_time(uncompress(mp->time), 1)); if (SUPPORT_PUEBLO) notify_noenter(player, tprintf("%c/A%c", TAG_START, TAG_END)); } --- 587,600 ---- TAG_START, Folder(mp), i[Folder(mp)], i[Folder(mp)], Folder(mp), TAG_END)); strcpy(subj, chopstr(get_subject(mp), 28)); + strcpy(sender, chopstr(get_sender(mp, 0), 12)); notify_format(player, "[%s] %2d:%-3d %c%-12s %-*s %s", status_chars(mp), Folder(mp), i[Folder(mp)], ! ((*sender != '!') && (Connected(mp->from) && ! (!hidden(mp->from) ! || Priv_Who(player))) ! ? '*' : ' '), sender, 30, subj, ! mail_list_time(uncompress(mp->time), 1)); if (SUPPORT_PUEBLO) notify_noenter(player, tprintf("%c/A%c", TAG_START, TAG_END)); } *************** *** 569,579 **** } void ! do_mail_purge(player) ! dbref player; { ! struct mail *mp, *nextp; /* Go through player's mail, and remove anything marked cleared */ for (mp = find_exact_starting_point(player); --- 638,653 ---- } + /** Expunge mail that's marked for deletion. + * \verbatim + * This implements @mail/purge. + * \endverbatim + * \param player the enactor. + */ void ! do_mail_purge(dbref player) { ! MAIL *mp, *nextp; /* Go through player's mail, and remove anything marked cleared */ for (mp = find_exact_starting_point(player); *************** *** 612,630 **** return; } void ! do_mail_fwd(player, msglist, tolist) ! dbref player; ! char *msglist; ! char *tolist; { ! struct mail *mp; ! struct mail *last; struct mail_selector ms; int j, num, folder; folder_array i; const char *head; ! struct mail *temp; dbref target; int num_recpts = 0; const char **start; --- 686,709 ---- return; } + /** Forward mail messages to someone(s) else. + * \verbatim + * This implements @mail/forward. + * \endverbatim + * \param player the enactor. + * \param msglist list of messages to forward. + * \param tolist list of recipients to forwared to. + */ void ! do_mail_fwd(dbref player, char *msglist, char *tolist) { ! MAIL *mp; ! MAIL *last; struct mail_selector ms; int j, num, folder; folder_array i; const char *head; ! MAIL *temp; dbref target; int num_recpts = 0; const char **start; *************** *** 697,710 **** notify_format(player, T("MAIL: %d messages forwarded."), num_recpts); } void ! do_mail_send(player, tolist, message, flags, silent, nosig) ! dbref player; ! char *tolist; ! char *message; ! mail_flag flags; ! int silent; ! int nosig; { const char *head; int num; --- 776,792 ---- notify_format(player, T("MAIL: %d messages forwarded."), num_recpts); } + /** Send a mail message. + * \param player the enactor. + * \param tolist list of recipients. + * \param message message text. + * \param flags flags to apply to the message. + * \param silent if 1, don't notify sender for each message sent. + * \param nosig if 1, don't apply sender's MAILSIGNATURE. + */ void ! do_mail_send(dbref player, char *tolist, char *message, mail_flag flags, ! int silent, int nosig) { const char *head; int num; *************** *** 748,754 **** /* Now locate a target */ if (is_strict_integer(current)) { /* reply to a mail message */ ! struct mail *temp; num = parse_integer(current); --- 830,836 ---- /* Now locate a target */ if (is_strict_integer(current)) { /* reply to a mail message */ ! MAIL *temp; num = parse_integer(current); *************** *** 794,815 **** /*-------------------------------------------------------------------------* * Basic mail functions *-------------------------------------------------------------------------*/ ! static struct mail * ! mail_fetch(player, num) ! dbref player; ! int num; { /* get an arbitrary mail message in the current folder */ return real_mail_fetch(player, num, player_folder(player)); } ! static struct mail * ! real_mail_fetch(player, num, folder) ! dbref player; ! int num; ! int folder; { ! struct mail *mp; int i = 0; for (mp = find_exact_starting_point(player); mp != NULL; mp = mp->next) { --- 876,892 ---- /*-------------------------------------------------------------------------* * Basic mail functions *-------------------------------------------------------------------------*/ ! static MAIL * ! mail_fetch(dbref player, int num) { /* get an arbitrary mail message in the current folder */ return real_mail_fetch(player, num, player_folder(player)); } ! static MAIL * ! real_mail_fetch(dbref player, int num, int folder) { ! MAIL *mp; int i = 0; for (mp = find_exact_starting_point(player); mp != NULL; mp = mp->next) { *************** *** 825,841 **** static void ! count_mail(player, folder, rcount, ucount, ccount) ! dbref player; ! int folder; ! int *rcount; ! int *ucount; ! int *ccount; { /* returns count of read, unread, & cleared messages as rcount, ucount, * ccount. folder=-1 returns for all folders */ ! struct mail *mp; int rc, uc, cc; cc = rc = uc = 0; --- 902,913 ---- static void ! count_mail(dbref player, int folder, int *rcount, int *ucount, int *ccount) { /* returns count of read, unread, & cleared messages as rcount, ucount, * ccount. folder=-1 returns for all folders */ ! MAIL *mp; int rc, uc, cc; cc = rc = uc = 0; *************** *** 857,874 **** static void ! send_mail(player, target, subject, message, flags, silent, nosig) ! dbref player; ! dbref target; ! char *subject; ! char *message; ! mail_flag flags; ! int silent; ! int nosig; { /* send a mail message */ ! struct mail *newp, *mp; char tbuf1[30]; int rc, uc, cc; char *newmsg, *nm, *buff, *bp; --- 929,940 ---- static void ! send_mail(dbref player, dbref target, char *subject, char *message, ! mail_flag flags, int silent, int nosig) { /* send a mail message */ ! MAIL *newp, *mp; char tbuf1[30]; int rc, uc, cc; char *newmsg, *nm, *buff, *bp; *************** *** 900,908 **** } /* initialize the appropriate fields */ ! newp = (struct mail *) mush_malloc(sizeof(struct mail), "mail"); newp->to = target; newp->from = player; /* Deal with the subject */ if (subject) strcpy(sbuf, subject); --- 966,975 ---- } /* initialize the appropriate fields */ ! newp = (MAIL *) mush_malloc(sizeof(struct mail), "mail"); newp->to = target; newp->from = player; + newp->from_ctime = CreTime(player); /* Deal with the subject */ if (subject) strcpy(sbuf, subject); *************** *** 931,937 **** buff = (char *) mush_malloc(BUFFER_LEN, "string"); if (!buff) panic(T("Failed to allocate string in send_mail")); ! ms = mailsig = safe_uncompress(AL_STR(a)); bp = buff; process_expression(buff, &bp, &ms, player, player, player, PE_DEFAULT, PT_DEFAULT, NULL); --- 998,1004 ---- buff = (char *) mush_malloc(BUFFER_LEN, "string"); if (!buff) panic(T("Failed to allocate string in send_mail")); ! ms = mailsig = safe_atr_value(a); bp = buff; process_expression(buff, &bp, &ms, player, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *************** *** 1004,1016 **** } void ! do_mail_nuke(player) ! dbref player; { ! /* wipes the entire maildb */ ! ! struct mail *mp, *nextp; if (!God(player)) { notify(player, T("The postal service issues a warrant for your arrest.")); --- 1071,1083 ---- } + /** Wipe the entire maildb. + * \param player the enactor. + */ void ! do_mail_nuke(dbref player) { ! MAIL *mp, *nextp; if (!God(player)) { notify(player, T("The postal service issues a warrant for your arrest.")); *************** *** 1028,1034 **** HEAD = TAIL = NULL; mdb_top = 0; - desc_mail_clear(); do_log(LT_ERR, 0, 0, T("** MAIL PURGE ** done by %s(#%d)."), --- 1095,1100 ---- *************** *** 1036,1048 **** notify(player, T("You annihilate the post office. All messages cleared.")); } void do_mail_debug(dbref player, const char *action, const char *victim) { - /* how to fix mail with a sledgehammer */ - dbref target; ! struct mail *mp, *nextp; int i; if (!Wizard(player)) { --- 1102,1118 ---- notify(player, T("You annihilate the post office. All messages cleared.")); } + /** Low-level mail sanity checking or debugging. + * "how to fix mail with a sledgehammer". + * \param player the enactor. + * \param action action to take (sanity, clear, fix) + * \param victim name of player whose mail is to be checked (NULL to check all) + */ void do_mail_debug(dbref player, const char *action, const char *victim) { dbref target; ! MAIL *mp, *nextp; int i; if (!Wizard(player)) { *************** *** 1120,1137 **** } } void ! do_mail_stats(player, name, full) ! dbref player; ! char *name; ! int full; { - /* mail database statistics */ - dbref target; int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars; char last[50]; ! struct mail *mp; fc = fr = fu = tc = tr = tu = cchars = fchars = tchars = 0; --- 1190,1211 ---- } } + /** Display mail database statistics. + * \verbatim + * This implements @mail/stat, @mail/dstat, @mail/fstat. + * \endverbatim + * \param player the enactor. + * \param name name of player whose mail stats are to be checked (NULL for all) + * \param full level of verbosity: 0 = just count messages, 1 = break down by + * read/unread, 2 = include space usage. + */ void ! do_mail_stats(dbref player, char *name, int full) { dbref target; int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars; char last[50]; ! MAIL *mp; fc = fr = fu = tc = tr = tu = cchars = fchars = tchars = 0; *************** *** 1222,1228 **** if (full == 0) { /* just count number of messages */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (mp->from == target) fr++; if (mp->to == target) tr++; --- 1296,1302 ---- if (full == 0) { /* just count number of messages */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (was_sender(target, mp)) fr++; if (mp->to == target) tr++; *************** *** 1233,1239 **** } /* more detailed message count */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (mp->from == target) { if (Cleared(mp)) fc++; else if (Read(mp)) --- 1307,1313 ---- } /* more detailed message count */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (was_sender(target, mp)) { if (Cleared(mp)) fc++; else if (Read(mp)) *************** *** 1281,1295 **** } ! /*-------------------------------------------------------------------------* ! * Main mail routine for @mail w/o a switch ! *-------------------------------------------------------------------------*/ ! void ! do_mail(player, arg1, arg2) ! dbref player; ! char *arg1; ! char *arg2; { dbref sender; /* Force player to be a real player, but keep track of the --- 1355,1370 ---- } ! /** Main mail wrapper. ! * \verbatim ! * This implements the @mail command when called with no switches. ! * \endverbatim ! * \param player the enactor. ! * \param arg1 left-hand argument (before the =) ! * \param arg2 right-hand argument (after the =) ! */ void ! do_mail(dbref player, char *arg1, char *arg2) { dbref sender; /* Force player to be a real player, but keep track of the *************** *** 1360,1366 **** /* handle the case of wanting to count the number of messages */ if ((player = noisy_match_result(executor, args[0], TYPE_PLAYER, ! MAT_OBJECTS)) == NOTHING) { safe_str(T("#-1 NO SUCH PLAYER"), buff, bp); return; } else if (!controls(executor, player)) { --- 1435,1441 ---- /* handle the case of wanting to count the number of messages */ if ((player = noisy_match_result(executor, args[0], TYPE_PLAYER, ! MAT_ME | MAT_ABSOLUTE | MAT_PLAYER)) == NOTHING) { safe_str(T("#-1 NO SUCH PLAYER"), buff, bp); return; } else if (!controls(executor, player)) { *************** *** 1376,1382 **** case 2: if ((player = noisy_match_result(executor, args[0], TYPE_PLAYER, ! MAT_OBJECTS)) == NOTHING) { safe_str(T("#-1 NO SUCH PLAYER"), buff, bp); return; } else if (!controls(executor, player)) { --- 1451,1457 ---- case 2: if ((player = noisy_match_result(executor, args[0], TYPE_PLAYER, ! MAT_ME | MAT_ABSOLUTE | MAT_PLAYER)) == NOTHING) { safe_str(T("#-1 NO SUCH PLAYER"), buff, bp); return; } else if (!controls(executor, player)) { *************** *** 1410,1416 **** * mail() --> return total # of messages for player */ ! struct mail *mp; dbref player; int rc, uc, cc; --- 1485,1491 ---- * mail() --> return total # of messages for player */ ! MAIL *mp; dbref player; int rc, uc, cc; *************** *** 1421,1427 **** } /* Try mail() */ if (nargs == 1) { ! player = match_result(executor, args[0], TYPE_PLAYER, MAT_OBJECTS); if (GoodObject(player)) { if (!controls(executor, player)) { safe_str(T(e_perm), buff, bp); --- 1496,1504 ---- } /* Try mail() */ if (nargs == 1) { ! player = ! match_result(executor, args[0], TYPE_PLAYER, ! MAT_ME | MAT_ABSOLUTE | MAT_PLAYER); if (GoodObject(player)) { if (!controls(executor, player)) { safe_str(T(e_perm), buff, bp); *************** *** 1451,1462 **** * func([,] [:]) * and return the matching message or NULL */ ! static struct mail * ! mailfun_fetch(player, nargs, arg1, arg2) ! dbref player; ! int nargs; ! char *arg1; ! char *arg2; { dbref target; int msg; --- 1528,1535 ---- * func([,] [:]) * and return the matching message or NULL */ ! static MAIL * ! mailfun_fetch(dbref player, int nargs, char *arg1, char *arg2) { dbref target; int msg; *************** *** 1473,1479 **** /* Both a target and a message */ if ((target = noisy_match_result(player, arg1, TYPE_PLAYER, ! MAT_OBJECTS)) == NOTHING) { return NULL; } else if (!controls(player, target)) { notify(player, T("Permission denied")); --- 1546,1552 ---- /* Both a target and a message */ if ((target = noisy_match_result(player, arg1, TYPE_PLAYER, ! MAT_ME | MAT_ABSOLUTE | MAT_PLAYER)) == NOTHING) { return NULL; } else if (!controls(player, target)) { notify(player, T("Permission denied")); *************** *** 1494,1504 **** /* ARGSUSED */ FUNCTION(fun_mailfrom) { ! struct mail *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) safe_str("#-1", buff, bp); else safe_dbref(mp->from, buff, bp); return; --- 1567,1579 ---- /* ARGSUSED */ FUNCTION(fun_mailfrom) { ! MAIL *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) safe_str("#-1", buff, bp); + else if (!was_sender(mp->from, mp)) + safe_str("#-1", buff, bp); else safe_dbref(mp->from, buff, bp); return; *************** *** 1522,1528 **** dbref target; int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars; char last[50]; ! struct mail *mp; int full; /* Figure out how we were called */ --- 1597,1603 ---- dbref target; int fc, fr, fu, tc, tr, tu, fchars, tchars, cchars; char last[50]; ! MAIL *mp; int full; /* Figure out how we were called */ *************** *** 1623,1629 **** if (full == 0) { /* just count number of messages */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (mp->from == target) fr++; if (mp->to == target) tr++; --- 1698,1704 ---- if (full == 0) { /* just count number of messages */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (was_sender(target, mp)) fr++; if (mp->to == target) tr++; *************** *** 1636,1642 **** } /* more detailed message count */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (mp->from == target) { if (Cleared(mp)) fc++; else if (Read(mp)) --- 1711,1717 ---- } /* more detailed message count */ for (mp = HEAD; mp != NULL; mp = mp->next) { ! if (was_sender(target, mp)) { if (Cleared(mp)) fc++; else if (Read(mp)) *************** *** 1681,1687 **** /* ARGSUSED */ FUNCTION(fun_mailtime) { ! struct mail *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) --- 1756,1762 ---- /* ARGSUSED */ FUNCTION(fun_mailtime) { ! MAIL *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) *************** *** 1694,1700 **** /* ARGSUSED */ FUNCTION(fun_mailstatus) { ! struct mail *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) --- 1769,1775 ---- /* ARGSUSED */ FUNCTION(fun_mailstatus) { ! MAIL *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) *************** *** 1708,1714 **** /* ARGSUSED */ FUNCTION(fun_mailsubject) { ! struct mail *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) --- 1783,1789 ---- /* ARGSUSED */ FUNCTION(fun_mailsubject) { ! MAIL *mp; mp = mailfun_fetch(executor, nargs, args[0], args[1]); if (!mp) *************** *** 1718,1728 **** return; } int ! dump_mail(fp) ! FILE *fp; { ! struct mail *mp; int count = 0; int mail_flags = 0; --- 1793,1806 ---- return; } + /** Save mail to disk. + * \param fp pointer to filehandle to save mail to. + * \return number of mail messages saved. + */ int ! dump_mail(FILE * fp) { ! MAIL *mp; int count = 0; int mail_flags = 0; *************** *** 1731,1736 **** --- 1809,1815 ---- mail_flags += MDBF_ALIASES; #endif mail_flags += MDBF_NEW_EOD; + mail_flags += MDBF_SENDERCTIME; if (mail_flags) fprintf(fp, "+%d\n", mail_flags); *************** *** 1744,1749 **** --- 1823,1829 ---- for (mp = HEAD; mp != NULL; mp = mp->next) { putref(fp, mp->to); putref(fp, mp->from); + putref(fp, mp->from_ctime); putstring(fp, uncompress(mp->time)); if (mp->subject) putstring(fp, uncompress(mp->subject)); *************** *** 1760,1779 **** if (count != mdb_top) { do_log(LT_ERR, 0, 0, T("MAIL: Count of messages is %d, mdb_top is %d."), count, mdb_top); ! /* mdb_top = count; */ ! /* Removed since it won't make a difference unless the process isn't forked */ } return (count); } ! /* Find the first message in a player's mail chain, or NULL if none */ ! struct mail * ! find_exact_starting_point(player) ! dbref player; { ! static struct mail *mp; if (!HEAD) return NULL; --- 1840,1863 ---- if (count != mdb_top) { do_log(LT_ERR, 0, 0, T("MAIL: Count of messages is %d, mdb_top is %d."), count, mdb_top); ! mdb_top = count; /* Doesn't help if we forked, but oh well */ } return (count); } ! /** Find the first message in a player's mail chain, or NULL if none. ! * We first try to find a good place to start by looking for a connected ! * player with a dbref nearest to our target player. Then it's a linear ! * search. ! * \param player the player to search for. ! * \return pointer to first message in their mail chain, or NULL. ! */ ! MAIL * ! find_exact_starting_point(dbref player) { ! static MAIL *mp; if (!HEAD) return NULL; *************** *** 1804,1814 **** * 2. The last message before where the player's chain should start, or * 3. NULL (meaning TAIL) */ ! static struct mail * ! find_insertion_point(player) ! dbref player; { ! static struct mail *mp; if (!HEAD) return NULL; --- 1888,1897 ---- * 2. The last message before where the player's chain should start, or * 3. NULL (meaning TAIL) */ ! static MAIL * ! find_insertion_point(dbref player) { ! static MAIL *mp; if (!HEAD) return NULL; *************** *** 1832,1856 **** } ! void ! mail_init() { mdb_top = 0; HEAD = NULL; TAIL = NULL; } int ! load_mail(fp) ! FILE *fp; { char nbuf1[8]; unsigned char *tbuf = NULL; int mail_top = 0; int mail_flags = 0; int i = 0; ! struct mail *mp, *tmpmp; int done = 0; char sbuf[BUFFER_LEN]; --- 1915,1941 ---- } ! /** Initialize the mail database pointers */ void ! mail_init(void) { mdb_top = 0; HEAD = NULL; TAIL = NULL; } + /** Load mail from disk. + * \param fp pointer to filehandle from which to load mail. + */ int ! load_mail(FILE * fp) { char nbuf1[8]; unsigned char *tbuf = NULL; int mail_top = 0; int mail_flags = 0; int i = 0; ! MAIL *mp, *tmpmp; int done = 0; char sbuf[BUFFER_LEN]; *************** *** 1885,1893 **** return 0; } /* first one is a special case */ ! mp = (struct mail *) mush_malloc(sizeof(struct mail), "mail"); mp->to = getref(fp); mp->from = getref(fp); mp->time = compress(getstring_noalloc(fp)); if (mail_flags & MDBF_SUBJECT) { tbuf = compress(getstring_noalloc(fp)); --- 1970,1982 ---- return 0; } /* first one is a special case */ ! mp = (MAIL *) mush_malloc(sizeof(struct mail), "mail"); mp->to = getref(fp); mp->from = getref(fp); + if (mail_flags & MDBF_SENDERCTIME) + mp->from_ctime = getref(fp); + else + mp->from_ctime = 0; /* No one will have this creation time */ mp->time = compress(getstring_noalloc(fp)); if (mail_flags & MDBF_SUBJECT) { tbuf = compress(getstring_noalloc(fp)); *************** *** 1908,1916 **** /* now loop through */ for (; i < mail_top; i++) { ! mp = (struct mail *) mush_malloc(sizeof(struct mail), "mail"); mp->to = getref(fp); mp->from = getref(fp); mp->time = compress(getstring_noalloc(fp)); if (mail_flags & MDBF_SUBJECT) tbuf = compress(getstring_noalloc(fp)); --- 1997,2009 ---- /* now loop through */ for (; i < mail_top; i++) { ! mp = (MAIL *) mush_malloc(sizeof(struct mail), "mail"); mp->to = getref(fp); mp->from = getref(fp); + if (mail_flags & MDBF_SENDERCTIME) + mp->from_ctime = getref(fp); + else + mp->from_ctime = 0; /* No one will have this creation time */ mp->time = compress(getstring_noalloc(fp)); if (mail_flags & MDBF_SUBJECT) tbuf = compress(getstring_noalloc(fp)); *************** *** 1981,1989 **** } static int ! get_folder_number(player, name) ! dbref player; ! char *name; { ATTR *a; char str[BUFFER_LEN], pat[BUFFER_LEN], *res, *p; --- 2074,2080 ---- } static int ! get_folder_number(dbref player, char *name) { ATTR *a; char str[BUFFER_LEN], pat[BUFFER_LEN], *res, *p; *************** *** 1991,1997 **** a = (ATTR *) atr_get_noparent(player, "MAILFOLDERS"); if (!a) return -1; ! strcpy(str, uncompress(a->value)); sprintf(pat, ":%s:", strupper(name)); res = strstr(str, pat); if (!res) --- 2082,2088 ---- a = (ATTR *) atr_get_noparent(player, "MAILFOLDERS"); if (!a) return -1; ! strcpy(str, atr_value(a)); sprintf(pat, ":%s:", strupper(name)); res = strstr(str, pat); if (!res) *************** *** 2005,2013 **** } static char * ! get_folder_name(player, fld) ! dbref player; ! int fld; { static char str[BUFFER_LEN]; char pat[BUFFER_LEN]; --- 2096,2102 ---- } static char * ! get_folder_name(dbref player, int fld) { static char str[BUFFER_LEN]; char pat[BUFFER_LEN]; *************** *** 2023,2029 **** strcpy(str, "unnamed"); return str; } ! strcpy(str, uncompress(a->value)); old = (char *) string_match(str, pat); if (old) { r = old + strlen(pat); --- 2112,2118 ---- strcpy(str, "unnamed"); return str; } ! strcpy(str, atr_value(a)); old = (char *) string_match(str, pat); if (old) { r = old + strlen(pat); *************** *** 2037,2047 **** } } void ! add_folder_name(player, fld, name) ! dbref player; ! int fld; ! const char *name; { char *old, *res, *r; char *new, *pat, *str, *tbuf; --- 2126,2138 ---- } } + /** Assign a name to a folder. + * \param player dbref of player whose folder is to be named. + * \param fld folder number. + * \param name new name for folder. + */ void ! add_folder_name(dbref player, int fld, const char *name) { char *old, *res, *r; char *new, *pat, *str, *tbuf; *************** *** 2067,2073 **** old = NULL; a = (ATTR *) atr_get_noparent(player, "MAILFOLDERS"); if (a) { ! strcpy(str, uncompress(a->value)); old = (char *) string_match(str, pat); } if (old && *old) { --- 2158,2164 ---- old = NULL; a = (ATTR *) atr_get_noparent(player, "MAILFOLDERS"); if (a) { ! strcpy(str, atr_value(a)); old = (char *) string_match(str, pat); } if (old && *old) { *************** *** 2095,2102 **** } static int ! player_folder(player) ! dbref player; { /* Return the player's current folder number. If they don't have one, set * it to 0 */ --- 2186,2192 ---- } static int ! player_folder(dbref player) { /* Return the player's current folder number. If they don't have one, set * it to 0 */ *************** *** 2107,2119 **** set_player_folder(player, 0); return 0; } ! return atoi(uncompress(a->value)); } void ! set_player_folder(player, fnum) ! dbref player; ! int fnum; { /* Set a player's folder to fnum */ ATTR *a; --- 2197,2211 ---- set_player_folder(player, 0); return 0; } ! return atoi(atr_value(a)); } + /** Set a player's current mail folder to a given folder. + * \param player the player whose current folder is to be set. + * \param fnum folder number to make current. + */ void ! set_player_folder(dbref player, int fnum) { /* Set a player's folder to fnum */ ATTR *a; *************** *** 2129,2137 **** } static int ! parse_folder(player, folder_string) ! dbref player; ! char *folder_string; { int fnum; --- 2221,2227 ---- } static int ! parse_folder(dbref player, char *folder_string) { int fnum; *************** *** 2151,2161 **** } static int ! mail_match(player, mp, ms, num) ! dbref player; ! struct mail *mp; ! struct mail_selector ms; ! int num; { time_t msgtime, diffdays; char msgtimestr[BUFFER_LEN]; --- 2241,2247 ---- } static int ! mail_match(dbref player, MAIL *mp, struct mail_selector ms, int num) { time_t msgtime, diffdays; char msgtimestr[BUFFER_LEN]; *************** *** 2167,2173 **** return 0; if (ms.high && num > ms.high) return 0; ! if (ms.player && mp->from != ms.player) return 0; if (AllInFolder(ms) && (Folder(mp) == player_folder(player))) return 1; --- 2253,2259 ---- return 0; if (ms.high && num > ms.high) return 0; ! if (ms.player && !was_sender(ms.player, mp)) return 0; if (AllInFolder(ms) && (Folder(mp) == player_folder(player))) return 1; *************** *** 2339,2346 **** } static char * ! status_chars(mp) ! struct mail *mp; { /* Return a short description of message flags */ static char res[10]; --- 2425,2431 ---- } static char * ! status_chars(MAIL *mp) { /* Return a short description of message flags */ static char res[10]; *************** *** 2359,2366 **** } static char * ! status_string(mp) ! struct mail *mp; { /* Return a longer description of message flags */ static char tbuf1[BUFFER_LEN]; --- 2444,2450 ---- } static char * ! status_string(MAIL *mp) { /* Return a longer description of message flags */ static char tbuf1[BUFFER_LEN]; *************** *** 2383,2396 **** return tbuf1; } void ! check_mail(player, folder, silent) ! dbref player; ! int folder; ! int silent; { - - /* Check for new @mail */ int rc; /* read messages */ int uc; /* unread messages */ int cc; /* cleared messages */ --- 2467,2480 ---- return tbuf1; } + /** Check for new mail. + * \param player player to check for new mail (and to notify). + * \param folder folder number to check. + * \param silent if 1, don't tell player if they have no mail. + */ void ! check_mail(dbref player, int folder, int silent) { int rc; /* read messages */ int uc; /* unread messages */ int cc; /* cleared messages */ *************** *** 2413,2420 **** } static int ! sign(x) ! int x; { if (x == 0) { return 0; --- 2497,2503 ---- } static int ! sign(int x) { if (x == 0) { return 0; *************** *** 2432,2443 **** * Used in parse_msglist, fun_mail and relatives. */ static int ! parse_message_spec(player, s, msglow, msghigh, folder) ! dbref player; ! const char *s; ! int *msglow; ! int *msghigh; ! int *folder; { char buf[BUFFER_LEN]; char *p, *q; --- 2515,2522 ---- * Used in parse_msglist, fun_mail and relatives. */ static int ! parse_message_spec(dbref player, const char *s, int *msglow, int *msghigh, ! int *folder) { char buf[BUFFER_LEN]; char *p, *q; *************** *** 2525,2538 **** #ifdef MAIL_ALIASES static int ! send_mail_alias(player, aname, subject, message, flags, silent, nosig) ! dbref player; ! char *aname; ! char *subject; ! char *message; ! mail_flag flags; ! int silent; ! int nosig; { struct mail_alias *m; int i; --- 2604,2611 ---- #ifdef MAIL_ALIASES static int ! send_mail_alias(dbref player, char *aname, char *subject, char *message, ! mail_flag flags, int silent, int nosig) { struct mail_alias *m; int i; *** 1_7_7.366/src/extchat.c Thu, 27 Mar 2003 21:05:56 -0600 dunemush (pennmush/c/23_extchat.c 1.1.1.1.1.1.1.1.1.2.1.1.1.3.1.1.1.5.1.1.1.1.1.5.1.2.1.3.1.3.1.1.1.4.1.2.1.6.1.2.1.1.2.4.2.9.1.2.1.2.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.2.1.1.2.1.1.2.1.1.1.1.1.24 660) --- 1_7_7.446(w)/src/extchat.c Thu, 29 May 2003 10:01:27 -0500 dunemush (pennmush/c/23_extchat.c 1.1.1.1.1.1.1.1.1.2.1.1.1.3.1.1.1.5.1.1.1.1.1.5.1.2.1.3.1.3.1.1.1.4.1.2.1.6.1.2.1.1.2.4.2.9.1.2.1.2.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.2.1.1.2.1.1.2.1.1.1.1.1.30 660) *************** *** 36,48 **** #ifdef CHAT_SYSTEM - extern CHAN *channels; - struct na_cpass { - CHANUSER *u; - int checkquiet; - }; - extern jmp_buf db_err; #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0) static CHAN *new_channel(void); --- 36,44 ---- #ifdef CHAT_SYSTEM extern jmp_buf db_err; + + /** Evaluate a function and jump on error. */ #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0) static CHAN *new_channel(void); *************** *** 84,107 **** static void channel_shift_buffer(CHAN *c, int space_needed); static int channel_buffer_lines(CHAN *c); ! const char *chan_speak_lock = "ChanSpeakLock"; ! const char *chan_join_lock = "ChanJoinLock"; ! const char *chan_mod_lock = "ChanModLock"; ! const char *chan_see_lock = "ChanSeeLock"; ! const char *chan_hide_lock = "ChanHideLock"; ! ! #define YES 1 ! #define NO 0 ! #define ERR -1 #define insert_user_by_dbref(who,chan) \ insert_user(new_user(who),chan) #define remove_user_by_dbref(who,chan) \ remove_user(onchannel(who,chan),chan) ! int num_channels; ! CHAN *channels; static PRIV priv_table[] = { {"Disabled", 'D', CHANNEL_DISABLED, CHANNEL_DISABLED}, --- 80,105 ---- static void channel_shift_buffer(CHAN *c, int space_needed); static int channel_buffer_lines(CHAN *c); ! const char *chan_speak_lock = "ChanSpeakLock"; /**< Name of speak lock */ ! const char *chan_join_lock = "ChanJoinLock"; /**< Name of join lock */ ! const char *chan_mod_lock = "ChanModLock"; /**< Name of modify lock */ ! const char *chan_see_lock = "ChanSeeLock"; /**< Name of see lock */ ! const char *chan_hide_lock = "ChanHideLock"; /**< Name of hide lock */ ! ! #define YES 1 /**< An affirmative. */ ! #define NO 0 /**< A negative. */ ! #define ERR -1 /**< An error. Clever, eh? */ + /** Wrapper for insert_user() that generates a new CHANUSER and inserts it */ #define insert_user_by_dbref(who,chan) \ insert_user(new_user(who),chan) + /** Wrapper for remove_user() that searches for the CHANUSER to remove */ #define remove_user_by_dbref(who,chan) \ remove_user(onchannel(who,chan),chan) ! int num_channels; /**< Number of channels defined */ ! CHAN *channels; /**< Pointer to channel list */ static PRIV priv_table[] = { {"Disabled", 'D', CHANNEL_DISABLED, CHANNEL_DISABLED}, *************** *** 1111,1118 **** --- 1109,1118 ---- } /** Parse a chat token command. + * \verbatim * This function hacks up something of the form "+ ", * finding the two args, and passes it to do_chat + * \endverbatim * \param player the enactor. * \param command the command to parse. * \retval 1 chat was successful. *************** *** 1149,1161 **** * \verbatim * This function parses a channel name and then calls do_chat() * to do the actual work of chatting. If it was called through ! * @chat, it fails noisily, but if it was called through +, * it fails silently so that the command can be matched against $commands. * \endverbatim * \param player the enactor. * \param name name of channel to speak on. * \param msg message to send to channel. ! * \param source 0 if from +, 1 if from the chat command. * \retval 1 successful chat. * \retval 0 failed chat. */ --- 1149,1161 ---- * \verbatim * This function parses a channel name and then calls do_chat() * to do the actual work of chatting. If it was called through ! * @chat, it fails noisily, but if it was called through +channel, * it fails silently so that the command can be matched against $commands. * \endverbatim * \param player the enactor. * \param name name of channel to speak on. * \param msg message to send to channel. ! * \param source 0 if from +channel, 1 if from the chat command. * \retval 1 successful chat. * \retval 0 failed chat. */ *************** *** 2059,2067 **** /* The actual chowning of a channel */ static void ! chan_chown(c, victim) ! CHAN *c; ! dbref victim; { giveto(ChanCreator(c), ChanCost(c)); ChanCreator(c) = victim; --- 2059,2065 ---- /* The actual chowning of a channel */ static void ! chan_chown(CHAN *c, dbref victim) { giveto(ChanCreator(c), ChanCost(c)); ChanCreator(c) = victim; *************** *** 2474,2481 **** * \return string listing player's channels, prefixed with Channels: */ const char * ! channel_description(player) ! dbref player; { static char buf[BUFFER_LEN]; CHANLIST *c; --- 2472,2478 ---- * \return string listing player's channels, prefixed with Channels: */ const char * ! channel_description(dbref player) { static char buf[BUFFER_LEN]; CHANLIST *c; *************** *** 2712,2720 **** * \return next dbref to notify. */ dbref ! na_channel(current, data) ! dbref current; ! void *data; { struct na_cpass *nac = data; CHANUSER *u, *nu; --- 2709,2715 ---- * \return next dbref to notify. */ dbref ! na_channel(dbref current, void *data) { struct na_cpass *nac = data; CHANUSER *u, *nu; *************** *** 2800,2806 **** int size, jump; int skipped = 0; ! while (*p && space_needed > 0) { /* First 4 bytes is the size of the first string, not including \0 */ memcpy(&size, p, sizeof(size)); /* Jump to the start of the next string */ --- 2795,2801 ---- int size, jump; int skipped = 0; ! while ((space_needed > 0) && (p < ChanBufferEnd(c))) { /* First 4 bytes is the size of the first string, not including \0 */ memcpy(&size, p, sizeof(size)); /* Jump to the start of the next string */ *************** *** 2810,2821 **** skipped++; } ! if (*p) { ! /* Shift everything here and after up to the front */ ! memmove(ChanBuffer(c), p, ChanBufferEnd(c) - p); ! ChanBufferEnd(c) -= (p - ChanBuffer(c)); ! ChanNumBuffered(c) -= skipped; } } /* Size of buffer in lines */ --- 2805,2828 ---- skipped++; } ! if (space_needed > 0) { ! /* Not good. We couldn't get the space we needed even after we ! * emptied the buffer. This should never happen, but if it does, ! * we'll just log a fault and do nothing. ! */ ! do_rawlog(LT_ERR, "Unable to get enough buffer space on channel %s", ! ChanName(c)); ! return; } + + /* Shift everything here and after up to the front + * At this point, p may be pointing at the very end of the buffer, + * in which case, we just move it to the front with no shifting. + */ + if (p < ChanBufferEnd(c)) + memmove(ChanBuffer(c), p, ChanBufferEnd(c) - p); + ChanBufferEnd(c) -= (p - ChanBuffer(c)); + ChanNumBuffered(c) -= skipped; } /* Size of buffer in lines */ *************** *** 3011,3016 **** --- 3018,3025 ---- if (ChanBuffer(chan)) { /* Resize a buffer */ channel_reallocate_buffer(chan, size); + notify_format(player, T("CHAT: Resizing buffer of channel <%s>"), + ChanName(chan)); } else { /* Start a new buffer */ channel_allocate_buffer(chan, size); *** 1_7_7.366/src/destroy.c Wed, 26 Mar 2003 14:31:41 -0600 dunemush (pennmush/c/24_destroy.c 1.24.2.2.1.3.1.1.1.1.1.1.1.4.1.16 660) --- 1_7_7.446(w)/src/destroy.c Thu, 29 May 2003 10:01:26 -0500 dunemush (pennmush/c/24_destroy.c 1.24.2.2.1.3.1.1.1.1.1.1.1.4.1.1.1.2 660) *************** *** 76,84 **** ! dbref first_free = NOTHING; ! static dbref what_to_destroy(dbref player, char *name, int confirm); static void pre_destroy(dbref player, dbref thing); static void free_object(dbref thing); static void empty_contents(dbref thing); --- 76,84 ---- ! dbref first_free = NOTHING; /**< Object at top of free list */ + static dbref what_to_destroy(dbref player, char *name, int confirm); static void pre_destroy(dbref player, dbref thing); static void free_object(dbref thing); static void empty_contents(dbref thing); *************** *** 103,109 **** --- 103,111 ---- extern struct db_stat_info current_state; + /** Mark an object */ #define SetMarked(x) Type(x) |= TYPE_MARKED + /** Unmark an object */ #define ClearMarked(x) Type(x) &= ~TYPE_MARKED *************** *** 231,237 **** notify(player, T("Permission denied.")); return NOTHING; } ! if (thing == PLAYER_START || thing == MASTER_ROOM || God(thing)) { notify(player, T("That is too special to be destroyed.")); return NOTHING; } --- 233,240 ---- notify(player, T("Permission denied.")); return NOTHING; } ! if (thing == PLAYER_START || thing == MASTER_ROOM || thing == BASE_ROOM || ! thing == DEFAULT_HOME || God(thing)) { notify(player, T("That is too special to be destroyed.")); return NOTHING; } *************** *** 629,635 **** switch (Typeof(i)) { case TYPE_PLAYER: case TYPE_THING: ! Home(i) = PLAYER_START; break; case TYPE_EXIT: /* Huh. An exit that claims to be from here, but wasn't linked --- 632,638 ---- switch (Typeof(i)) { case TYPE_PLAYER: case TYPE_THING: ! Home(i) = DEFAULT_HOME; break; case TYPE_EXIT: /* Huh. An exit that claims to be from here, but wasn't linked *************** *** 695,701 **** /* Make absolutely sure we are removed from Location's content or exit list. If we are in a room we own and destroy_possessions is yes, this can happen, causing much ickyness: All garbage ! items would be in PLAYER_START. */ case TYPE_PLAYER: case TYPE_THING: loc = Location(thing); --- 698,704 ---- /* Make absolutely sure we are removed from Location's content or exit list. If we are in a room we own and destroy_possessions is yes, this can happen, causing much ickyness: All garbage ! items would be in DEFAULT_HOME. */ case TYPE_PLAYER: case TYPE_THING: loc = Location(thing); *************** *** 770,780 **** /* Make sure the home is a reasonable object. */ if (!GoodObject(Home(first)) || IsExit(Home(first)) || Home(first) == thing) ! Home(first) = PLAYER_START; target = Home(first); ! /* If home isn't a good place to send it, send it to PLAYER_START. */ if (!GoodObject(target) || recursive_member(target, first, 0)) ! target = PLAYER_START; if (target != NOTHING) { /* Use enter_room() on everything so that AENTER and such * are all triggered properly. */ --- 773,783 ---- /* Make sure the home is a reasonable object. */ if (!GoodObject(Home(first)) || IsExit(Home(first)) || Home(first) == thing) ! Home(first) = DEFAULT_HOME; target = Home(first); ! /* If home isn't a good place to send it, send it to DEFAULT_HOME. */ if (!GoodObject(target) || recursive_member(target, first, 0)) ! target = DEFAULT_HOME; if (target != NOTHING) { /* Use enter_room() on everything so that AENTER and such * are all triggered properly. */ *************** *** 834,840 **** /* Clear out names from the player list */ delete_player(thing, NULL); if ((atemp = atr_get_noparent(thing, "ALIAS")) != NULL) { ! strcpy(alias, uncompress(atemp->value)); delete_player(thing, alias); } /* Do all the thing-esque manipulations. */ --- 837,843 ---- /* Clear out names from the player list */ delete_player(thing, NULL); if ((atemp = atr_get_noparent(thing, "ALIAS")) != NULL) { ! strcpy(alias, atr_value(atemp)); delete_player(thing, alias); } /* Do all the thing-esque manipulations. */ *************** *** 1048,1054 **** case TYPE_PLAYER: case TYPE_THING: if (!GoodObject(home) || IsGarbage(home) || IsExit(home)) ! Home(thing) = PLAYER_START; if (!GoodObject(loc) || IsGarbage(loc) || IsExit(loc)) enter_room(thing, Home(thing), 0); break; --- 1051,1057 ---- case TYPE_PLAYER: case TYPE_THING: if (!GoodObject(home) || IsGarbage(home) || IsExit(home)) ! Home(thing) = DEFAULT_HOME; if (!GoodObject(loc) || IsGarbage(loc) || IsExit(loc)) enter_room(thing, Home(thing), 0); break; *************** *** 1209,1221 **** } } ! /* An auxiliary function for check_contents. */ ! static void ! mark_contents(dbref thing) ! { ! /* In this next macro, field must be an lvalue whose evaluation has ! * no side effects. All hell will break loose if this is not so. ! */ #define CHECK(field) \ if ((field) != NOTHING) { \ if (!GoodObject(field) || IsGarbage(field)) { \ --- 1212,1221 ---- } } ! /** In this macro, field must be an lvalue whose evaluation has ! * no side effects and results in a dbref to be checked. ! * All hell will break loose if this is not so. ! */ #define CHECK(field) \ if ((field) != NOTHING) { \ if (!GoodObject(field) || IsGarbage(field)) { \ *************** *** 1236,1241 **** --- 1236,1245 ---- } \ } + /* An auxiliary function for check_contents. */ + static void + mark_contents(dbref thing) + { if (!GoodObject(thing) || IsGarbage(thing)) return; *************** *** 1258,1266 **** unparse_object(GOD, thing)); break; } - #undef CHECK } /* Check that for every thing, player, and exit, you can trace exactly one * path to that object from a room by following the exits field of rooms, * the next field of non-rooms, and the contents field of non-exits. --- 1262,1271 ---- unparse_object(GOD, thing)); break; } } + #undef CHECK + /* Check that for every thing, player, and exit, you can trace exactly one * path to that object from a room by following the exits field of rooms, * the next field of non-rooms, and the contents field of non-exits. *************** *** 1282,1288 **** T("You own an object %s that was \'orphaned\'."), object_header(Owner(thing), thing)); /* We try to fix this by trying to send players and things to ! * their current location, to their home, or to PLAYER_START, in * that order, and relinking exits to their source. */ Next(thing) = NOTHING; --- 1287,1293 ---- T("You own an object %s that was \'orphaned\'."), object_header(Owner(thing), thing)); /* We try to fix this by trying to send players and things to ! * their current location, to their home, or to DEFAULT_HOME, in * that order, and relinking exits to their source. */ Next(thing) = NOTHING; *************** *** 1301,1308 **** } else { Contents(Location(thing)) = remove_first(Contents(Location(thing)), thing); ! PUSH(thing, Contents(PLAYER_START)); ! Location(thing) = PLAYER_START; } enter_room(thing, Location(thing), 0); /* If we've managed to reconnect it, then we've reconnected --- 1306,1313 ---- } else { Contents(Location(thing)) = remove_first(Contents(Location(thing)), thing); ! PUSH(thing, Contents(DEFAULT_HOME)); ! Location(thing) = DEFAULT_HOME; } enter_room(thing, Location(thing), 0); /* If we've managed to reconnect it, then we've reconnected *************** *** 1404,1410 **** ClearMarked(thing); else if (Mobile(thing)) { do_rawlog(LT_ERR, T("ERROR DBCK: Moved object %d"), thing); ! moveto(thing, PLAYER_START); } } --- 1409,1415 ---- ClearMarked(thing); else if (Mobile(thing)) { do_rawlog(LT_ERR, T("ERROR DBCK: Moved object %d"), thing); ! moveto(thing, DEFAULT_HOME); } } *** 1_7_7.366/src/db.c Wed, 12 Feb 2003 14:28:44 -0600 dunemush (pennmush/c/25_db.c 1.26.1.1.1.1.1.6.1.10 660) --- 1_7_7.446(w)/src/db.c Thu, 29 May 2003 10:01:26 -0500 dunemush (pennmush/c/25_db.c 1.26.1.1.1.1.1.6.1.15 660) *************** *** 1,4 **** ! /* db.c */ #include "copyrite.h" #include "config.h" --- 1,10 ---- ! /** ! * \file db.c ! * ! * \brief Loading and saving the PennMUSH object database. ! * ! * ! */ #include "copyrite.h" #include "config.h" *************** *** 36,67 **** #endif extern jmp_buf db_err; #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0) #define MAYBE_GET(f,x) \ (indb_flags & (x)) ? getref(f) : 0 extern int paranoid_checkpt; /* from game.c */ ! long indb_flags; /* What flags are in the db we read at startup? */ ! int loading_db = 0; ! struct object *db = 0; ! dbref db_top = 0; ! dbref errobj; const char *EOD = "***END OF DUMP***\n"; #ifndef DB_INITIAL_SIZE ! #define DB_INITIAL_SIZE 5000 #endif /* DB_INITIAL_SIZE */ ! dbref db_size = DB_INITIAL_SIZE; ! HASHTAB htab_objdata; ! HASHTAB htab_objdata_keys; extern char ccom[]; --- 42,77 ---- #endif extern jmp_buf db_err; + + /** Run a function, and jump if error */ #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0) + /** Get a ref out of the database if a given db flag is set */ #define MAYBE_GET(f,x) \ (indb_flags & (x)) ? getref(f) : 0 extern int paranoid_checkpt; /* from game.c */ ! long indb_flags; /**< What flags are in the db we read at startup? */ ! int loading_db = 0; /**< Are we loading the database? */ ! struct object *db = NULL; /**< The object db array */ ! dbref db_top = 0; /**< The number of objects in the db array */ ! dbref errobj; /**< Dbref of object on which an error has occurred */ + /** String that markes the end of dumps */ const char *EOD = "***END OF DUMP***\n"; #ifndef DB_INITIAL_SIZE ! #define DB_INITIAL_SIZE 5000 /**< Initial size for db array */ #endif /* DB_INITIAL_SIZE */ ! dbref db_size = DB_INITIAL_SIZE; /**< Current size of db array */ ! HASHTAB htab_objdata; /**< Object data hash table */ ! HASHTAB htab_objdata_keys; /**< Object data keys hash table */ extern char ccom[]; *************** *** 81,87 **** static void init_objdata_htab(int size); static void db_write_flags(FILE * f); ! StrTree object_names; extern StrTree atr_names; void init_names(void); --- 91,97 ---- static void init_objdata_htab(int size); static void db_write_flags(FILE * f); ! StrTree object_names; /**< String tree of object names */ extern StrTree atr_names; void init_names(void); *************** *** 118,124 **** return Name(obj); } ! int db_init = 0; static void db_grow(dbref newtop) --- 128,134 ---- return Name(obj); } ! int db_init = 0; /**< Has the db array been initialized yet? */ static void db_grow(dbref newtop) *************** *** 584,592 **** for (list = o->list; list; list = AL_NEXT(list)) { if (AL_FLAGS(list) & AF_NODUMP) continue; ! OUTPUT(fprintf(f, "]%s^%d^%d\n", AL_NAME(list), Owner(AL_CREATOR(list)), ! AL_FLAGS(list))); ! putstring(f, uncompress(AL_STR(list))); } OUTPUT(fprintf(f, "<\n")); return 0; --- 594,602 ---- for (list = o->list; list; list = AL_NEXT(list)) { if (AL_FLAGS(list) & AF_NODUMP) continue; ! OUTPUT(fprintf(f, "]%s^%d^%d^%d\n", AL_NAME(list), Owner(AL_CREATOR(list)), ! AL_FLAGS(list), AL_DEREFS(list))); ! putstring(f, atr_value(list)); } OUTPUT(fprintf(f, "<\n")); return 0; *************** *** 735,741 **** OUTPUT(fprintf(f, "]%s^%d^%d\n", name, (int) owner, AL_FLAGS(list))); /* now check the attribute */ ! strcpy(tbuf1, uncompress(AL_STR(list))); /* get rid of unprintables and hard newlines */ lastp = '\0'; for (p = tbuf1; *p; p++) { --- 745,751 ---- OUTPUT(fprintf(f, "]%s^%d^%d\n", name, (int) owner, AL_FLAGS(list))); /* now check the attribute */ ! strcpy(tbuf1, atr_value(list)); /* get rid of unprintables and hard newlines */ lastp = '\0'; for (p = tbuf1; *p; p++) { *************** *** 839,845 **** /** Read in a long int. ! * \param file file pointer to read from. * \return long int read. */ long int --- 849,855 ---- /** Read in a long int. ! * \param f file pointer to read from. * \return long int read. */ long int *************** *** 856,862 **** * written by putstring. The string is read into a static buffer * that is not allocated, so the return value must usually be copied * elsewhere. ! * \param file file pointer to read from. * \return pointer to static buffer containing string read. */ const char * --- 866,872 ---- * written by putstring. The string is read into a static buffer * that is not allocated, so the return value must usually be copied * elsewhere. ! * \param f file pointer to read from. * \return pointer to static buffer containing string read. */ const char * *************** *** 1194,1199 **** --- 1204,1210 ---- char tbuf1[BUFFER_LEN + 150]; int flags; int count = 0; + unsigned char derefs; List(i) = NULL; tbuf1[0] = '\0'; *************** *** 1227,1237 **** flags |= AF_VISUAL; flags &= ~AF_ODARK; } /* We add the attribute assuming that atoi(p) is an ok dbref * since we haven't loaded the whole db and can't really tell * if it is or not. We'll fix this up at the end of the load */ ! atr_new_add(i, tbuf1, getstring_noalloc(f), atoi(p), flags); count++; /* Check removed for atoi(q) == 0 (which results in NOTHING for that * parameter, and thus no flags), since this eliminates 'visual' --- 1238,1255 ---- flags |= AF_VISUAL; flags &= ~AF_ODARK; } + /* Read in the deref count for the attribute, or set it to 0 if not + present. */ + q = strchr(q, '^'); + if (q++) + derefs = atoi(q); + else + derefs = 0; /* We add the attribute assuming that atoi(p) is an ok dbref * since we haven't loaded the whole db and can't really tell * if it is or not. We'll fix this up at the end of the load */ ! atr_new_add(i, tbuf1, getstring_noalloc(f), atoi(p), flags, derefs); count++; /* Check removed for atoi(q) == 0 (which results in NOTHING for that * parameter, and thus no flags), since this eliminates 'visual' *************** *** 1286,1291 **** --- 1304,1313 ---- int minimum_flags = DBF_NEW_STRINGS | DBF_TYPE_GARBAGE | DBF_SPLIT_IMMORTAL | DBF_NO_TEMPLE; + #ifdef MEM_CHECK + log_mem_check(); + #endif + loading_db = 1; clear_players(); *************** *** 1482,1487 **** --- 1504,1512 ---- loading_db = 0; fix_free_list(); dbck(); + #ifdef MEM_CHECK + log_mem_check(); + #endif return db_top; } } *** 1_7_7.366/src/csrimalloc.c Sun, 01 Dec 2002 21:14:41 -0600 dunemush (pennmush/c/26_csrimalloc 1.23.1.4 660) --- 1_7_7.446(w)/src/csrimalloc.c Thu, 29 May 2003 10:01:26 -0500 dunemush (pennmush/c/26_csrimalloc 1.23.1.7 660) *************** *** 947,971 **** #ifdef CSRI_TRACE /* Tracing malloc definitions - helps find leaks */ univptr_t ! __malloc _((size_t nbytes, const char *fname, int linenum)); univptr_t ! __calloc _((size_t nelem, size_t elsize, const char *fname, int linenum)); univptr_t ! __realloc _((univptr_t cp, size_t nbytes, const char *fname, int linenum)); ! univptr_t __valloc _((size_t size, const char *fname, int linenum)); univptr_t ! __memalign _((size_t alignment, size_t size, const char *fname, int linenum)); ! univptr_t __emalloc _((size_t nbytes, const char *fname, int linenum)); univptr_t ! __ecalloc _((size_t nelem, size_t sz, const char *fname, int linenum)); univptr_t ! __erealloc _((univptr_t ptr, size_t nbytes, const char *fname, int linenum)); ! char *__strdup _((const char *s, const char *fname, int linenum)); ! char *__strsave _((const char *s, const char *fname, int linenum)); void ! __free _((univptr_t cp, const char *fname, int linenum)); void ! __cfree _((univptr_t cp, const char *fname, int linenum)); #else /* CSRI_TRACE */ univptr_t malloc _((size_t nbytes)); --- 947,973 ---- #ifdef CSRI_TRACE /* Tracing malloc definitions - helps find leaks */ univptr_t ! trace__malloc _((size_t nbytes, const char *fname, int linenum)); univptr_t ! trace__calloc _((size_t nelem, size_t elsize, const char *fname, int linenum)); univptr_t ! trace__realloc _((univptr_t cp, size_t nbytes, const char *fname, int linenum)); ! univptr_t trace__valloc _((size_t size, const char *fname, int linenum)); univptr_t ! trace__memalign ! _((size_t alignment, size_t size, const char *fname, int linenum)); ! univptr_t trace__emalloc _((size_t nbytes, const char *fname, int linenum)); univptr_t ! trace__ecalloc _((size_t nelem, size_t sz, const char *fname, int linenum)); univptr_t ! trace__erealloc ! _((univptr_t ptr, size_t nbytes, const char *fname, int linenum)); ! char *trace__strdup _((const char *s, const char *fname, int linenum)); ! char *trace__strsave _((const char *s, const char *fname, int linenum)); void ! trace__free _((univptr_t cp, const char *fname, int linenum)); void ! trace__cfree _((univptr_t cp, const char *fname, int linenum)); #else /* CSRI_TRACE */ univptr_t malloc _((size_t nbytes)); *************** *** 1023,1029 **** #ifdef CSRI_TRACE univptr_t ! __emalloc(nbytes, fname, linenum) size_t nbytes; const char *fname; int --- 1025,1031 ---- #ifdef CSRI_TRACE univptr_t ! trace__emalloc(nbytes, fname, linenum) size_t nbytes; const char *fname; int *************** *** 1039,1045 **** univptr_t ! __erealloc(ptr, nbytes, fname, linenum) univptr_t ptr; size_t nbytes; --- 1041,1047 ---- univptr_t ! trace__erealloc(ptr, nbytes, fname, linenum) univptr_t ptr; size_t nbytes; *************** *** 1056,1062 **** } univptr_t ! __ecalloc(nelem, sz, fname, linenum) size_t nelem, sz; const char *fname; int --- 1058,1064 ---- } univptr_t ! trace__ecalloc(nelem, sz, fname, linenum) size_t nelem, sz; const char *fname; int *************** *** 1083,1089 **** /*LINTLIBRARY */ univptr_t ! __malloc(nbytes, fname, linenum) size_t nbytes; const char *fname; int --- 1085,1091 ---- /*LINTLIBRARY */ univptr_t ! trace__malloc(nbytes, fname, linenum) size_t nbytes; const char *fname; int *************** *** 1099,1105 **** /* ARGSUSED if CSRI_TRACE is not defined */ void ! __free(cp, fname, linenum) univptr_t cp; const char *fname; int linenum; --- 1101,1107 ---- /* ARGSUSED if CSRI_TRACE is not defined */ void ! trace__free(cp, fname, linenum) univptr_t cp; const char *fname; int linenum; *************** *** 1110,1116 **** } univptr_t ! __realloc(cp, nbytes, fname, linenum) univptr_t cp; size_t nbytes; --- 1112,1118 ---- } univptr_t ! trace__realloc(cp, nbytes, fname, linenum) univptr_t cp; size_t nbytes; *************** *** 1131,1137 **** } univptr_t ! __calloc(nelem, elsize, fname, linenum) size_t nelem, elsize; const char *fname; int --- 1133,1139 ---- } univptr_t ! trace__calloc(nelem, elsize, fname, linenum) size_t nelem, elsize; const char *fname; int *************** *** 1147,1153 **** /* ARGSUSED if CSRI_TRACE is not defined */ void ! __cfree(cp, fname, linenum) univptr_t cp; const char *fname; int linenum; --- 1149,1155 ---- /* ARGSUSED if CSRI_TRACE is not defined */ void ! trace__cfree(cp, fname, linenum) univptr_t cp; const char *fname; int linenum; *************** *** 1165,1171 **** univptr_t ! __memalign(alignment, size, fname, linenum) size_t alignment, size; const char *fname; int --- 1167,1173 ---- univptr_t ! trace__memalign(alignment, size, fname, linenum) size_t alignment, size; const char *fname; int *************** *** 1180,1186 **** } univptr_t ! __valloc(size, fname, linenum) size_t size; const char *fname; int --- 1182,1188 ---- } univptr_t ! trace__valloc(size, fname, linenum) size_t size; const char *fname; int *************** *** 1199,1205 **** /*LINTLIBRARY */ char * ! __strdup(s, fname, linenum) const char *s; const char *fname; int linenum; --- 1201,1207 ---- /*LINTLIBRARY */ char * ! trace__strdup(s, fname, linenum) const char *s; const char *fname; int linenum; *************** *** 1213,1219 **** } char * ! __strsave(s, fname, linenum) const char *s; const char *fname; int linenum; --- 1215,1221 ---- } char * ! trace__strsave(s, fname, linenum) const char *s; const char *fname; int linenum; *** 1_7_7.366/src/create.c Wed, 05 Mar 2003 16:05:56 -0600 dunemush (pennmush/c/27_create.c 1.27.1.3.1.1.1.1.1.1.1.2.1.1.1.1.1.19 660) --- 1_7_7.446(w)/src/create.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/27_create.c 1.27.1.3.1.1.1.1.1.1.1.2.1.1.1.1.1.22 660) *************** *** 29,35 **** static dbref clone_object(dbref player, dbref thing, const char *newname, int preserve); ! struct db_stat_info current_state; /* utility for open and link */ static dbref --- 29,35 ---- static dbref clone_object(dbref player, dbref thing, const char *newname, int preserve); ! struct db_stat_info current_state; /**< Current stats for database */ /* utility for open and link */ static dbref *************** *** 333,339 **** } else { /* do the link */ Home(thing) = room; /* home */ ! notify(player, T("Home set.")); } break; case TYPE_ROOM: --- 333,340 ---- } else { /* do the link */ Home(thing) = room; /* home */ ! if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) ! notify(player, T("Home set.")); } break; case TYPE_ROOM: *************** *** 574,580 **** current_state.things++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, ! wenv); return clone; } return NOTHING; --- 575,581 ---- current_state.things++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, ! wenv, 0); return clone; } return NOTHING; *************** *** 587,593 **** current_state.rooms++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, ! wenv); return clone; } return NOTHING; --- 588,594 ---- current_state.rooms++; local_data_clone(clone, thing); real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING, ! wenv, 0); return clone; } return NOTHING; *** 1_7_7.366/src/cque.c Mon, 10 Feb 2003 16:52:04 -0600 dunemush (pennmush/c/28_cque.c 1.36.1.5.1.1.1.1.1.1.1.2.1.6.1.4 660) --- 1_7_7.446(w)/src/cque.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/28_cque.c 1.36.1.5.1.1.1.1.1.1.1.2.1.6.1.9 660) *************** *** 1,4 **** ! /* cque.c */ #include "copyrite.h" #include "config.h" --- 1,10 ---- ! /** ! * \file cque.c ! * ! * \brief Queue for PennMUSH. ! * ! * ! */ #include "copyrite.h" #include "config.h" *************** *** 36,53 **** extern char ccom[]; extern dbref cplr; ! char *wenv[10]; /* working environment (wptr/rptr equiv) */ ! char renv[NUMQ][BUFFER_LEN]; ! char *wnxt[10], *rnxt[NUMQ]; /* stuff to be shoved into the queue */ ! int break_called = 0; ! typedef struct bque BQUE; /** A queue entry. * This structure reprsents a queue entry on a linked list of queue * entries (a queue). It is used for all of the queues. */ ! struct bque { ! BQUE *next; /**< pointer to next entry on queue */ dbref player; /**< player who will do command */ dbref queued; /**< object whose QUEUE gets incremented for this command */ dbref cause; /**< player causing command (for %N) */ --- 42,59 ---- extern char ccom[]; extern dbref cplr; ! char *wenv[10]; /**< working environment (%0-%9) */ ! char renv[NUMQ][BUFFER_LEN]; /**< working registers q0-q9,qa-qz */ ! char *wnxt[10]; /**< environment to shove into the queue */ ! char *rnxt[NUMQ]; /**< registers to shove into the queue */ ! int break_called = 0; /**< Has the break command been called? */ /** A queue entry. * This structure reprsents a queue entry on a linked list of queue * entries (a queue). It is used for all of the queues. */ ! typedef struct bque { ! struct bque *next; /**< pointer to next entry on queue */ dbref player; /**< player who will do command */ dbref queued; /**< object whose QUEUE gets incremented for this command */ dbref cause; /**< player causing command (for %N) */ *************** *** 57,63 **** char *env[10]; /**< environment, from wild match */ char *rval[NUMQ]; /**< environment, from setq() */ char *comm; /**< command to be executed */ ! }; static BQUE *qfirst = NULL, *qlast = NULL, *qwait = NULL; static BQUE *qlfirst = NULL, *qllast = NULL; --- 63,69 ---- char *env[10]; /**< environment, from wild match */ char *rval[NUMQ]; /**< environment, from setq() */ char *comm; /**< command to be executed */ ! } BQUE; static BQUE *qfirst = NULL, *qlast = NULL, *qwait = NULL; static BQUE *qlfirst = NULL, *qllast = NULL; *************** *** 79,87 **** static void do_raw_restart(dbref victim); static int waitable_attr(dbref thing, const char *atr); ! extern sig_atomic_t cpu_time_limit_hit; #define SEMAPHORE_FLAGS (AF_LOCKED | AF_PRIVATE | AF_NOCOPY | AF_NODUMP) /* Returns true if the attribute on thing can be used as a semaphore. * atr should be given in UPPERCASE. */ --- 85,98 ---- static void do_raw_restart(dbref victim); static int waitable_attr(dbref thing, const char *atr); ! extern sig_atomic_t cpu_time_limit_hit; /**< Have we used too much CPU? */ + /** Attribute flags to be set or checked on attributes to be used + * as semaphores. + */ #define SEMAPHORE_FLAGS (AF_LOCKED | AF_PRIVATE | AF_NOCOPY | AF_NODUMP) + + /* Returns true if the attribute on thing can be used as a semaphore. * atr should be given in UPPERCASE. */ *************** *** 99,105 **** return !strcmp(AL_NAME(a), "SEMAPHORE"); /* Only allow SEMAPHORE for now */ } else { /* Attribute is set. Check for proper owner and flags and value */ if ((AL_CREATOR(a) == GOD) && (AL_FLAGS(a) == SEMAPHORE_FLAGS)) { ! char *v = uncompress(AL_STR(a)); if (!*v || is_integer(v)) return 1; else --- 110,116 ---- return !strcmp(AL_NAME(a), "SEMAPHORE"); /* Only allow SEMAPHORE for now */ } else { /* Attribute is set. Check for proper owner and flags and value */ if ((AL_CREATOR(a) == GOD) && (AL_FLAGS(a) == SEMAPHORE_FLAGS)) { ! char *v = atr_value(a); if (!*v || is_integer(v)) return 1; else *************** *** 119,125 **** char buff[MAX_COMMAND_LEN]; a = atr_get_noparent(player, name); if (a) ! num = parse_integer(uncompress(AL_STR(a))); num += am; if (num) { sprintf(buff, "%d", num); --- 130,136 ---- char buff[MAX_COMMAND_LEN]; a = atr_get_noparent(player, name); if (a) ! num = parse_integer(atr_value(a)); num += am; if (num) { sprintf(buff, "%d", num); *************** *** 279,285 **** atr_get(executor, strupper(atrname))); if (!a) return 0; ! start = safe_uncompress(AL_STR(a)); command = start; /* Trim off $-command or ^-command prefix */ if (*command == '$' || *command == '^') { --- 290,296 ---- atr_get(executor, strupper(atrname))); if (!a) return 0; ! start = safe_atr_value(a); command = start; /* Trim off $-command or ^-command prefix */ if (*command == '$' || *command == '^') { *************** *** 468,475 **** * \return number of commands executed. */ int ! do_top(ncom) ! int ncom; { int a, i; BQUE *entry; --- 479,485 ---- * \return number of commands executed. */ int ! do_top(int ncom) { int a, i; BQUE *entry; *************** *** 827,833 **** add_to_sem(thing, 1, aname); a = atr_get_noparent(thing, aname); if (a) ! num = parse_integer(uncompress(a->value)); else num = 0; if (num <= 0) { --- 837,843 ---- add_to_sem(thing, 1, aname); a = atr_get_noparent(thing, aname); if (a) ! num = parse_integer(atr_value(a)); else num = 0; if (num <= 0) { *************** *** 839,854 **** } static void ! show_queue(player, victim, q_type, q_quiet, q_all, q_ptr, tot, self, del) ! dbref player; ! dbref victim; ! int q_type; ! int q_quiet; ! int q_all; ! BQUE *q_ptr; ! int *tot; ! int *self; ! int *del; { BQUE *tmp; for (tmp = q_ptr; tmp; tmp = tmp->next) { --- 849,856 ---- } static void ! show_queue(dbref player, dbref victim, int q_type, int q_quiet, int q_all, ! BQUE *q_ptr, int *tot, int *self, int *del) { BQUE *tmp; for (tmp = q_ptr; tmp; tmp = tmp->next) { *** 1_7_7.366/src/conf.c Wed, 26 Mar 2003 11:33:35 -0600 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.3 660) --- 1_7_7.446(w)/src/conf.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.12 660) *************** *** 40,53 **** #include "PMInit.h" #endif ! time_t mudtime; /* game time, in seconds */ ! ! typedef struct confparm CONF; /** Runtime configuration parameter. * This structure represents a runtime configuration option. */ ! struct confparm { const char *name; /**< name of option. */ /** the function handler. */ int (*handler) --- 40,51 ---- #include "PMInit.h" #endif ! time_t mudtime; /**< game time, in seconds */ /** Runtime configuration parameter. * This structure represents a runtime configuration option. */ ! typedef struct confparm { const char *name; /**< name of option. */ /** the function handler. */ int (*handler) *************** *** 56,68 **** int max; /**< max: string length, integer value. */ int overridden; /**< Has the default been overridden? */ const char *group; /**< The option's group name */ ! }; static void show_compile_options(dbref player); static char *config_list_helper(dbref player, CONF *cp, int lc); static char *config_list_helper2(dbref player, CONF *cp, int lc); ! OPTTAB options; int cf_bool(const char *opt, const char *val, void *loc, int maxval, int source); --- 54,66 ---- int max; /**< max: string length, integer value. */ int overridden; /**< Has the default been overridden? */ const char *group; /**< The option's group name */ ! } CONF; static void show_compile_options(dbref player); static char *config_list_helper(dbref player, CONF *cp, int lc); static char *config_list_helper2(dbref player, CONF *cp, int lc); ! OPTTAB options; /**< The table of configuration options */ int cf_bool(const char *opt, const char *val, void *loc, int maxval, int source); *************** *** 72,77 **** --- 70,77 ---- int source); int cf_flag(const char *opt, const char *val, void *loc, int maxval, int source); + int cf_time(const char *opt, const char *val, void *loc, int maxval, + int source); int config_set(const char *opt, char *val, int source, int restrictions); void conf_default_set(void); *************** *** 182,192 **** , {"base_room", cf_dbref, &options.base_room, 100000, 0, "db"} , {"exits_connect_rooms", cf_bool, &options.exits_connect_rooms, 2, 0, "db"} , {"zone_control_zmp_only", cf_bool, &options.zone_control, 2, 0, "db"} , ! {"mud_name", cf_str, options.mud_name, 128, 0, "net"} , {"ip_addr", cf_str, options.ip_addr, 64, 0, "net"} --- 182,201 ---- , {"base_room", cf_dbref, &options.base_room, 100000, 0, "db"} , + {"default_home", cf_dbref, &options.default_home, 100000, 0, "db"} + , {"exits_connect_rooms", cf_bool, &options.exits_connect_rooms, 2, 0, "db"} , {"zone_control_zmp_only", cf_bool, &options.zone_control, 2, 0, "db"} , ! {"ancestor_room", cf_int, &options.ancestor_room, 100000, 0, "db"} ! , ! {"ancestor_exit", cf_int, &options.ancestor_exit, 100000, 0, "db"} ! , ! {"ancestor_thing", cf_int, &options.ancestor_thing, 100000, 0, "db"} ! , ! {"ancestor_player", cf_int, &options.ancestor_player, 100000, 0, "db"} ! , {"mud_name", cf_str, options.mud_name, 128, 0, "net"} , {"ip_addr", cf_str, options.ip_addr, 64, 0, "net"} *************** *** 199,205 **** , {"use_ident", cf_bool, &options.use_ident, 2, 0, "net"} , ! {"ident_timeout", cf_int, &options.ident_timeout, 60, 0, "net"} , {"logins", cf_bool, &options.login_allow, 2, 0, "net"} , --- 208,214 ---- , {"use_ident", cf_bool, &options.use_ident, 2, 0, "net"} , ! {"ident_timeout", cf_time, &options.ident_timeout, 60, 0, "net"} , {"logins", cf_bool, &options.login_allow, 2, 0, "net"} , *************** *** 226,238 **** sizeof options.dump_warning_5min, 0, "dump"} , ! {"dump_interval", cf_int, &options.dump_interval, 100000, 0, "dump"} , ! {"warn_interval", cf_int, &options.warn_interval, 32000, 0, "dump"} , ! {"purge_interval", cf_int, &options.purge_interval, 10000, 0, "dump"} , ! {"dbck_interval", cf_int, &options.dbck_interval, 10000, 0, "dump"} , {"money_singular", cf_str, options.money_singular, --- 235,247 ---- sizeof options.dump_warning_5min, 0, "dump"} , ! {"dump_interval", cf_time, &options.dump_interval, 100000, 0, "dump"} , ! {"warn_interval", cf_time, &options.warn_interval, 32000, 0, "dump"} , ! {"purge_interval", cf_time, &options.purge_interval, 10000, 0, "dump"} , ! {"dbck_interval", cf_time, &options.dbck_interval, 10000, 0, "dump"} , {"money_singular", cf_str, options.money_singular, *************** *** 287,295 **** , {"max_guests", cf_int, &options.max_guests, 128, 0, "limits"} , ! {"idle_timeout", cf_int, &options.idle_timeout, 100000, 0, "limits"} , ! {"unconnected_idle_timeout", cf_int, &options.unconnected_idle_timeout, 100000, 0, "limits"} , {"whisper_loudness", cf_int, &options.whisper_loudness, 100, 0, "limits"} --- 296,304 ---- , {"max_guests", cf_int, &options.max_guests, 128, 0, "limits"} , ! {"idle_timeout", cf_time, &options.idle_timeout, 100000, 0, "limits"} , ! {"unconnected_idle_timeout", cf_time, &options.unconnected_idle_timeout, 100000, 0, "limits"} , {"whisper_loudness", cf_int, &options.whisper_loudness, 100, 0, "limits"} *************** *** 452,472 **** NULL} , {NULL, NULL, NULL, 0, 0, NULL} }; - typedef struct confgroupparm CONFGROUP; /** A runtime configuration group. * This struction represents the name and information about a group * of runtime configuration directives. Groups are used to organize * the display of configuration options. */ ! struct confgroupparm { const char *name; /**< name of group */ const char *desc; /**< description of group */ int viewperms; /**< who can view this group */ ! }; /** The table of all configuration groups. */ CONFGROUP confgroups[] = { --- 461,493 ---- NULL} , + {"chunk_swap_file", cf_str, options.chunk_swap_file, + sizeof options.chunk_swap_file, 0, "files"} + , + {"chunk_cache_memory", cf_int, &options.chunk_cache_memory, + 1000000000, 65510 * 2, "files"} + , + {"chunk_migrate", cf_int, &options.chunk_migrate_inactive, 100000, 0, + "limits"} + , + {"active_chunk_migrate", cf_int, &options.chunk_migrate_active, 100000, 0, + "limits"} + , + {NULL, NULL, NULL, 0, 0, NULL} }; /** A runtime configuration group. * This struction represents the name and information about a group * of runtime configuration directives. Groups are used to organize * the display of configuration options. */ ! typedef struct confgroupparm { const char *name; /**< name of group */ const char *desc; /**< description of group */ int viewperms; /**< who can view this group */ ! } CONFGROUP; /** The table of all configuration groups. */ CONFGROUP confgroups[] = { *************** *** 618,623 **** --- 639,707 ---- } + /** Parse an time configuration option with a default unit of seconds + * \param opt name of the configuration option. + * \param val value of the option. + * \param loc address to store the value. + * \param maxval maximum value. + * \param source 0 if read from config file; 1 if from command. + * \retval 0 failure (unable to parse val). + * \retval 1 success. + */ + int + cf_time(const char *opt, const char *val, void *loc, int maxval, int source) + { + /* enter time parameter */ + char *end = NULL; + int in_minutes = 0; + int n, secs = 0; + + + if (strstr(opt, "idle")) + in_minutes = 1; + + while (val && *val) { + n = strtol(val, &end, 10); + + switch (*end) { + case '\0': + if (in_minutes) + secs += n * 60; + else + secs += n; + goto done; /* Sigh. What I wouldn't give for named loops in C */ + case 's': + case 'S': + secs += n; + break; + case 'm': + case 'M': + secs += n * 60; + break; + case 'h': + case 'H': + secs += n * 3600; + break; + default: + if (source == 0) + do_rawlog(LT_ERR, T("CONFIG: Unknown time interval in option %s"), opt); + return 0; + } + val = end + 1; + } + done: + /* enforce limits */ + if ((maxval >= 0) && (secs > maxval)) { + secs = maxval; + if (source == 0) { + do_rawlog(LT_ERR, T("CONFIG: option %s value limited to %d\n"), opt, + maxval); + } + } + *((int *) loc) = secs; + return 1; + } + /** Parse a flag configuration option. * This is just like parsing a string option, but collects multiple * string options with the same name into a single value. *************** *** 857,864 **** options.player_start = 0; options.master_room = 2; options.base_room = 0; options.idle_timeout = 0; ! options.unconnected_idle_timeout = 5; options.dump_interval = 3601; strcpy(options.dump_message, T("GAME: Dumping database. Game may freeze for a minute")); --- 941,953 ---- options.player_start = 0; options.master_room = 2; options.base_room = 0; + options.default_home = 0; + options.ancestor_room = -1; + options.ancestor_exit = -1; + options.ancestor_thing = -1; + options.ancestor_player = -1; options.idle_timeout = 0; ! options.unconnected_idle_timeout = 300; options.dump_interval = 3601; strcpy(options.dump_message, T("GAME: Dumping database. Game may freeze for a minute")); *************** *** 988,993 **** --- 1077,1086 ---- options.queue_entry_cpu_time = 1500; options.ascii_names = 1; options.call_lim = 10000; + strcpy(options.chunk_swap_file, "data/chunkswap"); + options.chunk_cache_memory = 1000000; + options.chunk_migrate_inactive = 500; + options.chunk_migrate_active = 200; } /* Limit how many files we can nest */ *************** *** 1257,1277 **** notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END)); } #define MAYBE_LC(x) (lc ? strlower(x) : x) static char * config_list_helper(dbref player __attribute__ ((__unused__)), CONF *cp, int lc) { static char result[BUFFER_LEN]; if ((cp->handler == cf_str) || (cp->handler == cf_flag)) ! sprintf(result, " %-40s %s", MAYBE_LC(cp->name), (char *) cp->loc); else if (cp->handler == cf_int) ! sprintf(result, " %-40s %d", MAYBE_LC(cp->name), *((int *) cp->loc)); ! else if (cp->handler == cf_bool) ! sprintf(result, " %-40s %s", MAYBE_LC(cp->name), ! (*((int *) cp->loc) ? "Yes" : "No")); else if (cp->handler == cf_dbref) ! sprintf(result, " %-40s #%d", MAYBE_LC(cp->name), *((dbref *) cp->loc)); return result; } --- 1350,1393 ---- notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END)); } + /** Lowercase a string if we've been asked to */ #define MAYBE_LC(x) (lc ? strlower(x) : x) static char * config_list_helper(dbref player __attribute__ ((__unused__)), CONF *cp, int lc) { static char result[BUFFER_LEN]; + char *bp = result; if ((cp->handler == cf_str) || (cp->handler == cf_flag)) ! safe_format(result, &bp, " %-40s %s", MAYBE_LC(cp->name), (char *) cp->loc); else if (cp->handler == cf_int) ! safe_format(result, &bp, " %-40s %d", MAYBE_LC(cp->name), ! *((int *) cp->loc)); ! else if (cp->handler == cf_time) { ! div_t n; ! int secs = *(int *) cp->loc; ! ! safe_format(result, &bp, " %-40s ", MAYBE_LC(cp->name)); ! ! if (secs >= 3600) { ! n = div(secs, 3600); ! secs = n.rem; ! safe_format(result, &bp, "%dh", n.quot); ! } ! if (secs >= 60) { ! n = div(secs, 60); ! secs = n.rem; ! safe_format(result, &bp, "%dm", n.quot); ! } ! if (secs) ! safe_format(result, &bp, "%ds", secs); ! } else if (cp->handler == cf_bool) ! safe_format(result, &bp, " %-40s %s", MAYBE_LC(cp->name), ! (*((int *) cp->loc) ? "Yes" : "No")); else if (cp->handler == cf_dbref) ! safe_format(result, &bp, " %-40s #%d", MAYBE_LC(cp->name), ! *((dbref *) cp->loc)); ! *bp = '\0'; return result; } *************** *** 1281,1294 **** __attribute__ ((__unused__))) { static char result[BUFFER_LEN]; if ((cp->handler == cf_str) || (cp->handler == cf_flag)) ! sprintf(result, "%s", (char *) cp->loc); else if (cp->handler == cf_int) ! sprintf(result, "%d", *((int *) cp->loc)); ! else if (cp->handler == cf_bool) ! sprintf(result, "%s", (*((int *) cp->loc) ? "Yes" : "No")); else if (cp->handler == cf_dbref) ! sprintf(result, "#%d", *((dbref *) cp->loc)); return result; } --- 1397,1428 ---- __attribute__ ((__unused__))) { static char result[BUFFER_LEN]; + char *bp = result; if ((cp->handler == cf_str) || (cp->handler == cf_flag)) ! safe_format(result, &bp, "%s", (char *) cp->loc); else if (cp->handler == cf_int) ! safe_format(result, &bp, "%d", *((int *) cp->loc)); ! else if (cp->handler == cf_time) { ! div_t n; ! int secs = *(int *) cp->loc; ! ! if (secs >= 3600) { ! n = div(secs, 3600); ! secs = n.rem; ! safe_format(result, &bp, "%dh", n.quot); ! } ! if (secs >= 60) { ! n = div(secs, 60); ! secs = n.rem; ! safe_format(result, &bp, "%dm", n.quot); ! } ! if (secs) ! safe_format(result, &bp, "%ds", secs); ! } else if (cp->handler == cf_bool) ! safe_format(result, &bp, "%s", (*((int *) cp->loc) ? "Yes" : "No")); else if (cp->handler == cf_dbref) ! safe_format(result, &bp, "#%d", *((dbref *) cp->loc)); ! *bp = '\0'; return result; } *************** *** 1457,1473 **** #ifdef EMPTY_ATTRS notify(player, T(" Empty attributes are kept.")); - #ifdef VISIBLE_EMPTY_ATTRS notify(player, T(" Empty attributes are visible.")); - #else - notify(player, T(" Empty attributes are not visible.")); - #endif - #ifdef DELETE_ATTRS notify(player, T(" Attributes can be deleted.")); #else - notify(player, T(" Attributes cannot be deleted.")); - #endif - #else notify(player, T(" Empty attributes are deleted.")); #endif --- 1591,1599 ---- *** 1_7_7.366/src/compress.c Sat, 09 Nov 2002 19:47:06 -0600 dunemush (pennmush/c/32_compress.c 1.11 660) --- 1_7_7.446(w)/src/compress.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/32_compress.c 1.13 660) *************** *** 1,3 **** --- 1,13 ---- + /** + * \file compress.c + * + * \brief Compression routine wrapper file for PennMUSH. + * + * This file does nothing but conditionally include the appropriate + * attribute compression source code. + * + */ + #include "config.h" #include "options.h" *************** *** 7,13 **** #if defined(COMPRESSION_TYPE) && (COMPRESSION_TYPE == 0) /* No compression */ ! char ucbuff[BUFFER_LEN]; #elif (COMPRESSION_TYPE == 1) || (COMPRESSION_TYPE == 2) /* Huffman compression */ --- 17,23 ---- #if defined(COMPRESSION_TYPE) && (COMPRESSION_TYPE == 0) /* No compression */ ! char ucbuff[BUFFER_LEN]; /**< Dummy buffer for no compression */ #elif (COMPRESSION_TYPE == 1) || (COMPRESSION_TYPE == 2) /* Huffman compression */ *** 1_7_7.366/src/comp_w.c Mon, 10 Feb 2003 16:48:37 -0600 dunemush (pennmush/c/33_comp_w.c 1.16 660) --- 1_7_7.446(w)/src/comp_w.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/33_comp_w.c 1.17 660) *************** *** 127,140 **** #include "mymalloc.h" #include "confmagic.h" ! #define MAXTABLE 32768 ! #define MAXWORDS 100 ! #define COLLISION_LIMIT 20 ! #define COMPRESS_HASH_MASK 0x7FFF /* 32767 */ ! #define TABLE_FLAG 0x80 ! #define TABLE_MASK 0x7F /* Table of words */ --- 127,140 ---- #include "mymalloc.h" #include "confmagic.h" ! #define MAXTABLE 32768 /**< Maximum words in the table */ ! #define MAXWORDS 100 /**< Maximum size of a word */ ! #define COLLISION_LIMIT 20 /**< Maximum allowed collisions */ ! #define COMPRESS_HASH_MASK 0x7FFF /**< 32767 in hex */ ! #define TABLE_FLAG 0x80 /**< Distinguish a table */ ! #define TABLE_MASK 0x7F /**< Mask out words within a table */ /* Table of words */ *** 1_7_7.366/src/comp_w8.c Mon, 10 Feb 2003 16:48:37 -0600 dunemush (pennmush/f/33_comp_w8.c 1.12 660) --- 1_7_7.446(w)/src/comp_w8.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/f/33_comp_w8.c 1.13 660) *************** *** 128,142 **** #include "mymalloc.h" #include "confmagic.h" ! #define MAXTABLE 32768 ! #define MAXWORDS 100 ! #define COLLISION_LIMIT 20 ! #define COMPRESS_HASH_MASK 0x7FFF /* 32767 */ ! #define MARKER_CHAR 0x06 ! #define TABLE_FLAG 0x80 ! #define TABLE_MASK 0x7F /* Table of words */ --- 128,142 ---- #include "mymalloc.h" #include "confmagic.h" ! #define MAXTABLE 32768 /**< Maximum words in the table */ ! #define MAXWORDS 100 /**< Maximum length of a word */ ! #define COLLISION_LIMIT 20 /**< Maximum allowed collisions */ ! #define COMPRESS_HASH_MASK 0x7FFF /**< 32767 in hex */ ! #define MARKER_CHAR 0x06 /**< Separates words. This char is the only one that can't be represented */ ! #define TABLE_FLAG 0x80 /**< Distinguishes a table */ ! #define TABLE_MASK 0x7F /**< Mask out words within a table */ /* Table of words */ *** 1_7_7.366/src/comp_h.c Sun, 09 Feb 2003 21:11:26 -0600 dunemush (pennmush/c/34_comp_h.c 1.16 660) --- 1_7_7.446(w)/src/comp_h.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/34_comp_h.c 1.17 660) *************** *** 28,46 **** #pragma warning( disable : 4244) /* NJG: disable warning re conversion */ #endif ! #define TABLE_SIZE 256 /* allow all characters */ ! #define EOS 0 /* use null code for end of string */ ! #define CHAR_BITS 8 /* number of bits in char */ ! #define CHAR_MASK 255 /* mask for just one char */ ! #define CODE_BITS 25 /* max number of bits in code */ #ifndef SAMPLE_SIZE ! #define SAMPLE_SIZE 0 /* sample entire database */ #endif ! /* The code type */ ! typedef unsigned long CType; /* must be at least CODE_BITS+CHAR_BITS-1 */ ! /* bits long */ /** A node in the huffman compression tree. */ typedef struct cnode { --- 28,47 ---- #pragma warning( disable : 4244) /* NJG: disable warning re conversion */ #endif ! #define TABLE_SIZE 256 /**< allow all characters */ ! #define EOS 0 /**< use null code for end of string */ ! #define CHAR_BITS 8 /**< number of bits in char */ ! #define CHAR_MASK 255 /**< mask for just one char */ ! #define CODE_BITS 25 /**< max number of bits in code */ #ifndef SAMPLE_SIZE ! #define SAMPLE_SIZE 0 /**< sample entire database */ #endif ! /** Type for a huffman code. It must be at least CODE_BITS+CHAR_BITS-1 ! * bits long. ! */ ! typedef unsigned long CType; /** A node in the huffman compression tree. */ typedef struct cnode { *** 1_7_7.366/src/command.c Wed, 09 Apr 2003 13:26:05 -0500 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.3 660) --- 1_7_7.446(w)/src/command.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.8 660) *************** *** 39,62 **** #endif #include "confmagic.h" ! PTAB ptab_command; ! PTAB ptab_command_perms; ! HASHTAB htab_reserved_aliases; static const char *command_isattr(char *command); static int command_check(dbref player, COMMAND_INFO *cmd); static int switch_find(COMMAND_INFO *cmd, char *sw); static void strccat(char *buff, char **bp, const char *from); ! extern int global_fun_invocations; ! extern int global_fun_recursions; void run_hook(dbref player, dbref cause, struct hook_data *hook, char *saveregs[], int save); void do_hook(dbref player, char *command, char *obj, char *attrname, int hook_type); ! COMLIST commands[] = { {"@COMMAND", "ON OFF QUIET ENABLE DISABLE RESTRICT", cmd_command, --- 39,64 ---- #endif #include "confmagic.h" ! PTAB ptab_command; /**< Prefix table for command names. */ ! PTAB ptab_command_perms; /**< Prefix table for command permissions */ ! HASHTAB htab_reserved_aliases; /**< Hash table for reserved command aliases */ static const char *command_isattr(char *command); static int command_check(dbref player, COMMAND_INFO *cmd); static int switch_find(COMMAND_INFO *cmd, char *sw); static void strccat(char *buff, char **bp, const char *from); ! extern int global_fun_invocations; /**< Counter for function invocations */ ! extern int global_fun_recursions; /**< Counter for function recursion */ void run_hook(dbref player, dbref cause, struct hook_data *hook, char *saveregs[], int save); void do_hook(dbref player, char *command, char *obj, char *attrname, int hook_type); ! /** The list of standard commands. Additional commands can be added ! * at runtime with add_command(). ! */ COMLIST commands[] = { {"@COMMAND", "ON OFF QUIET ENABLE DISABLE RESTRICT", cmd_command, *************** *** 232,238 **** {"@SHUTDOWN", "PANIC REBOOT PARANOID", cmd_shutdown, CMD_T_ANY, "WIZARD", 0}, {"@SITELOCK", "BAN CHECK REGISTER REMOVE NAME", cmd_sitelock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS, "WIZARD", 0}, ! {"@STATS", "TABLES", cmd_stats, CMD_T_ANY, 0, 0}, {"@SWEEP", "CONNECTED HERE INVENTORY EXITS", cmd_sweep, CMD_T_ANY, 0, 0}, {"@SWITCH", "NOTIFY FIRST ALL", cmd_switch, --- 234,241 ---- {"@SHUTDOWN", "PANIC REBOOT PARANOID", cmd_shutdown, CMD_T_ANY, "WIZARD", 0}, {"@SITELOCK", "BAN CHECK REGISTER REMOVE NAME", cmd_sitelock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS, "WIZARD", 0}, ! {"@STATS", "CHUNKS FREESPACE PAGING REGIONS TABLES", cmd_stats, ! CMD_T_ANY, 0, 0}, {"@SWEEP", "CONNECTED HERE INVENTORY EXITS", cmd_sweep, CMD_T_ANY, 0, 0}, {"@SWITCH", "NOTIFY FIRST ALL", cmd_switch, *************** *** 327,332 **** --- 330,336 ---- /* switch_list is defined in switchinc.c */ #include "switchinc.c" + /** Table of command permissions/restrictions. */ struct command_perms_t command_perms[] = { {"player", CMD_T_PLAYER}, {"thing", CMD_T_THING}, *************** *** 1428,1434 **** if (!atr) return; ! code = safe_uncompress(AL_STR(atr)); if (!code) return; #ifdef MEM_CHECK --- 1432,1438 ---- if (!atr) return; ! code = safe_atr_value(atr); if (!code) return; #ifdef MEM_CHECK *** 1_7_7.366/src/cmds.c Wed, 26 Mar 2003 11:33:35 -0600 dunemush (pennmush/c/37_cmds.c 1.33.1.1.1.2.1.2.2.3.1.1.1.2.1.1.1.3.1.8.1.1.2.2.2.11 660) --- 1_7_7.446(w)/src/cmds.c Thu, 29 May 2003 10:01:25 -0500 dunemush (pennmush/c/37_cmds.c 1.33.1.1.1.2.1.2.2.3.1.1.1.2.1.1.1.3.1.8.1.1.2.2.2.17 660) *************** *** 50,56 **** void do_uptime(dbref player, int mortal); extern int config_set(const char *opt, char *val, int source, int restrictions); ! /* From command.c */ extern int rhs_present; void do_hook(dbref player, char *command, char *obj, char *attrname, int hook_type); --- 50,56 ---- void do_uptime(dbref player, int mortal); extern int config_set(const char *opt, char *val, int source, int restrictions); ! /** Is there a right-hand side of the equal sign? From command.c */ extern int rhs_present; void do_hook(dbref player, char *command, char *obj, char *attrname, int hook_type); *************** *** 95,101 **** --- 95,103 ---- do_boot(player, arg_left, 0); } + /** Has the break command been called? */ extern int break_called; + COMMAND (cmd_break) { if (parse_boolean(arg_left)) break_called = 1; *************** *** 692,698 **** } COMMAND (cmd_rejectmotd) { ! do_motd(player, 4, arg_left); } COMMAND (cmd_restart) { --- 694,700 ---- } COMMAND (cmd_rejectmotd) { ! do_motd(player, MOTD_DOWN, arg_left); } COMMAND (cmd_restart) { *************** *** 770,775 **** --- 772,785 ---- COMMAND (cmd_stats) { if (SW_ISSET(sw, SWITCH_TABLES)) do_list_memstats(player); + else if (SW_ISSET(sw, SWITCH_CHUNKS)) + chunk_stats(player, 0); + else if (SW_ISSET(sw, SWITCH_REGIONS)) + chunk_stats(player, 1); + else if (SW_ISSET(sw, SWITCH_PAGING)) + chunk_stats(player, 2); + else if (SW_ISSET(sw, SWITCH_FREESPACE)) + chunk_stats(player, 3); else do_stats(player, arg_left); } *************** *** 879,885 **** } COMMAND (cmd_wizmotd) { ! do_motd(player, 2, arg_left); } COMMAND (cmd_zemit) { --- 889,895 ---- } COMMAND (cmd_wizmotd) { ! do_motd(player, MOTD_WIZ, arg_left); } COMMAND (cmd_zemit) { *** 1_7_7.366/src/bsd.c Mon, 31 Mar 2003 23:52:59 -0600 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.13 660) --- 1_7_7.446(w)/src/bsd.c Thu, 29 May 2003 10:01:24 -0500 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.27 660) *************** *** 1,4 **** ! /* bsd.c */ #include "copyrite.h" #include "config.h" --- 1,15 ---- ! /** ! * \file bsd.c ! * ! * \brief Network communication through BSD sockets for PennMUSH. ! * ! * While mysocket.c provides low-level functions for working with ! * sockets, bsd.c focuses on player descriptors, a higher-level ! * structure that tracks all information associated with a connection, ! * and through which connection i/o is done. ! * ! * ! */ #include "copyrite.h" #include "config.h" *************** *** 111,116 **** --- 122,128 ---- #include "game.h" #include "confmagic.h" #ifdef HAS_WAITPID + /** What does wait*() return? */ #define WAIT_TYPE int #else #ifdef UNION_WAIT *************** *** 122,173 **** /* BSD 4.2 and maybe some others need these defined */ #ifndef FD_ZERO #define fd_set int #define FD_ZERO(p) (*p = 0) #define FD_SET(n,p) (*p |= (1<<(n))) #define FD_CLR(n,p) (*p &= ~(1<<(n))) #define FD_ISSET(n,p) (*p & (1<<(n))) #endif /* defines for BSD 4.2 */ #ifdef HAS_GETRUSAGE ! extern void rusage_stats(void); #endif ! extern int que_next(void); /* from cque.c */ ! extern int on_second; ! extern void dispatch(void); /* from timer.c */ ! extern dbref email_register_player(const char *name, const char *email, const char *host, const char *ip); /* from player.c */ ! ! extern time_t start_time, first_start_time; ! extern int reboot_count; #ifndef WIN32 #ifndef errno ! extern int errno; #endif #endif ! extern int reserved; static int extrafd; ! int shutdown_flag = 0; ! extern int paranoid_dump; /* from game.c */ ! extern void chat_player_announce(dbref player, char *msg); static int login_number = 0; static int under_limit = 1; ! char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN], ! cf_downmotd_msg[BUFFER_LEN], cf_fullmotd_msg[BUFFER_LEN]; static char poll_msg[DOING_LEN]; ! char confname[BUFFER_LEN]; ! char errlog[BUFFER_LEN]; #define CONN_DEFAULT 0 ! /* Using Pueblo, Smial, Mushclient, Simplemu, or some other * pueblo-style HTML aware client */ #define CONN_HTML 0x1 ! /* Using a client that understands telnet options */ #define CONN_TELNET 0x2 ! /* Send a telnet option to test client */ #define CONN_TELNET_QUERY 0x4 #define TELNET_ABLE(d) ((d)->conn_flags & (CONN_TELNET | CONN_TELNET_QUERY)) --- 134,195 ---- /* BSD 4.2 and maybe some others need these defined */ #ifndef FD_ZERO + /** An fd_set is 4 bytes */ #define fd_set int + /** Clear an fd_set */ #define FD_ZERO(p) (*p = 0) + /** Set a bit in an fd_set */ #define FD_SET(n,p) (*p |= (1<<(n))) + /** Clear a bit in an fd_set */ #define FD_CLR(n,p) (*p &= ~(1<<(n))) + /** Check a bit in an fd_set */ #define FD_ISSET(n,p) (*p & (1<<(n))) #endif /* defines for BSD 4.2 */ #ifdef HAS_GETRUSAGE ! void rusage_stats(void); #endif ! int que_next(void); /* from cque.c */ ! extern int on_second; /**< Are we ready to do per-second processing? */ ! void dispatch(void); /* from timer.c */ ! dbref email_register_player(const char *name, const char *email, const char *host, const char *ip); /* from player.c */ ! ! extern time_t start_time; /**< When was the mush last rebooted? */ ! extern time_t first_start_time; /**< When was the mush first started? */ ! extern int reboot_count; /**< How many times have we been rebooted? */ #ifndef WIN32 #ifndef errno ! extern int errno; /**< Error number for routines that use it */ #endif #endif ! extern int reserved; /**< A reserved file descriptor */ static int extrafd; ! int shutdown_flag = 0; /**< Is it time to shut down? */ ! extern int paranoid_dump; /**< Are we doing a paranoid dump? */ ! void chat_player_announce(dbref player, char *msg); static int login_number = 0; static int under_limit = 1; ! char cf_motd_msg[BUFFER_LEN]; /**< The message of the day */ ! char cf_wizmotd_msg[BUFFER_LEN]; /**< The wizard motd */ ! char cf_downmotd_msg[BUFFER_LEN]; /**< The down message */ ! char cf_fullmotd_msg[BUFFER_LEN]; /**< The 'mush full' message */ static char poll_msg[DOING_LEN]; ! char confname[BUFFER_LEN]; /**< Name of the config file */ ! char errlog[BUFFER_LEN]; /**< Name of the error log file */ + /** Default connection, nothing special */ #define CONN_DEFAULT 0 ! /** Using Pueblo, Smial, Mushclient, Simplemu, or some other * pueblo-style HTML aware client */ #define CONN_HTML 0x1 ! /** Using a client that understands telnet options */ #define CONN_TELNET 0x2 ! /** Send a telnet option to test client */ #define CONN_TELNET_QUERY 0x4 + /** Is this descriptor connected to a telnet-compatible terminal? */ #define TELNET_ABLE(d) ((d)->conn_flags & (CONN_TELNET | CONN_TELNET_QUERY)) *************** *** 192,215 **** */ /* Telnet codes */ ! #define IAC 255 /* interpret as command: */ ! #define DONT 254 /* you are not to use option */ ! #define DO 253 /* please, you use option */ ! #define WONT 252 /* I won't use option */ ! #define WILL 251 /* I will use option */ ! #define SB 250 /* interpret as subnegotiation */ ! #define SE 240 /* end sub negotiation */ ! #define TN_SGA 3 /* Suppress go-ahead */ ! #define TN_LINEMODE 34 ! #define TN_NAWS 31 /* Negotiate About Window Size */ ! void test_telnet(DESC *d); ! void setup_telnet(DESC *d); ! int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend); #define DESC_ITER_CONN(d) \ for(d = descriptor_list;(d);d=(d)->next) \ if((d)->connected) #define Hidden(d) ((d->hide == 1) && Can_Hide(d->player)) #ifdef NT_TCP --- 214,241 ---- */ /* Telnet codes */ ! #define IAC 255 /**< interpret as command: */ ! #define DONT 254 /**< you are not to use option */ ! #define DO 253 /**< please, you use option */ ! #define WONT 252 /**< I won't use option */ ! #define WILL 251 /**< I will use option */ ! #define SB 250 /**< interpret as subnegotiation */ ! #define SE 240 /**< end sub negotiation */ ! #define TN_SGA 3 /**< Suppress go-ahead */ ! #define TN_LINEMODE 34 /**< Line mode */ ! #define TN_NAWS 31 /**< Negotiate About Window Size */ ! static void test_telnet(DESC *d); ! static void setup_telnet(DESC *d); ! static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend); + /** Iterate through a list of descriptors, and do something with those + * that are connected. + */ #define DESC_ITER_CONN(d) \ for(d = descriptor_list;(d);d=(d)->next) \ if((d)->connected) + /** Is a descriptor hidden? */ #define Hidden(d) ((d->hide == 1) && Can_Hide(d->player)) #ifdef NT_TCP *************** *** 240,245 **** --- 266,272 ---- static const char *shutdown_message = "Going down - Bye"; static const char *asterisk_line = "**********************************************************************"; + /** Where we save the descriptor info across reboots. */ #define REBOOTFILE "reboot.db" #if 0 *************** *** 260,288 **** #endif ! DESC *descriptor_list = 0; static int sock, sslsock; static int ndescriptors = 0; #ifdef WIN32 static WSADATA wsadata; #endif ! int restarting = 0; static int maxd = 0; ! char ccom[BUFFER_LEN]; ! dbref cplr; #ifdef INFO_SLAVE static fd_set info_pending; static int info_slave; ! Pid_t info_slave_pid = -1; ! int info_slave_state = 0; static int info_query_spill, info_reap_spill; static time_t info_queue_time = 0; #endif ! sig_atomic_t signal_shutdown_flag = 0; ! sig_atomic_t signal_dump_flag = 0; #ifdef HAS_GETRLIMIT static void init_rlimit(void); --- 287,315 ---- #endif ! DESC *descriptor_list = NULL; /**< The linked list of descriptors */ static int sock, sslsock; static int ndescriptors = 0; #ifdef WIN32 static WSADATA wsadata; #endif ! int restarting = 0; /**< Are we restarting the server after a reboot? */ static int maxd = 0; ! char ccom[BUFFER_LEN]; /**< The last command entered from a socket */ ! dbref cplr; /**< The last player who entered a command */ #ifdef INFO_SLAVE static fd_set info_pending; static int info_slave; ! Pid_t info_slave_pid = -1; /**< Process id of the info_slave process */ ! int info_slave_state = 0; /**< State of the info_slave process */ static int info_query_spill, info_reap_spill; static time_t info_queue_time = 0; #endif ! sig_atomic_t signal_shutdown_flag = 0; /**< Have we caught a shutdown signal? */ ! sig_atomic_t signal_dump_flag = 0; /**< Have we caught a dump signal? */ #ifdef HAS_GETRLIMIT static void init_rlimit(void); *************** *** 295,313 **** #endif #endif void set_signals(void); ! struct timeval *timeval_sub(struct timeval *now, struct timeval *then); ! /* Windows doesn't have gettimeofday(). Implement it if on windows, ! * otherwise just call gettimeofday(). Cuts down on conditional code ! * elsewhere */ #ifdef WIN32 ! void win_gettimeofday(struct timeval *now); #define our_gettimeofday(now) win_gettimeofday((now)) #else #define our_gettimeofday(now) gettimeofday((now), (struct timezone *)NULL) #endif ! long int msec_diff(struct timeval *now, struct timeval *then); ! struct timeval *msec_add(struct timeval *t, int x); ! struct timeval *update_quotas(struct timeval *last, struct timeval *current); static int how_many_fds(void); static void shovechars(Port_t port, Port_t sslport); --- 322,339 ---- #endif #endif void set_signals(void); ! static struct timeval *timeval_sub(struct timeval *now, struct timeval *then); #ifdef WIN32 ! /** Windows doesn't have gettimeofday(), so we implement it here */ #define our_gettimeofday(now) win_gettimeofday((now)) + static void win_gettimeofday(struct timeval *now); #else + /** A wrapper for gettimeofday() in case the system doesn't have it */ #define our_gettimeofday(now) gettimeofday((now), (struct timezone *)NULL) #endif ! static long int msec_diff(struct timeval *now, struct timeval *then); ! static struct timeval *msec_add(struct timeval *t, int x); ! static void update_quotas(struct timeval *last, struct timeval *current); static int how_many_fds(void); static void shovechars(Port_t port, Port_t sslport); *************** *** 316,378 **** static DESC *new_connection(int oldsock, int *result); #endif ! void clearstrings(DESC *d); typedef struct fblock { ! unsigned char *buff; ! size_t len; } FBLOCK; struct fcache_entries { ! FBLOCK connect_fcache[2]; ! FBLOCK motd_fcache[2]; ! FBLOCK wizmotd_fcache[2]; ! FBLOCK newuser_fcache[2]; ! FBLOCK register_fcache[2]; ! FBLOCK quit_fcache[2]; ! FBLOCK down_fcache[2]; ! FBLOCK full_fcache[2]; ! FBLOCK guest_fcache[2]; }; static struct fcache_entries fcache; static void fcache_dump(DESC *d, FBLOCK fp[2], const unsigned char *prefix); ! int fcache_read(FBLOCK *cp, const char *filename); ! void logout_sock(DESC *d); ! void shutdownsock(DESC *d); ! DESC *initializesock(int s, char *addr, char *ip); int process_output(DESC *d); /* Notify.c */ ! struct text_block *make_text_block(const unsigned char *s, int n); ! void free_text_block(struct text_block *t); ! void add_to_queue(struct text_queue *q, const unsigned char *b, int n); ! int flush_queue(struct text_queue *q, int n); ! int queue_write(DESC *d, const unsigned char *b, int n); ! int queue_eol(DESC *d); ! int queue_newwrite(DESC *d, const unsigned char *b, int n); ! int queue_string(DESC *d, const char *s); ! int queue_string_eol(DESC *d, const char *s); ! void freeqs(DESC *d); ! void welcome_user(DESC *d); static void dump_info(DESC *call_by); ! void save_command(DESC *d, const unsigned char *command); ! int process_input(DESC *d); ! void process_input_helper(DESC *d, char *tbuf1, int got); ! void set_userstring(unsigned char **userstring, const char *command); ! void process_commands(void); ! int do_command(DESC *d, char *command); static int dump_messages(DESC *d, dbref player, int new); ! int check_connect(DESC *d, const char *msg); ! void parse_connect(const char *msg, char *command, char *user, char *pass); ! void close_sockets(void); dbref find_player_by_desc(int port); void NORETURN bailout(int sig); void WIN32_CDECL signal_shutdown(int sig); void WIN32_CDECL signal_dump(int sig); void reaper(int sig); ! extern Pid_t forked_dump_pid; ! extern time_t last_dump_time; ! void dump_users(DESC *call_by, char *match, int doing); ! const char *time_format_1(long int dt); ! const char *time_format_2(long int dt); static void announce_connect(dbref player, int isnew, int num); static void announce_disconnect(dbref player); void inactivity_check(void); --- 342,408 ---- static DESC *new_connection(int oldsock, int *result); #endif ! static void clearstrings(DESC *d); ! ! /** A block of cached text. */ typedef struct fblock { ! unsigned char *buff; /**< Pointer to the block as a string */ ! size_t len; /**< Length of buff */ } FBLOCK; + /** The complete collection of cached text files. */ struct fcache_entries { ! FBLOCK connect_fcache[2]; /**< connect.txt and connect.html */ ! FBLOCK motd_fcache[2]; /**< motd.txt and motd.html */ ! FBLOCK wizmotd_fcache[2]; /**< wizmotd.txt and wizmotd.html */ ! FBLOCK newuser_fcache[2]; /**< newuser.txt and newuser.html */ ! FBLOCK register_fcache[2]; /**< register.txt and register.html */ ! FBLOCK quit_fcache[2]; /**< quit.txt and quit.html */ ! FBLOCK down_fcache[2]; /**< down.txt and down.html */ ! FBLOCK full_fcache[2]; /**< full.txt and full.html */ ! FBLOCK guest_fcache[2]; /**< guest.txt and guest.html */ }; + static struct fcache_entries fcache; static void fcache_dump(DESC *d, FBLOCK fp[2], const unsigned char *prefix); ! static int fcache_read(FBLOCK *cp, const char *filename); ! static void logout_sock(DESC *d); ! static void shutdownsock(DESC *d); ! static DESC *initializesock(int s, char *addr, char *ip); int process_output(DESC *d); /* Notify.c */ ! extern void free_text_block(struct text_block *t); ! extern void add_to_queue(struct text_queue *q, const unsigned char *b, int n); ! extern int queue_write(DESC *d, const unsigned char *b, int n); ! extern int queue_eol(DESC *d); ! extern int queue_newwrite(DESC *d, const unsigned char *b, int n); ! extern int queue_string(DESC *d, const char *s); ! extern int queue_string_eol(DESC *d, const char *s); ! extern void freeqs(DESC *d); ! static void welcome_user(DESC *d); static void dump_info(DESC *call_by); ! static void save_command(DESC *d, const unsigned char *command); ! static int process_input(DESC *d); ! static void process_input_helper(DESC *d, char *tbuf1, int got); ! static void set_userstring(unsigned char **userstring, const char *command); ! static void process_commands(void); ! static int do_command(DESC *d, char *command); static int dump_messages(DESC *d, dbref player, int new); ! static int check_connect(DESC *d, const char *msg); ! static void parse_connect(const char *msg, char *command, char *user, ! char *pass); ! static void close_sockets(void); dbref find_player_by_desc(int port); + static DESC *lookup_desc(dbref executor, const char *name); void NORETURN bailout(int sig); void WIN32_CDECL signal_shutdown(int sig); void WIN32_CDECL signal_dump(int sig); void reaper(int sig); ! extern Pid_t forked_dump_pid; /**< Process id of forking dump process */ ! extern time_t last_dump_time; /**< Time of last database dump */ ! static void dump_users(DESC *call_by, char *match, int doing); ! static const char *time_format_1(long int dt); ! static const char *time_format_2(long int dt); static void announce_connect(dbref player, int isnew, int num); static void announce_disconnect(dbref player); void inactivity_check(void); *************** *** 765,771 **** } #endif /* BOOLEXP_DEBUGGING */ ! /* Close and reopen the logfiles - called on SIGHUP */ void reopen_logs(void) { --- 795,801 ---- } #endif /* BOOLEXP_DEBUGGING */ ! /** Close and reopen the logfiles - called on SIGHUP */ void reopen_logs(void) { *************** *** 792,797 **** --- 822,828 ---- start_all_logs(); } + /** Install our default signal handlers. */ void set_signals(void) { *************** *** 821,830 **** } #ifdef WIN32 ! /* Get the time using Windows function call * Looks weird, but it works. :-P */ ! void win_gettimeofday(struct timeval *now) { --- 852,862 ---- } #ifdef WIN32 ! /** Get the time using Windows function call. * Looks weird, but it works. :-P + * \param now address to store timeval data. */ ! static void win_gettimeofday(struct timeval *now) { *************** *** 848,854 **** #endif ! struct timeval * timeval_sub(struct timeval *now, struct timeval *then) { static struct timeval mytime; --- 880,891 ---- #endif ! /** Return the difference between two timeval structs as a timeval struct. ! * \param now pointer to the timeval to subtract from. ! * \param then pointer to the timeval to subtract. ! * \return pointer to a statically allocated timeval of the difference. ! */ ! static struct timeval * timeval_sub(struct timeval *now, struct timeval *then) { static struct timeval mytime; *************** *** 864,870 **** return &mytime; } ! long int msec_diff(struct timeval *now, struct timeval *then) { long int secs = now->tv_sec - then->tv_sec; --- 901,912 ---- return &mytime; } ! /** Return the difference between two timeval structs in milliseconds. ! * \param now pointer to the timeval to subtract from. ! * \param then pointer to the timeval to subtract. ! * \return milliseconds of difference between them. ! */ ! static long int msec_diff(struct timeval *now, struct timeval *then) { long int secs = now->tv_sec - then->tv_sec; *************** *** 878,884 **** return 0; } ! struct timeval * msec_add(struct timeval *t, int x) { static struct timeval mytime; --- 920,931 ---- return 0; } ! /** Add a given number of milliseconds to a timeval. ! * \param t pointer to a timeval struct. ! * \param x number of milliseconds to add to t. ! * \return address of static timeval struct representing the sum. ! */ ! static struct timeval * msec_add(struct timeval *t, int x) { static struct timeval mytime; *************** *** 893,899 **** return &mytime; } ! struct timeval * update_quotas(struct timeval *last, struct timeval *current) { int nslices; --- 940,954 ---- return &mytime; } ! /** Update each descriptor's allowed rate of issuing commands. ! * Players are rate-limited; they may only perform up to a certain ! * number of commands per time slice. This function is run periodically ! * to refresh each descriptor's available command quota based on how ! * many slices have passed since it was last updated. ! * \param last pointer to timeval struct of last time quota was updated. ! * \param current pointer to timeval struct of current time. ! */ ! static void update_quotas(struct timeval *last, struct timeval *current) { int nslices; *************** *** 907,913 **** d->quota = COMMAND_BURST_SIZE; } } - return msec_add(last, nslices * COMMAND_TIME_MSEC); } static void --- 962,967 ---- *************** *** 964,972 **** while (shutdown_flag == 0) { our_gettimeofday(¤t_time); ! returned_time = update_quotas(&last_slice, ¤t_time); ! last_slice.tv_sec = returned_time->tv_sec; ! last_slice.tv_usec = returned_time->tv_usec; if (msec_diff(¤t_time, &then) >= 1000) { on_second = 1; --- 1018,1026 ---- while (shutdown_flag == 0) { our_gettimeofday(¤t_time); ! update_quotas(&last_slice, ¤t_time); ! last_slice.tv_sec = current_time.tv_sec; ! last_slice.tv_usec = current_time.tv_usec; if (msec_diff(¤t_time, &then) >= 1000) { on_second = 1; *************** *** 1079,1087 **** --- 1133,1143 ---- if (!found) { do_top(options.queue_chunk); + atr_migrate(CHUNK_MIGRATE_INACTIVE); continue; } else { do_top(options.active_q_chunk); + atr_migrate(CHUNK_MIGRATE_ACTIVE); } now = mudtime; #ifdef INFO_SLAVE *************** *** 1244,1250 **** } #endif ! void clearstrings(DESC *d) { if (d->output_prefix) { --- 1300,1306 ---- } #endif ! static void clearstrings(DESC *d) { if (d->output_prefix) { *************** *** 1261,1267 **** * display that line before the text file, but only if we've * got a text file to display */ ! void fcache_dump(DESC *d, FBLOCK fb[2], const unsigned char *prefix) { /* If we've got nothing nice to say, don't say anything */ --- 1317,1323 ---- * display that line before the text file, but only if we've * got a text file to display */ ! static void fcache_dump(DESC *d, FBLOCK fb[2], const unsigned char *prefix) { /* If we've got nothing nice to say, don't say anything */ *************** *** 1270,1276 **** /* We've got something to say */ if (prefix) { queue_newwrite(d, prefix, u_strlen(prefix)); ! queue_write(d, (unsigned char *) "\r\n", 2); } if (d->conn_flags & CONN_HTML) { if (fb[1].buff) --- 1326,1332 ---- /* We've got something to say */ if (prefix) { queue_newwrite(d, prefix, u_strlen(prefix)); ! queue_eol(d); } if (d->conn_flags & CONN_HTML) { if (fb[1].buff) *************** *** 1278,1288 **** else queue_write(d, fb[0].buff, fb[0].len); } else ! queue_newwrite(d, fb[0].buff, fb[0].len); } ! int fcache_read(FBLOCK *fb, const char *filename) { if (!fb || !filename) --- 1334,1344 ---- else queue_write(d, fb[0].buff, fb[0].len); } else ! queue_write(d, fb[0].buff, fb[0].len); } ! static int fcache_read(FBLOCK *fb, const char *filename) { if (!fb || !filename) *************** *** 1399,1404 **** --- 1455,1463 ---- return fb->len; } + /** Load all of the cached text files. + * \param player the enactor. + */ void fcache_load(dbref player) { *************** *** 1428,1440 **** } void fcache_init(void) { fcache_load(NOTHING); } ! void logout_sock(DESC *d) { if (d->connected) { --- 1487,1501 ---- } + /** Initialize all of the cached text files (at startup). + */ void fcache_init(void) { fcache_load(NOTHING); } ! static void logout_sock(DESC *d) { if (d->connected) { *************** *** 1484,1490 **** welcome_user(d); } ! void shutdownsock(DESC *d) { #ifdef NT_TCP --- 1545,1556 ---- welcome_user(d); } ! /** Disconnect a descriptor. ! * This sends appropriate disconnection text, flushes output, and ! * then closes the associated socket. ! * \param d pointer to descriptor to disconnect. ! */ ! static void shutdownsock(DESC *d) { #ifdef NT_TCP *************** *** 1580,1586 **** ndescriptors--; } ! DESC * initializesock(int s, char *addr, char *ip) { DESC *d; --- 1646,1652 ---- ndescriptors--; } ! static DESC * initializesock(int s, char *addr, char *ip) { DESC *d; *************** *** 1998,2003 **** --- 2064,2071 ---- } } + /** Kill the info_slave process, typically at shutdown. + */ void kill_info_slave(void) { *************** *** 2034,2039 **** --- 2102,2114 ---- + /** Flush pending output for a descriptor. + * This function actually sends the queued output over the descriptor's + * socket. + * \param d pointer to descriptor to send output to. + * \retval 1 successfully flushed all output. + * \retval 0 didn't flush all output (partial flush possible) + */ int process_output(DESC *d) { *************** *** 2075,2081 **** } ! void welcome_user(DESC *d) { if (SUPPORT_PUEBLO && !(d->conn_flags & CONN_HTML)) --- 2150,2156 ---- } ! static void welcome_user(DESC *d) { if (SUPPORT_PUEBLO && !(d->conn_flags & CONN_HTML)) *************** *** 2083,2095 **** fcache_dump(d, fcache.connect_fcache, NULL); } ! void save_command(DESC *d, const unsigned char *command) { add_to_queue(&d->input, command, u_strlen(command) + 1); } ! void test_telnet(DESC *d) { /* Use rfc 1184 to test telnet support, as it tries to set linemode --- 2158,2170 ---- fcache_dump(d, fcache.connect_fcache, NULL); } ! static void save_command(DESC *d, const unsigned char *command) { add_to_queue(&d->input, command, u_strlen(command) + 1); } ! static void test_telnet(DESC *d) { /* Use rfc 1184 to test telnet support, as it tries to set linemode *************** *** 2103,2109 **** } } ! void setup_telnet(DESC *d) { /* Win2k telnet doesn't do local echo by default, --- 2178,2184 ---- } } ! static void setup_telnet(DESC *d) { /* Win2k telnet doesn't do local echo by default, *************** *** 2121,2127 **** } } ! int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend) { /* *(*q - q) == IAC at this point. */ --- 2196,2202 ---- } } ! static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend) { /* *(*q - q) == IAC at this point. */ *************** *** 2269,2275 **** return 1; } ! void process_input_helper(DESC *d, char *tbuf1, int got) { unsigned char *p, *pend, *q, *qend; --- 2344,2350 ---- return 1; } ! static void process_input_helper(DESC *d, char *tbuf1, int got) { unsigned char *p, *pend, *q, *qend; *************** *** 2318,2325 **** } ! /* end of process_input_helper */ ! int process_input(DESC *d) { int got; --- 2393,2399 ---- } ! static int process_input(DESC *d) { int got; *************** *** 2349,2355 **** return 1; } ! void set_userstring(unsigned char **userstring, const char *command) { if (*userstring) { --- 2423,2429 ---- return 1; } ! static void set_userstring(unsigned char **userstring, const char *command) { if (*userstring) { *************** *** 2362,2368 **** *userstring = (unsigned char *) mush_strdup(command, "userstring"); } ! void process_commands(void) { int nprocessed; --- 2436,2442 ---- *userstring = (unsigned char *) mush_strdup(command, "userstring"); } ! static void process_commands(void) { int nprocessed; *************** *** 2401,2419 **** } while (nprocessed > 0); } #define send_prefix(d) \ if (d->output_prefix) { \ queue_newwrite(d, d->output_prefix, u_strlen(d->output_prefix)); \ queue_eol(d); \ } #define send_suffix(d) \ if (d->output_suffix) { \ queue_newwrite(d, d->output_suffix, u_strlen(d->output_suffix)); \ queue_eol(d); \ } ! int do_command(DESC *d, char *command) { int j; --- 2475,2495 ---- } while (nprocessed > 0); } + /** Send a descriptor's output prefix */ #define send_prefix(d) \ if (d->output_prefix) { \ queue_newwrite(d, d->output_prefix, u_strlen(d->output_prefix)); \ queue_eol(d); \ } + /** Send a descriptor's output suffix */ #define send_suffix(d) \ if (d->output_suffix) { \ queue_newwrite(d, d->output_suffix, u_strlen(d->output_suffix)); \ queue_eol(d); \ } ! static int do_command(DESC *d, char *command) { int j; *************** *** 2576,2582 **** return 1; } ! int check_connect(DESC *d, const char *msg) { char command[MAX_COMMAND_LEN]; --- 2652,2658 ---- return 1; } ! static int check_connect(DESC *d, const char *msg) { char command[MAX_COMMAND_LEN]; *************** *** 2749,2755 **** return 1; } ! void parse_connect(const char *msg1, char *command, char *user, char *pass) { unsigned char *p; --- 2825,2831 ---- return 1; } ! static void parse_connect(const char *msg1, char *command, char *user, char *pass) { unsigned char *p; *************** *** 2794,2800 **** *p = '\0'; } ! void close_sockets(void) { DESC *d, *dnext; --- 2870,2876 ---- *p = '\0'; } ! static void close_sockets(void) { DESC *d, *dnext; *************** *** 2815,2832 **** --- 2891,2917 ---- } + /** Give everyone the boot. + */ void emergency_shutdown(void) { close_sockets(); } + /** Disconnect a descriptor. + * \param d pointer to descriptor to disconnect. + */ void boot_desc(DESC *d) { shutdownsock(d); } + /** Given a player dbref, return the player's first connected descriptor. + * \param player dbref of player. + * \return pointer to player's first connected descriptor, or NULL. + */ DESC * player_desc(dbref player) { *************** *** 2839,2844 **** --- 2924,2934 ---- return (DESC *) NULL; } + /** Page a specified socket. + * \param player the enactor. + * \param pc string containing port number to send message to. + * \param message message to send. + */ void do_page_port(dbref player, const char *pc, const char *message) { *************** *** 2907,2914 **** } ! /* Return an inactive descriptor, as long as there's more than ! * one descriptor connected. Used for @boot/me. */ DESC * inactive_desc(dbref player) --- 2997,3006 ---- } ! /** Return an inactive descriptor, as long as there's more than ! * one descriptor connected. Used for boot/me. ! * \param player player to find an inactive descriptor for. ! * \return pointer to player's inactive descriptor, or NULL. */ DESC * inactive_desc(dbref player) *************** *** 2930,2935 **** --- 3022,3031 ---- return (DESC *) NULL; } + /** Given a port (a socket number), return the descriptor. + * \param port port (socket file descriptor number). + * \return pointer to descriptor associated with the port. + */ DESC * port_desc(int port) { *************** *** 2942,2952 **** return (DESC *) NULL; } dbref find_player_by_desc(int port) { - /* given a descriptor, find the matching player dbref */ - DESC *d; for (d = descriptor_list; (d); d = d->next) { if (d->connected && (d->descriptor == port)) { --- 3038,3050 ---- return (DESC *) NULL; } + /** Given a port, find the matching player dbref. + * \param port (socket file descriptor number). + * \return dbref of connected player using that port, or NOTHING. + */ dbref find_player_by_desc(int port) { DESC *d; for (d = descriptor_list; (d); d = d->next) { if (d->connected && (d->descriptor == port)) { *************** *** 2958,2973 **** return NOTHING; } - void - bailout(int sig) - { - char tbuf1[BUFFER_LEN]; - sprintf(tbuf1, T("BAILOUT: caught signal %d"), sig); - panic(tbuf1); - _exit(7); - } #ifndef WIN32 void signal_shutdown(int sig __attribute__ ((__unused__))) { --- 3056,3066 ---- return NOTHING; } #ifndef WIN32 + /** Handler for SIGINT. Note that we've received it, and reinstall. + * \param sig signal caught. + */ void signal_shutdown(int sig __attribute__ ((__unused__))) { *************** *** 2975,2980 **** --- 3068,3076 ---- reload_sig_handler(SIGINT, signal_shutdown); } + /** Handler for SIGUSR2. Note that we've received it, and reinstall + * \param sig signal caught. + */ void signal_dump(int sig __attribute__ ((__unused__))) { *************** *** 2983,2991 **** } #endif /* To-do: What about OS X? */ #if !(defined(macintosh) || defined(WIN32)) ! /* Don't fear this function :) */ void reaper(int sig __attribute__ ((__unused__))) { --- 3079,3102 ---- } #endif + /** A general handler to puke and die. + * \param sig signal caught. + */ + void + bailout(int sig) + { + char tbuf1[BUFFER_LEN]; + sprintf(tbuf1, T("BAILOUT: caught signal %d"), sig); + panic(tbuf1); + _exit(7); + } + /* To-do: What about OS X? */ #if !(defined(macintosh) || defined(WIN32)) ! /** Reap child processes, notably info_slaves and forking dumps, ! * when we receive a SIGCHLD signal. Don't fear this function. :) ! * \param sig signal caught. ! */ void reaper(int sig __attribute__ ((__unused__))) { *************** *** 3106,3112 **** } ! void dump_users(DESC *call_by, char *match, int doing) /* doing: 0 if normal WHO, 1 if DOING, 2 if SESSION */ { --- 3217,3223 ---- } ! static void dump_users(DESC *call_by, char *match, int doing) /* doing: 0 if normal WHO, 1 if DOING, 2 if SESSION */ { *************** *** 3241,3247 **** queue_newwrite(call_by, (unsigned char *) "\r\n", 2); } ! const char * time_format_1(long dt) { register struct tm *delta; --- 3352,3358 ---- queue_newwrite(call_by, (unsigned char *) "\r\n", 2); } ! static const char * time_format_1(long dt) { register struct tm *delta; *************** *** 3264,3270 **** return buf; } ! const char * time_format_2(long dt) { register struct tm *delta; --- 3375,3381 ---- return buf; } ! static const char * time_format_2(long dt) { register struct tm *delta; *************** *** 3359,3365 **** /* added to allow player's inventory to hear a player connect */ if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTERACTION); /* clear the environment for possible actions */ for (j = 0; j < 10; j++) --- 3470,3476 ---- /* added to allow player's inventory to hear a player connect */ if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE); /* clear the environment for possible actions */ for (j = 0; j < 10; j++) *************** *** 3428,3434 **** sprintf(tbuf1, T("%s has disconnected."), Name(player)); if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTERACTION); /* notify contents */ notify_except(Contents(player), player, tbuf1, 0); --- 3539,3545 ---- sprintf(tbuf1, T("%s has disconnected."), Name(player)); if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE); /* notify contents */ notify_except(Contents(player), player, tbuf1, 0); *************** *** 3472,3478 **** sprintf(tbuf1, T("%s has partially disconnected."), Name(player)); if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTERACTION); /* notify contents */ notify_except(Contents(player), player, tbuf1, 0); } --- 3583,3589 ---- sprintf(tbuf1, T("%s has partially disconnected."), Name(player)); if (!Dark(player)) ! notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE); /* notify contents */ notify_except(Contents(player), player, tbuf1, 0); } *************** *** 3503,3508 **** --- 3614,3627 ---- local_disconnect(player, num); } + /** Set an motd message. + * \verbatim + * This implements @motd. + * \endverbatim + * \param player the enactor. + * \param key type of MOTD to set. + * \param message text to set the motd to. + */ void do_motd(dbref player, enum motd_type key, const char *message) { *************** *** 3540,3545 **** --- 3659,3671 ---- } } + /** Set a DOING message. + * \verbatim + * This implements @doing. + * \endverbatim + * \param player the enactor. + * \param message the message to set. + */ void do_doing(dbref player, const char *message) { *************** *** 3574,3580 **** notify(player, T("Doing set.")); } ! /* this sets the message which replaces "Doing" */ void do_poll(dbref player, const char *message) { --- 3700,3712 ---- notify(player, T("Doing set.")); } ! /** Set a poll message (which replaces "Doing" in the DOING output). ! * \verbatim ! * This implements @poll. ! * \endverbatim ! * \param player the enactor. ! * \param message the message to set. ! */ void do_poll(dbref player, const char *message) { *************** *** 3602,3612 **** do_log(LT_WIZ, player, NOTHING, T("Poll Set to '%s'."), poll_msg); } dbref short_page(const char *match) { - /* attempts to match to the partial name of a connected player */ - DESC *d; dbref who1 = NOTHING; int count = 0; --- 3734,3747 ---- do_log(LT_WIZ, player, NOTHING, T("Poll Set to '%s'."), poll_msg); } + /** Match the partial name of a connected player. + * \param match string to match. + * \return dbref of a unique connected player whose name partial-matches, + * AMBIGUOUS, or NOTHING. + */ dbref short_page(const char *match) { DESC *d; dbref who1 = NOTHING; int count = 0; *************** *** 3635,3644 **** return who1; } dbref visible_short_page(dbref player, const char *match) { - /* match to the partial name of a connected player visible to you */ dbref target; target = short_page(match); if (Priv_Who(player) || !GoodObject(target)) --- 3770,3784 ---- return who1; } + /** Match the partial name of a connected player the enactor can see. + * \param player the enactor + * \param match string to match. + * \return dbref of a unique connected player whose name partial-matches, + * AMBIGUOUS, or NOTHING. + */ dbref visible_short_page(dbref player, const char *match) { dbref target; target = short_page(match); if (Priv_Who(player) || !GoodObject(target)) *************** *** 3648,3654 **** return target; } ! /* LWHO() function - really belongs in eval.c but needs stuff declared here */ /* ARGSUSED */ FUNCTION(fun_lwho) --- 3788,3794 ---- return target; } ! /* LWHO() function - really belongs elsewhere but needs stuff declared here */ /* ARGSUSED */ FUNCTION(fun_lwho) *************** *** 3669,3675 **** } } - static DESC *lookup_desc(dbref executor, const char *name); static DESC * lookup_desc(dbref executor, const char *name) --- 3809,3814 ---- *************** *** 3815,3823 **** } void hide_player(dbref player, int hide) - /* hide player? */ { DESC *d; if (!Connected(player)) --- 3954,3967 ---- } + /** Hide or unhide a player. + * Although hiding is a per-descriptor state, this function sets all of + * a player's connected descriptors to be hidden. + * \param player dbref of player to hide. + * \param hide if 1, hide; if 0, unhide. + */ void hide_player(dbref player, int hide) { DESC *d; if (!Connected(player)) *************** *** 3827,3833 **** return; } /* change status on WHO */ - if (Can_Hide(player)) { DESC_ITER_CONN(d) { if (d->player == player) --- 3971,3976 ---- *************** *** 3840,3845 **** --- 3983,3991 ---- notify(player, T("You now appear on the WHO list.")); } + /** Perform the periodic check of inactive descriptors, and + * disconnect them or autohide them as appropriate. + */ void inactivity_check(void) { *************** *** 3848,3855 **** if (!INACTIVITY_LIMIT && !UNCONNECTED_LIMIT) return; now = mudtime; ! idle = INACTIVITY_LIMIT ? INACTIVITY_LIMIT * 60 : INT_MAX; ! unconnected_idle = UNCONNECTED_LIMIT ? UNCONNECTED_LIMIT * 60 : INT_MAX; for (d = descriptor_list; d; d = nextd) { nextd = d->next; idle_for = now - d->last_time; --- 3994,4001 ---- if (!INACTIVITY_LIMIT && !UNCONNECTED_LIMIT) return; now = mudtime; ! idle = INACTIVITY_LIMIT ? INACTIVITY_LIMIT : INT_MAX; ! unconnected_idle = UNCONNECTED_LIMIT ? UNCONNECTED_LIMIT : INT_MAX; for (d = descriptor_list; d; d = nextd) { nextd = d->next; idle_for = now - d->last_time; *************** *** 3882,3887 **** --- 4028,4038 ---- } + /** Given a player dbref, return the player's hidden status. + * \param player dbref of player to check. + * \retval 1 player is hidden. + * \retval 0 player is not hidden. + */ int hidden(dbref player) { *************** *** 3899,3914 **** #ifdef USE_MAILER ! /* Return the mailp of the player closest in db# to player, ! * or NULL if there's nobody on-line */ ! struct mail * desc_mail(dbref player) { DESC *d; int i; int diff = db_top; ! static struct mail *mp; mp = NULL; DESC_ITER_CONN(d) { i = abs(d->player - player); --- 4050,4079 ---- #ifdef USE_MAILER ! /** Return the mailp of the player closest in db# to player, ! * or NULL if there's nobody on-line. ! * In the current mail system, mail is stored in a linked list, sorted ! * by recipient, which makes the most common operations (listing and reading ! * your mail) fast. When a player first connects, we store (on the ! * mailp element of their descriptor) a pointer to the beginning of ! * their part of the linked list. Rather than search the whole linked ! * list to find this location, we look at the mailp's of all the other ! * connected players, and find the mailp of the player whose dbref ! * is closest to the connecting player, and start our search from that ! * point. This scales up nicely - as a mushes get larger, the linked ! * list gets larger, but the more people connected at once, the faster ! * the search for a newly connecting player's first mail. ! * \param player player whose db# we want to get near. ! * \return pointer to first mail of connected player with db# closest to ! * player. */ ! MAIL * desc_mail(dbref player) { DESC *d; int i; int diff = db_top; ! static MAIL *mp; mp = NULL; DESC_ITER_CONN(d) { i = abs(d->player - player); *************** *** 3922,3929 **** return mp; } void ! desc_mail_set(dbref player, struct mail *mp) { DESC *d; DESC_ITER_CONN(d) { --- 4087,4098 ---- return mp; } + /** Set a player's mail position on all their descriptors. + * \param player player to set mail position for. + * \param mp pointer to first mail in their list. + */ void ! desc_mail_set(dbref player, MAIL *mp) { DESC *d; DESC_ITER_CONN(d) { *************** *** 3932,3937 **** --- 4101,4108 ---- } } + /** Clear mail positions on all descriptors. Called from do_mail_nuke(). + */ void desc_mail_clear(void) { *************** *** 4082,4092 **** #endif /* WIN 32 */ } ! /* ! * --------------------------------------------------------------------------- ! * * dump_reboot_db: Dumps descriptor_list to REBOOTFILE */ - extern jmp_buf db_err; void dump_reboot_db(void) { --- 4253,4262 ---- #endif /* WIN 32 */ } ! extern jmp_buf db_err; /**< Jump buffer in the event of a dump error */ ! ! /** Dump the descriptor list to our REBOOTFILE so we can restore it on reboot. */ void dump_reboot_db(void) { *************** *** 4150,4155 **** --- 4320,4327 ---- } } + /** Load the descriptor list back from the REBOOTFILE on reboot. + */ void load_reboot_db(void) { *** 1_7_7.366/src/boolexp.c Sun, 09 Feb 2003 14:41:06 -0600 dunemush (pennmush/c/39_boolexp.c 1.1.1.17.1.1.1.13.1.2.1.5 660) --- 1_7_7.446(w)/src/boolexp.c Thu, 29 May 2003 10:01:23 -0500 dunemush (pennmush/c/39_boolexp.c 1.1.1.17.1.1.1.13.1.2.1.8 660) *************** *** 57,73 **** static int check_attrib_lock(dbref player, dbref target, const char *atrname, const char *str); ! /* Assuming 4096 byte pages and 16 byte boolexps */ #define BOOLEXPS_PER_PAGE 250 static struct boolexp *free_list = NULL; extern StrTree atr_names; extern StrTree lock_names; extern int loading_db; static int boolexp_recursion = 0; struct boolatr * alloc_atr(const char *name, const char *s) { --- 57,85 ---- static int check_attrib_lock(dbref player, dbref target, const char *atrname, const char *str); ! /** How many boolexps in a page of memory to allocate? ! * Assuming 4096 byte pages and 16 byte boolexps. ! */ #define BOOLEXPS_PER_PAGE 250 static struct boolexp *free_list = NULL; + /** String tree of attribute names. */ extern StrTree atr_names; + /** String tree of lock names. */ extern StrTree lock_names; + /** Are we currently loading the db? If so, we avoid certain checks that + * would create a circularity. + */ extern int loading_db; static int boolexp_recursion = 0; + /** Allocate memory to store an attribute test in a boolexp. + * \param name attribute name. + * \param s text to test attribute against. + * \return pointer to newly allocated boolatr structure. + */ struct boolatr * alloc_atr(const char *name, const char *s) { *************** *** 359,365 **** } boolexp_recursion--; strcpy(tbuf1, b->data.atr_lock->text); ! strcpy(tbuf2, uncompress(a->value)); return local_wild_match(tbuf1, tbuf2); case BOOLEXP_EVAL: boolexp_recursion++; --- 371,377 ---- } boolexp_recursion--; strcpy(tbuf1, b->data.atr_lock->text); ! strcpy(tbuf2, atr_value(a)); return local_wild_match(tbuf1, tbuf2); case BOOLEXP_EVAL: boolexp_recursion++; *************** *** 917,923 **** return 0; if (!Can_Read_Attr(target, target, a)) return 0; ! asave = safe_uncompress(a->value); /* perform pronoun substitution */ save_global_regs("check_attrib_lock_save", preserve); bp = buff; --- 929,935 ---- return 0; if (!Can_Read_Attr(target, target, a)) return 0; ! asave = safe_atr_value(a); /* perform pronoun substitution */ save_global_regs("check_attrib_lock_save", preserve); bp = buff; *** 1_7_7.366/src/attrib.c Wed, 26 Mar 2003 14:31:41 -0600 dunemush (pennmush/c/40_attrib.c 1.15.1.2.1.5.1.1.1.3.1.3.1.2.1.2.1.2.2.1.1.2.1.2.1.2.1.1.1.3.1.1.1.1.1.1.3.7 660) --- 1_7_7.446(w)/src/attrib.c Thu, 29 May 2003 10:01:23 -0500 dunemush (pennmush/c/40_attrib.c 1.15.1.2.1.5.1.1.1.3.1.3.1.2.1.2.1.2.2.1.1.2.1.2.1.2.1.1.1.3.1.1.1.1.1.1.3.15 660) *************** *** 11,16 **** --- 11,17 ---- #include #include #include "conf.h" + #include "chunk.h" #include "attrib.h" #include "dbdefs.h" #include "externs.h" *************** *** 29,43 **** #include "log.h" #include "confmagic.h" ! /* A hash table that temporarily stores matching attributes from current * and parent objects when we're searching for commands on attributes */ StrTree temp_attrib; StrTree atr_names; extern PRIV attr_privs[]; /*======================================================================*/ #define ATTRS_PER_PAGE (200) /** A page of memory for attributes. --- 30,49 ---- #include "log.h" #include "confmagic.h" ! /** A string tree that temporarily stores matching attributes from current * and parent objects when we're searching for commands on attributes */ StrTree temp_attrib; + /** A string tree of attribute names in use, to save us memory since + * many are duplicated. + */ StrTree atr_names; + /** Table of attribute flags. */ extern PRIV attr_privs[]; /*======================================================================*/ + /** How many attributes go in a "page" of attribute memory? */ #define ATTRS_PER_PAGE (200) /** A page of memory for attributes. *************** *** 62,68 **** st_init(&atr_names); } ! /* Lookup table for good_atr_name */ extern char atr_name_table[UCHAR_MAX + 1]; /** Decide if a name is valid for an attribute. --- 68,74 ---- st_init(&atr_names); } ! /** Lookup table for good_atr_name */ extern char atr_name_table[UCHAR_MAX + 1]; /** Decide if a name is valid for an attribute. *************** *** 120,135 **** * This is a stripped down version of atr_add, without duplicate checking, * permissions checking, attribute count checking, or auto-ODARKing. * If anyone uses this outside of database load or atr_cpy (below), ! * I will personally string them up by their toes. - Alex * \param thing object to set the attribute on. * \param atr name of the attribute to set. * \param s value of the attribute to set. * \param player the attribute creator. * \param flags bitmask of attribute flags for this attribute. */ void atr_new_add(dbref thing, const char *RESTRICT atr, const char *RESTRICT s, ! dbref player, int flags) { ATTR *ptr; char const *name; --- 126,142 ---- * This is a stripped down version of atr_add, without duplicate checking, * permissions checking, attribute count checking, or auto-ODARKing. * If anyone uses this outside of database load or atr_cpy (below), ! * I will personally string them up by their toes. - Alex * \param thing object to set the attribute on. * \param atr name of the attribute to set. * \param s value of the attribute to set. * \param player the attribute creator. * \param flags bitmask of attribute flags for this attribute. + * \param derefs the initial deref count to use for the attribute value. */ void atr_new_add(dbref thing, const char *RESTRICT atr, const char *RESTRICT s, ! dbref player, int flags, unsigned char derefs) { ATTR *ptr; char const *name; *************** *** 160,166 **** AL_FLAGS(ptr) = (flags != NOTHING) ? flags : 0; AL_FLAGS(ptr) &= ~AF_COMMAND & ~AF_LISTEN; AL_CREATOR(ptr) = player; ! AL_STR(ptr) = NULL; /* link it in */ ins = &List(thing); --- 167,173 ---- AL_FLAGS(ptr) = (flags != NOTHING) ? flags : 0; AL_FLAGS(ptr) &= ~AF_COMMAND & ~AF_LISTEN; AL_CREATOR(ptr) = player; ! ptr->data = NULL_CHUNK_REFERENCE; /* link it in */ ins = &List(thing); *************** *** 171,186 **** /* replace string with new string */ if (!s || !*s) { ! AL_STR(ptr) = (unsigned char *) ""; } else { ! AL_STR(ptr) = compress(s); ! if (!AL_STR(ptr)) { ! AL_STR(ptr) = (unsigned char *) ""; return; ! } ! #ifdef MEM_CHECK ! add_check(T("ATTR value")); ! #endif } if (*s == '$') AL_FLAGS(ptr) |= AF_COMMAND; --- 178,190 ---- /* replace string with new string */ if (!s || !*s) { ! /* nothing */ } else { ! unsigned char *t = compress(s); ! if (!t) return; ! ptr->data = chunk_create(t, u_strlen(t) + 1, derefs); ! free(t); } if (*s == '$') AL_FLAGS(ptr) |= AF_COMMAND; *************** *** 188,195 **** --- 192,202 ---- AL_FLAGS(ptr) |= AF_LISTEN; } + /** Attribute error - invalid name */ #define AE_BADNAME -3 + /** Attribute error - attempt to overwrite a safe attribute */ #define AE_SAFE -2 + /** Attribute error - general failure */ #define AE_ERROR -1 /** Add an attribute to an object, safely. *************** *** 265,271 **** AL_FLAGS(ptr) |= flags; } AL_CREATOR(ptr) = player; ! AL_STR(ptr) = NULL; /* check for permission to create atr */ if (!Can_Write_Attr(player, thing, ptr) || --- 272,278 ---- AL_FLAGS(ptr) |= flags; } AL_CREATOR(ptr) = player; ! ptr->data = NULL_CHUNK_REFERENCE; /* check for permission to create atr */ if (!Can_Write_Attr(player, thing, ptr) || *************** *** 298,316 **** AL_FLAGS(ptr) &= ~AF_COMMAND & ~AF_LISTEN; /* replace string with new string */ ! if (AL_STR(ptr) && *AL_STR(ptr)) ! mush_free((Malloc_t) AL_STR(ptr), T("ATTR value")); if (!s || !*s) { ! AL_STR(ptr) = (unsigned char *) ""; } else { ! AL_STR(ptr) = compress(s); ! if (!AL_STR(ptr)) { ! AL_STR(ptr) = (unsigned char *) ""; return AE_ERROR; } ! #ifdef MEM_CHECK ! add_check(T("ATTR value")); ! #endif } if (*s == '$') AL_FLAGS(ptr) |= AF_COMMAND; --- 305,322 ---- AL_FLAGS(ptr) &= ~AF_COMMAND & ~AF_LISTEN; /* replace string with new string */ ! if (ptr->data) ! chunk_delete(ptr->data); if (!s || !*s) { ! ptr->data = NULL_CHUNK_REFERENCE; } else { ! unsigned char *t = compress(s); ! if (!t) { ! ptr->data = NULL_CHUNK_REFERENCE; return AE_ERROR; } ! ptr->data = chunk_create(t, u_strlen(t) + 1, 0); ! free(t); } if (*s == '$') AL_FLAGS(ptr) |= AF_COMMAND; *************** *** 332,343 **** ATTR *ptr, **prev; int comp = 0; - #ifdef EMPTY_ATTRS - #ifndef DELETE_ATTRS - return atr_add(thing, atr, "", player, NOTHING); - #endif - #endif - prev = &List(thing); ptr = *prev; while (ptr && (comp = strcoll(atr, AL_NAME(ptr))) > 0) { --- 338,343 ---- *************** *** 358,365 **** *prev = AL_NEXT(ptr); ! if (AL_STR(ptr) && *AL_STR(ptr)) ! mush_free((Malloc_t) AL_STR(ptr), T("ATTR value")); st_delete(AL_NAME(ptr), &atr_names); AL_NEXT(ptr) = atr_free_list; --- 358,365 ---- *prev = AL_NEXT(ptr); ! if (ptr->data) ! chunk_delete(ptr->data); st_delete(AL_NAME(ptr), &atr_names); AL_NEXT(ptr) = atr_free_list; *************** *** 373,380 **** * This function retrieves an attribute from an object, or from its * parent chain, returning a pointer to the first attribute that * matches or NULL. This is a pointer to an attribute structure, not ! * to the value of the attribute, so the (compressed) value is usually ! * accessed through AL_STR() (and then uncompressed). * \param thing the object containing the attribute. * \param atr the name of the attribute. * \return pointer to the attribute structure retrieved, or NULL. --- 373,380 ---- * This function retrieves an attribute from an object, or from its * parent chain, returning a pointer to the first attribute that * matches or NULL. This is a pointer to an attribute structure, not ! * to the value of the attribute, so the value is usually accessed ! * through atr_value() or safe_atr_value(). * \param thing the object containing the attribute. * \param atr the name of the attribute. * \return pointer to the attribute structure retrieved, or NULL. *************** *** 385,438 **** ATTR *ptr; int parent_depth; dbref temp; int comp; if (thing == NOTHING || !good_atr_name(atr)) return NULL; - /* try real name */ for (parent_depth = 0, temp = thing; parent_depth < MAX_PARENTS && temp != NOTHING; ! parent_depth++, temp = Parent(temp)) for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0 ! #ifndef VISIBLE_EMPTY_ATTRS ! && *AL_STR(ptr) ! #endif ! ) { if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) return ptr; else break; } } ptr = atr_match(atr); if (!ptr || !strcmp(atr, AL_NAME(ptr))) return NULL; atr = AL_NAME(ptr); - /* try alias */ for (parent_depth = 0, temp = thing; parent_depth < MAX_PARENTS && temp != NOTHING; ! parent_depth++, temp = Parent(temp)) for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0 ! #ifndef VISIBLE_EMPTY_ATTRS ! && *AL_STR(ptr) ! #endif ! ) { if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) return ptr; else break; } } return NULL; } --- 385,481 ---- ATTR *ptr; int parent_depth; dbref temp; + dbref ancestor; int comp; if (thing == NOTHING || !good_atr_name(atr)) return NULL; for (parent_depth = 0, temp = thing; parent_depth < MAX_PARENTS && temp != NOTHING; ! parent_depth++, temp = Parent(temp)) { ! /* If the ancestor of the object is in its explict parent chain, ! * we use it there, and don't check the ancestor later ! */ ! if (temp == ancestor) ! ancestor = -1; for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0) { if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) return ptr; else break; } } + } + /* If we have a valid ancestor, search it as well */ + ancestor = Ancestor_Parent(thing); + if (GoodObject(ancestor)) { + for (parent_depth = 0, temp = ancestor; + parent_depth < MAX_PARENTS && temp != NOTHING; + parent_depth++, temp = Parent(temp)) { + for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { + comp = strcoll(atr, AL_NAME(ptr)); + if (comp < 0) + break; + if (comp == 0) { + if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) + return ptr; + else + break; + } + } + } + } + + /* Perhaps we should try the alias */ ptr = atr_match(atr); if (!ptr || !strcmp(atr, AL_NAME(ptr))) return NULL; atr = AL_NAME(ptr); for (parent_depth = 0, temp = thing; parent_depth < MAX_PARENTS && temp != NOTHING; ! parent_depth++, temp = Parent(temp)) { ! /* If the ancestor of the object is in its explict parent chain, ! * we use it there, and don't check the ancestor later ! */ ! if (temp == ancestor) ! ancestor = -1; for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0) { if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) return ptr; else break; } } + } + /* If we have a valid ancestor, search it as well */ + if (GoodObject(ancestor)) { + for (parent_depth = 0, temp = ancestor; + parent_depth < MAX_PARENTS && temp != NOTHING; + parent_depth++, temp = Parent(temp)) { + for (ptr = List(temp); ptr; ptr = AL_NEXT(ptr)) { + comp = strcoll(atr, AL_NAME(ptr)); + if (comp < 0) + break; + if (comp == 0) { + if (temp == thing || !(AL_FLAGS(ptr) & AF_PRIVATE)) + return ptr; + else + break; + } + } + } + } + return NULL; } *************** *** 441,447 **** * check the parent chain. It returns a pointer to the attribute * or NULL. This is a pointer to an attribute structure, not * to the value of the attribute, so the (compressed) value is usually ! * accessed through AL_STR() (and then uncompressed). * \param thing the object containing the attribute. * \param atr the name of the attribute. * \return pointer to the attribute structure retrieved, or NULL. --- 484,491 ---- * check the parent chain. It returns a pointer to the attribute * or NULL. This is a pointer to an attribute structure, not * to the value of the attribute, so the (compressed) value is usually ! * to the value of the attribute, so the value is usually accessed ! * through atr_value() or safe_atr_value(). * \param thing the object containing the attribute. * \param atr the name of the attribute. * \return pointer to the attribute structure retrieved, or NULL. *************** *** 460,470 **** comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0 ! #ifndef VISIBLE_EMPTY_ATTRS ! && *AL_STR(ptr) ! #endif ! ) return ptr; } --- 504,510 ---- comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0) return ptr; } *************** *** 478,488 **** comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0 ! #ifndef VISIBLE_EMPTY_ATTRS ! && *AL_STR(ptr) ! #endif ! ) return ptr; } --- 518,524 ---- comp = strcoll(atr, AL_NAME(ptr)); if (comp < 0) break; ! if (comp == 0) return ptr; } *************** *** 514,532 **** if (!wildcard(name)) { ptr = atr_get_noparent(thing, strupper(name)); ! if (ptr && ! #ifndef VISIBLE_EMPTY_ATTRS ! *AL_STR(ptr) && ! #endif ! Can_Read_Attr(player, thing, ptr)) result = func(player, thing, name, ptr, args); } else { for (ptr = List(thing); ptr; ptr = next) { next = AL_NEXT(ptr); if (Can_Read_Attr(player, thing, ptr) && - #ifndef VISIBLE_EMPTY_ATTRS - *AL_STR(ptr) && - #endif local_wild_match(name, AL_NAME(ptr))) result += func(player, thing, name, ptr, args); } --- 550,561 ---- if (!wildcard(name)) { ptr = atr_get_noparent(thing, strupper(name)); ! if (ptr && Can_Read_Attr(player, thing, ptr)) result = func(player, thing, name, ptr, args); } else { for (ptr = List(thing); ptr; ptr = next) { next = AL_NEXT(ptr); if (Can_Read_Attr(player, thing, ptr) && local_wild_match(name, AL_NAME(ptr))) result += func(player, thing, name, ptr, args); } *************** *** 556,563 **** while ((ptr = List(thing))) { List(thing) = AL_NEXT(ptr); ! if (AL_STR(ptr) && *AL_STR(ptr)) ! mush_free((Malloc_t) AL_STR(ptr), T("ATTR value")); st_delete(AL_NAME(ptr), &atr_names); AL_NEXT(ptr) = atr_free_list; --- 585,592 ---- while ((ptr = List(thing))) { List(thing) = AL_NEXT(ptr); ! if (ptr->data) ! chunk_delete(ptr->data); st_delete(AL_NAME(ptr), &atr_names); AL_NEXT(ptr) = atr_free_list; *************** *** 581,592 **** List(dest) = NULL; for (ptr = List(source); ptr; ptr = AL_NEXT(ptr)) if (!(AL_FLAGS(ptr) & AF_NOCOPY) - #ifndef VISIBLE_EMPTY_ATTRS - && *AL_STR(ptr) - #endif && (AttrCount(dest) < MAX_ATTRCOUNT)) { ! atr_new_add(dest, AL_NAME(ptr), uncompress(AL_STR(ptr)), ! AL_CREATOR(ptr), AL_FLAGS(ptr)); AttrCount(dest)++; } } --- 610,618 ---- List(dest) = NULL; for (ptr = List(source); ptr; ptr = AL_NEXT(ptr)) if (!(AL_FLAGS(ptr) & AF_NOCOPY) && (AttrCount(dest) < MAX_ATTRCOUNT)) { ! atr_new_add(dest, AL_NAME(ptr), atr_value(ptr), ! AL_CREATOR(ptr), AL_FLAGS(ptr), AL_DEREFS(ptr)); AttrCount(dest)++; } } *************** *** 642,656 **** st_init(&temp_attrib); for (ptr = List(thing); ptr; ptr = AL_NEXT(ptr)) { - #ifndef VISIBLE_EMPTY_ATTRS - if (!*AL_STR(ptr)) - continue; - #endif if (parent_depth) st_insert(AL_NAME(ptr), &temp_attrib); if (AL_FLAGS(ptr) & AF_NOPROG || !(AL_FLAGS(ptr) & flag_mask)) continue; ! strcpy(tbuf1, uncompress(AL_STR(ptr))); s = tbuf1; do { s = strchr(s + 1, end); --- 668,678 ---- st_init(&temp_attrib); for (ptr = List(thing); ptr; ptr = AL_NEXT(ptr)) { if (parent_depth) st_insert(AL_NAME(ptr), &temp_attrib); if (AL_FLAGS(ptr) & AF_NOPROG || !(AL_FLAGS(ptr) & flag_mask)) continue; ! strcpy(tbuf1, atr_value(ptr)); s = tbuf1; do { s = strchr(s + 1, end); *************** *** 692,701 **** for (parent_depth = MAX_PARENTS, parent = Parent(thing); parent_depth-- && parent != NOTHING; parent = Parent(parent)) { for (ptr = List(parent); ptr; ptr = AL_NEXT(ptr)) { - #ifndef VISIBLE_EMPTY_ATTRS - if (!*AL_STR(ptr)) - continue; - #endif if (st_find(AL_NAME(ptr), &temp_attrib)) continue; if (Parent(parent) != NOTHING) --- 714,719 ---- *************** *** 703,709 **** if (AL_FLAGS(ptr) & (AF_NOPROG | AF_PRIVATE) || !(AL_FLAGS(ptr) & flag_mask)) continue; ! strcpy(tbuf1, uncompress(AL_STR(ptr))); s = tbuf1; do { s = strchr(s + 1, end); --- 721,727 ---- if (AL_FLAGS(ptr) & (AF_NOPROG | AF_PRIVATE) || !(AL_FLAGS(ptr) & flag_mask)) continue; ! strcpy(tbuf1, atr_value(ptr)); s = tbuf1; do { s = strchr(s + 1, end); *************** *** 776,786 **** int was_listener; dbref contents; - - #ifndef DELETE_ATTRS - if (!s) - s = ""; - #endif #ifndef EMPTY_ATTRS if (s && !*s) s = NULL; --- 794,799 ---- *************** *** 799,805 **** old = atr_get_noparent(thing, "ALIAS"); if (old) { /* Old alias - we're allowed to change to a different case */ ! strcpy(tbuf1, uncompress(AL_STR(old))); if (s && (!*s || (strcasecmp(s, tbuf1) && !ok_player_name(s)))) { notify(player, T("That is not a valid alias.")); return -1; --- 812,818 ---- old = atr_get_noparent(thing, "ALIAS"); if (old) { /* Old alias - we're allowed to change to a different case */ ! strcpy(tbuf1, atr_value(old)); if (s && (!*s || (strcasecmp(s, tbuf1) && !ok_player_name(s)))) { notify(player, T("That is not a valid alias.")); return -1; *************** *** 879,889 **** if (!s && !was_listener && !Hearer(thing)) { notify_except(contents, thing, tprintf(T("%s loses its ears and becomes deaf."), ! Name(thing)), NA_INTERACTION); } else if (s && !was_hearer && !was_listener) { notify_except(contents, thing, tprintf(T("%s grows ears and can now hear."), ! Name(thing)), NA_INTERACTION); } } } --- 892,902 ---- if (!s && !was_listener && !Hearer(thing)) { notify_except(contents, thing, tprintf(T("%s loses its ears and becomes deaf."), ! Name(thing)), NA_INTER_PRESENCE); } else if (s && !was_hearer && !was_listener) { notify_except(contents, thing, tprintf(T("%s grows ears and can now hear."), ! Name(thing)), NA_INTER_PRESENCE); } } } *************** *** 1037,1042 **** --- 1050,1056 ---- /** Return the head of the attribute free list. * This function returns the head of the attribute free list. If there * are no more ATTR's on the free list, allocate a new page. + * \return the pointer to the head of the attribute free list. */ static ATTR * get_atr_free_list(void) *************** *** 1054,1056 **** --- 1068,1171 ---- } return atr_free_list; } + + /** Return the compressed data for an attribute. + * This is a chokepoint function for accessing the chunk data. + * \param atr the attribute struct from which to get the data reference. + * \return a pointer to the compressed data, in a static buffer. + */ + unsigned char *const + atr_get_compressed_data(ATTR *atr) + { + static unsigned char buffer[BUFFER_LEN * 2]; + if (!atr->data) + return (unsigned char *) ""; + if (chunk_fetch(atr->data, buffer, sizeof(buffer)) > sizeof(buffer)) + return (unsigned char *) ""; + return buffer; + } + + /** Return the uncompressed data for an attribute in a static buffer. + * This is a wrapper function, to centralize the use of compression/ + * decompression on attributes. + * \param atr the attribute struct from which to get the data reference. + * \return a pointer to the uncompressed data, in a static buffer. + */ + char *const + atr_value(ATTR *atr) + { + return uncompress(atr_get_compressed_data(atr)); + } + + /** Return the uncompressed data for an attribute in a dynamic buffer. + * This is a wrapper function, to centralize the use of compression/ + * decompression on attributes. + * \param atr the attribute struct from which to get the data reference. + * \return a pointer to the uncompressed data, in a dynamic buffer. + */ + char * + safe_atr_value(ATTR *atr) + { + return safe_uncompress(atr_get_compressed_data(atr)); + } + + /*======================================================================*/ + + /** Migrate some number of attributes. + * The requested amount is only a guideline; the actual amount + * migrated will be more or less due to always migrating all the + * attributes on any given object together. + * \param amount the suggested number of attributes to migrate. + */ + void + atr_migrate(int amount) + { + static int start_obj = 0; + static chunk_reference_t **refs = NULL; + static int refs_size = 0; + int end_obj; + int actual; + ATTR *ptr; + + if (db_top == 0) + return; + + end_obj = start_obj; + actual = 0; + do { + for (ptr = List(end_obj); ptr; ptr = AL_NEXT(ptr)) + if (ptr->data != NULL_CHUNK_REFERENCE) + actual++; + end_obj = (end_obj + 1) % db_top; + } while (actual < amount && end_obj != start_obj); + + if (actual == 0) + return; + + if (!refs || actual > refs_size) { + if (refs) + mush_free((Malloc_t) refs, "migration reference array"); + refs = + (chunk_reference_t **) mush_malloc(actual * sizeof(chunk_reference_t *), + "migration reference array"); + refs_size = actual; + if (!refs) + panic("Could not allocate migration reference array"); + } + #ifdef DEBUG_MIGRATE + do_rawlog(LT_TRACE, "Migrate asked %d, actual objects #%d to #%d for %d", + amount, start_obj, (end_obj + db_top - 1) % db_top, actual); + #endif + + actual = 0; + do { + for (ptr = List(start_obj); ptr; ptr = AL_NEXT(ptr)) + if (ptr->data != NULL_CHUNK_REFERENCE) { + refs[actual] = &(ptr->data); + actual++; + } + start_obj = (start_obj + 1) % db_top; + } while (start_obj != end_obj); + + chunk_migration(actual, refs); + } *** 1_7_7.366/src/atr_tab.c Sat, 08 Feb 2003 17:33:23 -0600 dunemush (pennmush/c/41_atr_tab.c 1.33 660) --- 1_7_7.446(w)/src/atr_tab.c Thu, 29 May 2003 10:01:23 -0500 dunemush (pennmush/c/41_atr_tab.c 1.35 660) *************** *** 23,39 **** #include "parse.h" #include "confmagic.h" - typedef struct atr_alias ATRALIAS; /** An alias for an attribute. */ ! struct atr_alias { const char *alias; /**< The alias. */ const char *realname; /**< The attribute's canonical name. */ ! }; PTAB ptab_attrib; PRIV attr_privs[] = { {"no_command", '$', AF_NOPROG, AF_NOPROG}, {"no_inherit", 'i', AF_PRIVATE, AF_PRIVATE}, --- 23,40 ---- #include "parse.h" #include "confmagic.h" /** An alias for an attribute. */ ! typedef struct atr_alias { const char *alias; /**< The alias. */ const char *realname; /**< The attribute's canonical name. */ ! } ATRALIAS; + /** Prefix table for standard attribute names */ PTAB ptab_attrib; + /** Attribute flags */ PRIV attr_privs[] = { {"no_command", '$', AF_NOPROG, AF_NOPROG}, {"no_inherit", 'i', AF_PRIVATE, AF_PRIVATE}, *************** *** 174,180 **** return; } AL_NAME(ap) = strdup(name); ! AL_STR(ap) = NULL; } AL_FLAGS(ap) = flags; AL_CREATOR(ap) = player; --- 175,181 ---- return; } AL_NAME(ap) = strdup(name); ! ap->data = NULL_CHUNK_REFERENCE; } AL_FLAGS(ap) = flags; AL_CREATOR(ap) = player; *** 1_7_7.366/src/access.c Mon, 03 Feb 2003 12:43:48 -0600 dunemush (pennmush/c/43_access.c 1.11.1.2.1.4.1.2 660) --- 1_7_7.446(w)/src/access.c Thu, 29 May 2003 10:01:23 -0500 dunemush (pennmush/c/43_access.c 1.11.1.2.1.4.1.5 660) *************** *** 57,62 **** --- 57,63 ---- #include "config.h" #include "copyrite.h" #include + #include #include #include #ifdef I_SYS_TYPES *************** *** 113,119 **** }; static struct access *access_top; ! extern int reserved; /* reserved file descriptor */ static int add_access_node (const char *host, const dbref who, const int can, const int cant, const char *comment); --- 114,120 ---- }; static struct access *access_top; ! extern int reserved; /**< reserved file descriptor */ static int add_access_node (const char *host, const dbref who, const int can, const int cant, const char *comment); *************** *** 411,416 **** --- 412,418 ---- /** Display an access rule. * \param ap pointer to access rule. * \param rulenum access rule's number in the list. + * \param who unused. * \param buff buffer to store output. * \param bp pointer into buff. * This function provides an appealing display of an access rule *** 1_7_7.366/src/Makefile Mon, 31 Mar 2003 23:36:01 -0600 dunemush (pennmush/c/44_Makefile 1.21.1.13.1.6 700) --- 1_7_7.446(w)/src/Makefile Thu, 22 May 2003 11:38:27 -0500 dunemush (pennmush/c/44_Makefile 1.21.1.13.1.8 700) *************** *** 25,126 **** OUTFILES = buildinf netmud info_slave # List of C files, used for make depend: ! C_FILES=access.c atr_tab.c attrib.c boolexp.c bsd.c cmdlocal.c cmds.c \ ! command.c compress.c conf.c cque.c create.c db.c destroy.c \ ! extchat.c extmail.c filecopy.c flags.c flaglocal.c \ ! function.c funcrypt.c fundb.c funlist.c \ ! funlocal.c funmath.c funmisc.c funstr.c funtime.c funufun.c game.c \ ! help.c htab.c ident.c local.c lock.c log.c look.c mycrypt.c mymalloc.c \ ! malias.c match.c memcheck.c move.c mysocket.c notify.c parse.c pcre.c \ ! player.c plyrlist.c predicat.c privtab.c ptab.c rob.c services.c \ ! set.c shs.c sig.c speech.c strdup.c strtree.c strutil.c \ ! tables.c timer.c unparse.c utils.c version.c warnings.c wild.c wiz.c ! ! H_FILES = ../hdrs/attrib.h ../hdrs/conf.h ../hdrs/copyrite.h ../hdrs/mushdb.h \ ! ../hdrs/flags.h ../hdrs/externs.h ../hdrs/case.h \ ! ../hdrs/help.h ../hdrs/log.h ../hdrs/boolexp.h ../hdrs/match.h \ ! ../hdrs/lock.h ../hdrs/mymalloc.h ../hdrs/ident.h \ ! ../hdrs/memcheck.h ../options.h ../hdrs/log.h \ ! ../hdrs/game.h ../hdrs/atr_tab.h ../hdrs/dbdefs.h ../hdrs/ansi.h \ ! ../hdrs/version.h ../hdrs/getpgsiz.h ../hdrs/case.h \ ! ../config.h ../confmagic.h ../hdrs/function.h ../hdrs/parse.h \ ! ../hdrs/extmail.h ../hdrs/malias.h ../hdrs/extchat.h \ ! ../hdrs/htab.h ../hdrs/command.h ../hdrs/htab.h ../hdrs/privtab.h \ ! ../hdrs/strtree.h ../hdrs/pcre.h ../hdrs/mysocket.h ../hdrs/mushtype.h \ ! ../hdrs/boolexp.h ../hdrs/ptab.h # .o versions of above - these are used in the build ! O_FILES= \ ! access.o \ ! atr_tab.o \ ! attrib.o \ ! boolexp.o \ ! bsd.o \ ! cmdlocal.o \ ! cmds.o \ ! command.o \ ! compress.o \ ! conf.o \ ! cque.o \ ! create.o \ ! db.o \ ! destroy.o \ ! extchat.o \ ! extmail.o \ ! filecopy.o \ ! flags.o \ ! flaglocal.o \ ! function.o \ ! funcrypt.o \ ! fundb.o \ ! funlist.o \ ! funlocal.o \ ! funmath.o \ ! funmisc.o \ ! funstr.o \ ! funtime.o \ ! funufun.o \ ! game.o \ ! help.o \ ! htab.o \ ! ident.o \ ! local.o \ ! lock.o \ ! log.o \ ! look.o \ ! mycrypt.o \ ! mymalloc.o \ ! malias.o \ ! match.o \ ! memcheck.o \ ! move.o \ ! mysocket.o \ ! notify.o \ ! parse.o \ ! pcre.o \ ! player.o \ ! plyrlist.o \ ! predicat.o \ ! privtab.o \ ! ptab.o \ ! rob.o \ ! services.o \ ! set.o \ ! shs.o \ ! sig.o \ ! speech.o \ ! strdup.o \ ! strtree.o \ ! strutil.o \ ! tables.o \ ! timer.o \ ! unparse.o \ ! utils.o \ ! version.o \ ! warnings.o \ ! wild.o \ ! wiz.o ! # This is a dummy target, in case you type 'make' in the source # directory (likely for emacs users who M-x compile.) --- 25,65 ---- OUTFILES = buildinf netmud info_slave # List of C files, used for make depend: ! C_FILES=access.c atr_tab.c attrib.c boolexp.c bsd.c chunk.c cmdlocal.c cmds.c \ ! command.c compress.c conf.c cque.c create.c db.c destroy.c extchat.c \ ! extmail.c filecopy.c flaglocal.c flags.c funcrypt.c function.c \ ! fundb.c funlist.c funlocal.c funmath.c funmisc.c funstr.c funtime.c \ ! funufun.c game.c help.c htab.c ident.c local.c lock.c log.c look.c \ ! malias.c match.c memcheck.c move.c mycrypt.c mymalloc.c mysocket.c \ ! notify.c parse.c pcre.c player.c plyrlist.c predicat.c privtab.c \ ! ptab.c rob.c services.c set.c shs.c sig.c speech.c strdup.c strtree.c \ ! strutil.c tables.c timer.c unparse.c utils.c version.c warnings.c \ ! wild.c wiz.c ! ! H_FILES = ../config.h ../confmagic.h ../hdrs/ansi.h ../hdrs/atr_tab.h \ ! ../hdrs/attrib.h ../hdrs/boolexp.h ../hdrs/boolexp.h ../hdrs/case.h \ ! ../hdrs/case.h ../hdrs/chunk.h ../hdrs/command.h ../hdrs/conf.h \ ! ../hdrs/copyrite.h ../hdrs/dbdefs.h ../hdrs/extchat.h \ ! ../hdrs/externs.h ../hdrs/extmail.h ../hdrs/flags.h \ ! ../hdrs/function.h ../hdrs/game.h ../hdrs/getpgsiz.h ../hdrs/help.h \ ! ../hdrs/htab.h ../hdrs/htab.h ../hdrs/ident.h ../hdrs/lock.h \ ! ../hdrs/log.h ../hdrs/log.h ../hdrs/malias.h ../hdrs/match.h \ ! ../hdrs/memcheck.h ../hdrs/mushdb.h ../hdrs/mushtype.h \ ! ../hdrs/mymalloc.h ../hdrs/mysocket.h ../hdrs/parse.h \ ! ../hdrs/pcre.h ../hdrs/privtab.h ../hdrs/ptab.h ../hdrs/strtree.h \ ! ../hdrs/version.h ../options.h # .o versions of above - these are used in the build ! O_FILES=access.o atr_tab.o attrib.o boolexp.o bsd.o chunk.o cmdlocal.o cmds.o \ ! command.o compress.o conf.o cque.o create.o db.o destroy.o extchat.o \ ! extmail.o filecopy.o flaglocal.o flags.o funcrypt.o function.o \ ! fundb.o funlist.o funlocal.o funmath.o funmisc.o funstr.o funtime.o \ ! funufun.o game.o help.o htab.o ident.o local.o lock.o log.o look.o \ ! malias.o match.o memcheck.o move.o mycrypt.o mymalloc.o mysocket.o \ ! notify.o parse.o pcre.o player.o plyrlist.o predicat.o privtab.o \ ! ptab.o rob.o services.o set.o shs.o sig.o speech.o strdup.o strtree.o \ ! strutil.o tables.o timer.o unparse.o utils.o version.o warnings.o \ ! wild.o wiz.o # This is a dummy target, in case you type 'make' in the source # directory (likely for emacs users who M-x compile.) *************** *** 142,152 **** # We recompile mysocket.c instead of reusing mysocket.o because we # want to do some error handing differently for info_slave. ! info_slave: info_slave.c ident.o strdup.o sig.o mysocket.c @echo "Making info_slave." $(CC) $(CCFLAGS) $(IDEFS) -c info_slave.c $(CC) $(LDFLAGS) $(CCFLAGS) -DINFOSLAVE -o info_slave info_slave.o \ ! ident.o strdup.o sig.o mysocket.c $(LIBS) # ../hdrs/buildinf.h contains build information used by version.c: # time/date of build and CFLAGS --- 81,91 ---- # We recompile mysocket.c instead of reusing mysocket.o because we # want to do some error handing differently for info_slave. ! info_slave: info_slave.c ident.o strdup.o sig.o mymalloc.o mysocket.c @echo "Making info_slave." $(CC) $(CCFLAGS) $(IDEFS) -c info_slave.c $(CC) $(LDFLAGS) $(CCFLAGS) -DINFOSLAVE -o info_slave info_slave.o \ ! ident.o strdup.o sig.o mymalloc.o mysocket.c $(LIBS) # ../hdrs/buildinf.h contains build information used by version.c: # time/date of build and CFLAGS *************** *** 237,243 **** -T __ptr_t -T __malloc_size_t -T HASHENT -T HASHTAB -T COMMAND_INFO \ -T CHANLIST -T Malloc_t -T Free_t -T MATH -T socklen_t \ -T StrNode -T IDENT -T Size_t -T Port_t -T help_file -T PTAB \ ! -T SHS_INFO -T ansi_string $$file ; done) clean: -rm -f *.o --- 176,182 ---- -T __ptr_t -T __malloc_size_t -T HASHENT -T HASHTAB -T COMMAND_INFO \ -T CHANLIST -T Malloc_t -T Free_t -T MATH -T socklen_t \ -T StrNode -T IDENT -T Size_t -T Port_t -T help_file -T PTAB \ ! -T SHS_INFO -T ansi_string -T MAIL $$file ; done) clean: -rm -f *.o *** 1_7_7.366/hdrs/csrimalloc.h Sun, 01 Dec 2002 21:14:41 -0600 dunemush (pennmush/c/45_csrimalloc 1.6 660) --- 1_7_7.446(w)/hdrs/csrimalloc.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/c/45_csrimalloc 1.8 660) *************** *** 21,57 **** #ifdef CSRI_TRACE /* Tracing malloc definitions - helps find leaks */ ! extern univptr_t __malloc _((size_t nbytes, const char *fname, int linenum)); ! extern univptr_t __calloc _((size_t nelem, size_t elsize, const char *fname, int linenum)); ! extern univptr_t __realloc _((univptr_t cp, size_t nbytes, const char *fname, int linenum)); ! extern univptr_t __valloc _((size_t size, const char *fname, int linenum)); ! extern univptr_t __memalign _((size_t alignment, size_t size, const char *fname, int linenum)); ! extern univptr_t __emalloc _((size_t nbytes, const char *fname, int linenum)); ! extern univptr_t __ecalloc _((size_t nelem, size_t sz, const char *fname, int linenum)); ! extern univptr_t __erealloc _((univptr_t ptr, size_t nbytes, const char *fname, int linenum)); ! extern char *__strdup _((const char *s, const char *fname, int linenum)); ! extern char *__strsave _((const char *s, const char *fname, int linenum)); ! extern void __free _((univptr_t cp, const char *fname, int linenum)); ! extern void __cfree _((univptr_t cp, const char *fname, int linenum)); ! #define malloc(x) __malloc((x), __FILE__, __LINE__) ! #define calloc(x, n) __calloc((x), (n), __FILE__, __LINE__) ! #define realloc(p, x) __realloc((p), (x), __FILE__, __LINE__) ! #define memalign(x, n) __memalign((x), (n), __FILE__, __LINE__) ! #define valloc(x) __valloc((x), __FILE__, __LINE__) ! #define emalloc(x) __emalloc((x), __FILE__, __LINE__) ! #define ecalloc(x, n) __ecalloc((x), (n), __FILE__, __LINE__) ! #define erealloc(p, x) __erealloc((p), (x), __FILE__, __LINE__) ! #define strdup(p) __strdup((p), __FILE__, __LINE__) ! #define strsave(p) __strsave((p), __FILE__, __LINE__) /* cfree and free are identical */ ! #define cfree(p) __free((p), __FILE__, __LINE__) ! #define free(p) __free((p), __FILE__, __LINE__) #else /* CSRI_TRACE */ --- 21,59 ---- #ifdef CSRI_TRACE /* Tracing malloc definitions - helps find leaks */ ! extern univptr_t trace__malloc ! _((size_t nbytes, const char *fname, int linenum)); ! extern univptr_t trace__calloc _((size_t nelem, size_t elsize, const char *fname, int linenum)); ! extern univptr_t trace__realloc _((univptr_t cp, size_t nbytes, const char *fname, int linenum)); ! extern univptr_t trace__valloc _((size_t size, const char *fname, int linenum)); ! extern univptr_t trace__memalign _((size_t alignment, size_t size, const char *fname, int linenum)); ! extern univptr_t trace__emalloc ! _((size_t nbytes, const char *fname, int linenum)); ! extern univptr_t trace__ecalloc _((size_t nelem, size_t sz, const char *fname, int linenum)); ! extern univptr_t trace__erealloc _((univptr_t ptr, size_t nbytes, const char *fname, int linenum)); ! extern char *trace__strdup _((const char *s, const char *fname, int linenum)); ! extern char *trace__strsave _((const char *s, const char *fname, int linenum)); ! extern void trace__free _((univptr_t cp, const char *fname, int linenum)); ! extern void trace__cfree _((univptr_t cp, const char *fname, int linenum)); ! #define malloc(x) trace__malloc((x), __FILE__, __LINE__) ! #define calloc(x, n) trace__calloc((x), (n), __FILE__, __LINE__) ! #define realloc(p, x) trace__realloc((p), (x), __FILE__, __LINE__) ! #define memalign(x, n) trace__memalign((x), (n), __FILE__, __LINE__) ! #define valloc(x) trace__valloc((x), __FILE__, __LINE__) ! #define emalloc(x) trace__emalloc((x), __FILE__, __LINE__) ! #define ecalloc(x, n) trace__ecalloc((x), (n), __FILE__, __LINE__) ! #define erealloc(p, x) trace__erealloc((p), (x), __FILE__, __LINE__) ! #define strdup(p) trace__strdup((p), __FILE__, __LINE__) ! #define strsave(p) trace__strsave((p), __FILE__, __LINE__) /* cfree and free are identical */ ! #define cfree(p) trace__free((p), __FILE__, __LINE__) ! #define free(p) trace__free((p), __FILE__, __LINE__) #else /* CSRI_TRACE */ *** 1_7_7.366/hdrs/version.h Wed, 09 Apr 2003 14:28:37 -0500 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.17 660) --- 1_7_7.446(w)/hdrs/version.h Thu, 29 May 2003 10:01:50 -0500 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.18 660) *************** *** 1,3 **** ! #define VERSION "PennMUSH version 1.7.7 patchlevel 13 [04/09/2003]" ! #define SHORTVN "PennMUSH 1.7.7p13" ! #define NUMVERSION 001007007013 --- 1,3 ---- ! #define VERSION "PennMUSH version 1.7.7 patchlevel 14 [05/22/2003]" ! #define SHORTVN "PennMUSH 1.7.7p14" ! #define NUMVERSION 001007007014 *** 1_7_7.366/hdrs/parse.h Thu, 27 Mar 2003 11:21:38 -0600 dunemush (pennmush/c/51_parse.h 1.10.1.10 660) --- 1_7_7.446(w)/hdrs/parse.h Thu, 29 May 2003 10:01:48 -0500 dunemush (pennmush/c/51_parse.h 1.10.1.11 660) *************** *** 86,91 **** --- 86,92 ---- typedef struct fun FUN; #define HAVE_FUN_DEFINED #endif + /** Common declaration for softcode function implementations */ #define FUNCTION(fun_name) \ /* ARGSUSED */ /* try to keep lint happy */ \ void fun_name (FUN *fun, char *buff, char **bp, int nargs, char *args[], \ *** 1_7_7.366/hdrs/mymalloc.h Wed, 24 Jan 2001 16:56:43 -0600 dunemush (pennmush/d/1_mymalloc.h 1.2 660) --- 1_7_7.446(w)/hdrs/mymalloc.h Thu, 29 May 2003 10:01:47 -0500 dunemush (pennmush/d/1_mymalloc.h 1.3 660) *************** *** 24,33 **** --- 24,35 ---- #if (MALLOC_PACKAGE == 1) #define CSRI #elif (MALLOC_PACKAGE == 2) + #include #define CSRI #define CSRI_TRACE #define CSRI_PROFILESIZES #define CSRI_DEBUG + #include "csrimalloc.h" #endif #endif /* _MYMALLOC_H */ *** 1_7_7.366/hdrs/extmail.h Thu, 30 Jan 2003 13:16:01 -0600 dunemush (pennmush/d/15_extmail.h 1.6.1.7 660) --- 1_7_7.446(w)/hdrs/extmail.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/15_extmail.h 1.6.1.8 660) *************** *** 68,73 **** --- 68,76 ---- /* Database ends with ***END OF DUMP*** not *** END OF DUMP *** */ #define MDBF_NEW_EOD 0x4 + /* Database contains sender ctimes */ + #define MDBF_SENDERCTIME 0x8 + /* From extmail.c */ extern struct mail *maildb; extern void set_player_folder(dbref player, int fnum); *************** *** 87,97 **** extern void do_mail_list(dbref player, const char *msglist); extern void do_mail_read(dbref player, char *msglist); extern void do_mail_clear(dbref player, const char *msglist); ! extern void do_mail_unclear(dbref player, char *msglist); extern void do_mail_purge(dbref player); extern void do_mail_file(dbref player, char *msglist, char *folder); ! extern void do_mail_tag(dbref player, char *msglist); ! extern void do_mail_untag(dbref player, char *msglist); extern void do_mail_fwd(dbref player, char *msglist, char *tolist); extern void do_mail_send (dbref player, char *tolist, char *message, mail_flag flags, --- 90,100 ---- extern void do_mail_list(dbref player, const char *msglist); extern void do_mail_read(dbref player, char *msglist); extern void do_mail_clear(dbref player, const char *msglist); ! extern void do_mail_unclear(dbref player, const char *msglist); extern void do_mail_purge(dbref player); extern void do_mail_file(dbref player, char *msglist, char *folder); ! extern void do_mail_tag(dbref player, const char *msglist); ! extern void do_mail_untag(dbref player, const char *msglist); extern void do_mail_fwd(dbref player, char *msglist, char *tolist); extern void do_mail_send (dbref player, char *tolist, char *message, mail_flag flags, *** 1_7_7.366/hdrs/externs.h Sat, 05 Apr 2003 17:37:13 -0600 dunemush (pennmush/d/16_externs.h 1.1.1.53.1.2.1.8.2.1.1.2.1.1.1.1.1.2.1.6.1.3.1.4.3.1.1.1.1.2 660) --- 1_7_7.446(w)/hdrs/externs.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/16_externs.h 1.1.1.53.1.2.1.8.2.1.1.2.1.1.1.1.1.2.1.6.1.3.1.4.3.1.1.1.1.9 660) *************** *** 1,4 **** ! /* externs.h */ #ifndef __EXTERNS_H #define __EXTERNS_H --- 1,10 ---- ! /** ! * \file externs.h ! * ! * \brief Header file for external functions called from many source files. ! * ! * ! */ #ifndef __EXTERNS_H #define __EXTERNS_H *************** *** 8,14 **** --- 14,22 ---- #include #endif #if defined(HAS_GETTEXT) && !defined(DONT_TRANSLATE) + /** Macro for a translated string */ #define T(str) gettext(str) + /** Macro to note that a string has a translation but not to translate */ #define N_(str) gettext_noop(str) #else #define T(str) str *************** *** 60,66 **** #ifdef WIN32 /* From timer.c */ extern void init_timer(void); - extern void kill_timer(void); #endif /* WIN32 */ /* From bsd.c */ --- 68,73 ---- *************** *** 72,77 **** --- 79,85 ---- extern int restarting; #ifdef SUN_OS extern int f_close(FILE * file); + /** SunOS fclose macro */ #define fclose(f) f_close(f); #endif extern int hidden(dbref player); *************** *** 87,95 **** #define NA_PUPPET 0x0040 /* Ok to puppet */ #define NA_PUPPET2 0x0080 /* Message to a player from a puppet */ #define NA_MUST_PUPPET 0x0100 /* Ok to puppet even in same room */ ! #define NA_INTERACTION 0x0200 /* Message follows interaction rules */ ! #define NA_INTER_DIDIT 0x0400 /* Message is from @verb or ilk */ typedef dbref (*na_lookup) (dbref, void *); extern void notify_anything(dbref speaker, na_lookup func, void *fdata, --- 95,106 ---- #define NA_PUPPET 0x0040 /* Ok to puppet */ #define NA_PUPPET2 0x0080 /* Message to a player from a puppet */ #define NA_MUST_PUPPET 0x0100 /* Ok to puppet even in same room */ ! #define NA_INTER_HEAR 0x0200 /* Message is auditory in nature */ ! #define NA_INTER_SEE 0x0400 /* Message is visual in nature */ ! #define NA_INTER_PRESENCE 0x0800 /* Message is about presence */ ! #define NA_INTERACTION (NA_INTER_HEAR|NA_INTER_SEE|NA_INTER_PRESENCE) /* Message follows interaction rules */ + /** A notify_anything lookup function type definition */ typedef dbref (*na_lookup) (dbref, void *); extern void notify_anything(dbref speaker, na_lookup func, void *fdata, *************** *** 171,176 **** --- 182,188 ---- extern void fork_and_dump(int forking); /* From look.c */ + /** Enumeration of types of looks that can be performed */ enum look_type { LOOK_NORMAL, LOOK_TRANS, LOOK_AUTO, LOOK_CLOUDYTRANS, LOOK_CLOUDY }; *************** *** 181,186 **** --- 193,199 ---- /* From move.c */ extern void enter_room(dbref player, dbref loc, int nomovemsgs); extern int can_move(dbref player, const char *direction); + /** Enumeration of types of movements that can be performed */ enum move_type { MOVE_NORMAL, MOVE_GLOBAL, MOVE_ZONE }; extern void do_move(dbref player, const char *direction, enum move_type type); extern void moveto(dbref what, dbref where); *************** *** 228,241 **** extern void did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc); extern void real_did_it(dbref player, dbref thing, const char *what, ! const char *def, const char *owhat, ! const char *odef, const char *awhat, dbref loc, ! char *myenv[10]); extern int can_see(dbref player, dbref thing, int can_see_loc); extern int controls(dbref who, dbref what); extern int can_pay_fees(dbref who, int pennies); ! extern void giveto(dbref who, dbref pennies); extern int payfor(dbref who, int cost); extern int nearby(dbref obj1, dbref obj2); #ifdef QUOTA --- 241,258 ---- extern void did_it(dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc); + extern void did_it_interact(dbref player, dbref thing, const char *what, + const char *def, const char *owhat, + const char *odef, const char *awhat, dbref loc, + int flags); extern void real_did_it(dbref player, dbref thing, const char *what, ! const char *def, const char *owhat, const char *odef, ! const char *awhat, dbref loc, char *myenv[10], ! int flags); extern int can_see(dbref player, dbref thing, int can_see_loc); extern int controls(dbref who, dbref what); extern int can_pay_fees(dbref who, int pennies); ! extern void giveto(dbref who, int pennies); extern int payfor(dbref who, int cost); extern int nearby(dbref obj1, dbref obj2); #ifdef QUOTA *************** *** 264,270 **** extern void notify_except(dbref first, dbref exception, const char *msg, int flags); extern void notify_except2(dbref first, dbref exc1, dbref exc2, ! const char *msg); /* Return thing/PREFIX + msg */ extern void make_prefixstr(dbref thing, const char *msg, char *tbuf1); extern int filter_found(dbref thing, const char *msg, int flag); --- 281,287 ---- extern void notify_except(dbref first, dbref exception, const char *msg, int flags); extern void notify_except2(dbref first, dbref exc1, dbref exc2, ! const char *msg, int flags); /* Return thing/PREFIX + msg */ extern void make_prefixstr(dbref thing, const char *msg, char *tbuf1); extern int filter_found(dbref thing, const char *msg, int flag); *************** *** 289,296 **** strdup(const char *s) __attribute_malloc__; #endif ! char *mush_strdup(const char *s, const char *check) ! __attribute_malloc__; #ifdef WIN32 #ifndef HAS_VSNPRINTF #define HAS_VSNPRINTF --- 306,312 ---- strdup(const char *s) __attribute_malloc__; #endif ! char *mush_strdup(const char *s, const char *check) __attribute_malloc__; #ifdef WIN32 #ifndef HAS_VSNPRINTF #define HAS_VSNPRINTF *************** *** 299,308 **** #endif extern char *remove_markup(const char *orig, size_t * stripped_len); extern char *skip_leading_ansi(const char *s); typedef struct { ! char text[BUFFER_LEN]; ! char *codes[BUFFER_LEN]; ! size_t len; } ansi_string; --- 315,326 ---- #endif extern char *remove_markup(const char *orig, size_t * stripped_len); extern char *skip_leading_ansi(const char *s); + + /** A string, with ansi attributes broken out from the text */ typedef struct { ! char text[BUFFER_LEN]; /**< Text of the string */ ! char *codes[BUFFER_LEN]; /**< Ansi codes associated with each char of text */ ! size_t len; /**< Length of text */ } ansi_string; *************** *** 374,383 **** const char *conjoin, const char *space, char *buff, char **bp); typedef struct { ! const char *base; ! const char *entity; } accent_info; extern accent_info accent_table[]; extern int ansi_strlen(const char *string); --- 392,403 ---- const char *conjoin, const char *space, char *buff, char **bp); + /** This structure associates html entities and base ascii representations */ typedef struct { ! const char *base; /**< Base ascii representation */ ! const char *entity; /**< HTML entity */ } accent_info; + extern accent_info accent_table[]; extern int ansi_strlen(const char *string); *************** *** 436,447 **** #define wild_match(s,d) wild_match_case(s,d,0) #define local_wild_match(s,d) local_wild_match_case(s, d, 0) ! /* From function.c and other fun*.c */ typedef enum list_type { ALPHANUM_LIST, NUMERIC_LIST, DBREF_LIST, FLOAT_LIST, INSENS_ALPHANUM_LIST, UNKNOWN_LIST } list_type; ! extern char *rptr[10]; extern char *strip_braces(char const *line); extern void save_global_regs(const char *funcname, char *preserve[]); extern void restore_global_regs(const char *funcname, char *preserve[]); --- 456,467 ---- #define wild_match(s,d) wild_match_case(s,d,0) #define local_wild_match(s,d) local_wild_match_case(s, d, 0) ! /** Types of lists, based on what their elements are */ typedef enum list_type { ALPHANUM_LIST, NUMERIC_LIST, DBREF_LIST, FLOAT_LIST, INSENS_ALPHANUM_LIST, UNKNOWN_LIST } list_type; ! /* From function.c and other fun*.c */ extern char *strip_braces(char const *line); extern void save_global_regs(const char *funcname, char *preserve[]); extern void restore_global_regs(const char *funcname, char *preserve[]); *************** *** 492,500 **** void local_flags(void); /* funlist.c */ ! void do_gensort(char **s, int n, list_type sort_type); /* sig.c */ typedef void (*Sigfunc) (int); /* Set up a signal handler. Use instead of signal() */ Sigfunc install_sig_handler(int signo, Sigfunc f); --- 512,521 ---- void local_flags(void); /* funlist.c */ ! void do_gensort(char *s[], int n, list_type sort_type); /* sig.c */ + /** Type definition for signal handlers */ typedef void (*Sigfunc) (int); /* Set up a signal handler. Use instead of signal() */ Sigfunc install_sig_handler(int signo, Sigfunc f); *** 1_7_7.366/hdrs/extchat.h Sun, 16 Mar 2003 16:05:04 -0600 dunemush (pennmush/d/17_extchat.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.3.1.1.1.1.2.2.1.6 660) --- 1_7_7.446(w)/hdrs/extchat.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/17_extchat.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.3.1.1.1.1.2.2.1.8 660) *************** *** 111,116 **** --- 111,123 ---- #define Chanlist(x) ((struct chanlist *)get_objdata(x, "CHANNELS")) #define s_Chanlist(x, y) set_objdata(x, "CHANNELS", (void *)y) + /** A structure for passing channel data to notify_anything */ + struct na_cpass { + CHANUSER *u; /**< Pointer to channel user */ + int checkquiet; /**< Should quiet property be checked? */ + }; + + /* Channel type flags and macros */ #define CHANNEL_PLAYER 0x1 /* Players may join */ #define CHANNEL_OBJECT 0x2 /* Objects may join */ *** 1_7_7.366/hdrs/dbdefs.h Wed, 26 Mar 2003 14:57:24 -0600 dunemush (pennmush/d/18_dbdefs.h 1.1.1.1.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.2.1.1.12 660) --- 1_7_7.446(w)/hdrs/dbdefs.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/18_dbdefs.h 1.1.1.1.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.2.1.1.15 660) *************** *** 14,36 **** extern dbref first_free; /* pointer to free list */ - /* Some stuff we moved from option.h, of interest to people trying - * to use really old databases or code. - */ - - /* With this option, '& ;' will delete an attribute, - * otherwise it will set the attribute empty. Recommended, unless - * emulating pre-1.6.9 behavior. - */ - #define DELETE_ATTRS /* */ - - /* With this option, empty attributes will be visible with hasattr(), - * lattr(), grep(), etc. Also controls whether empty attrubutes stop - * searches through parent chains. Recommended, unless emulating - * pre-1.6.9 behavior. - */ - #define VISIBLE_EMPTY_ATTRS /* */ - /*------------------------------------------------------------------------- * Database access macros */ --- 14,19 ---- *************** *** 163,168 **** --- 146,152 ---- #define NoCommand(x) (has_flag_by_name(x, "NO_COMMAND", NOTYPE)) #define NoWarn(x) (has_flag_by_name(x, "NOWARN", NOTYPE)) #define Opaque(x) (has_flag_by_name(x, "OPAQUE", NOTYPE)) + #define Orphan(x) (has_flag_by_name(x, "ORPHAN", NOTYPE)) #define Quiet(x) (has_flag_by_name(x, "QUIET", NOTYPE)) #define Safe(x) (has_flag_by_name(x, "SAFE", NOTYPE)) #define Sticky(x) (has_flag_by_name(x, "STICKY", NOTYPE)) *************** *** 200,205 **** --- 184,196 ---- #define Inheritable(x) (IsPlayer(x) || Inherit(x) || \ Inherit(Owner(x)) || Wizard(x)) + /* Ancestor_Parent() - returns appropriate ancestor object */ + #define Ancestor_Parent(x) (Orphan(x) ? NOTHING : \ + (IsRoom(x) ? ANCESTOR_ROOM : \ + (IsExit(x) ? ANCESTOR_EXIT : \ + (IsPlayer(x) ? ANCESTOR_PLAYER : \ + (IsThing(x) ? ANCESTOR_THING : NOTHING ))))) + /*-------------------------------------------------------------------------- * Other db stuff *************** *** 283,294 **** --- 274,288 ---- struct mail *prev; /**< Pointer to previous message */ dbref to; /**< Recipient dbref */ dbref from; /**< Sender's dbref */ + time_t from_ctime; /**< Sender's creation time */ unsigned char *message; /**< Message text, compressed */ unsigned char *time; /**< Message date/time, compressed */ unsigned char *subject; /**< Message subject, compressed */ int read; /**< Bitflags of message status */ }; + typedef struct mail MAIL; + #endif /* USE_MAILER */ extern const char *EOD; *** 1_7_7.366/hdrs/conf.h Sat, 22 Feb 2003 13:49:12 -0600 dunemush (pennmush/d/20_conf.h 1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.5.1.1.1.1.1.1.1.1.1.2.1.1.2.1.2.13.1.4.1.11 660) --- 1_7_7.446(w)/hdrs/conf.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/20_conf.h 1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.5.1.1.1.1.1.1.1.1.1.2.1.1.2.1.2.13.1.4.1.14 660) *************** *** 90,95 **** --- 90,99 ---- char mail_db[256]; /**< Name of the mail database file */ dbref player_start; /**< The room in which new players are created */ dbref master_room; /**< The master room for global commands/exits */ + dbref ancestor_room; /**< The ultimate parent room */ + dbref ancestor_exit; /**< The ultimate parent exit */ + dbref ancestor_thing; /**< The ultimate parent thing */ + dbref ancestor_player; /**< The ultimate parent player */ int idle_timeout; /**< Maximum idle time allowed, in minutes */ int unconnected_idle_timeout; /**< Maximum idle time for connections without dbrefs, in minutes */ int dump_interval; /**< Interval between database dumps, in seconds */ *************** *** 149,154 **** --- 153,159 ---- int warn_interval; /**< Interval between warning checks */ time_t warn_counter; /**< Time since last warning check */ dbref base_room; /**< Room which floating checks consider as the base */ + dbref default_home; /**< Home for the homeless */ int use_dns; /**< Should we use DNS lookups? */ int haspower_restricted; /**< Should haspower() be restricted to see_all? */ int safer_ufun; /**< Should we require security for ufun calls? */ *************** *** 222,227 **** --- 227,236 ---- int queue_entry_cpu_time; /**< Maximum cpu time allowed per queue entry */ int ascii_names; /**< Are object names restricted to ascii characters? */ int max_global_fns; /**< Maximum number of functions */ + char chunk_swap_file[256]; /**< Name of the attribute swap file */ + int chunk_cache_memory; /**< Memory to use for the attribute cache */ + int chunk_migrate_inactive; /**< Number of attrs to migrate when inactive */ + int chunk_migrate_active; /**< Number of attrs to migrate when active */ }; extern OPTTAB options; *************** *** 254,259 **** --- 263,272 ---- #define SSLPORT (options.sslport) #define PLAYER_START (options.player_start) #define MASTER_ROOM (options.master_room) + #define ANCESTOR_ROOM (options.ancestor_room) + #define ANCESTOR_EXIT (options.ancestor_exit) + #define ANCESTOR_THING (options.ancestor_thing) + #define ANCESTOR_PLAYER (options.ancestor_player) #define MONEY (options.money_singular) #define MONIES (options.money_plural) #define WHISPER_LOUDNESS (options.whisper_loudness) *************** *** 274,279 **** --- 287,293 ---- #define DEF_DB_OUT (options.output_db) #define BASE_ROOM (options.base_room) + #define DEFAULT_HOME (options.default_home) #define PURGE_INTERVAL (options.purge_interval) #define DBCK_INTERVAL (options.dbck_interval) *************** *** 349,354 **** --- 363,373 ---- #define ONLY_ASCII_NAMES (options.ascii_names) #define MAX_GLOBAL_FNS (options.max_global_fns) + #define CHUNK_SWAP_FILE (options.chunk_swap_file) + #define CHUNK_CACHE_MEMORY (options.chunk_cache_memory) + #define CHUNK_MIGRATE_INACTIVE (options.chunk_migrate_inactive) + #define CHUNK_MIGRATE_ACTIVE (options.chunk_migrate_active) + /* Compiler-specific stuff. */ #ifndef __GNUC_PREREQ *** 1_7_7.366/hdrs/switches.h Sun, 16 Mar 2003 16:05:04 -0600 dunemush (pennmush/d/21_switches.h 1.2.1.6.2.7.1.2 660) --- 1_7_7.446(w)/hdrs/switches.h Thu, 29 May 2003 10:01:49 -0500 dunemush (pennmush/d/21_switches.h 1.2.1.6.2.7.1.3 660) *************** *** 13,134 **** #define SWITCH_BUFFER 12 #define SWITCH_CHECK 13 #define SWITCH_CHOWN 14 ! #define SWITCH_CLEAR 15 ! #define SWITCH_CMD 16 ! #define SWITCH_COMMANDS 17 ! #define SWITCH_CONN 18 ! #define SWITCH_CONNECT 19 ! #define SWITCH_CONNECTED 20 ! #define SWITCH_CONTENTS 21 ! #define SWITCH_COSTS 22 ! #define SWITCH_COUNT 23 ! #define SWITCH_CREATE 24 ! #define SWITCH_DATABASE 25 ! #define SWITCH_DB 26 ! #define SWITCH_DEBUG 27 ! #define SWITCH_DECOMPILE 28 ! #define SWITCH_DEFAULTS 29 ! #define SWITCH_DELETE 30 ! #define SWITCH_DELIMIT 31 ! #define SWITCH_DESCRIBE 32 ! #define SWITCH_DESTROY 33 ! #define SWITCH_DISABLE 34 ! #define SWITCH_DOWN 35 ! #define SWITCH_DSTATS 36 ! #define SWITCH_EMIT 37 ! #define SWITCH_ENABLE 38 ! #define SWITCH_ERR 39 ! #define SWITCH_EXITS 40 ! #define SWITCH_FILE 41 ! #define SWITCH_FIRST 42 ! #define SWITCH_FLAGS 43 ! #define SWITCH_FOLDERS 44 ! #define SWITCH_FORWARD 45 ! #define SWITCH_FSTATS 46 ! #define SWITCH_FULL 47 ! #define SWITCH_FUNCTIONS 48 ! #define SWITCH_FWD 49 ! #define SWITCH_GAG 50 ! #define SWITCH_GLOBALS 51 ! #define SWITCH_HEADER 52 ! #define SWITCH_HERE 53 ! #define SWITCH_HIDE 54 ! #define SWITCH_ILIST 55 ! #define SWITCH_INVENTORY 56 ! #define SWITCH_IPRINT 57 ! #define SWITCH_JOIN 58 ! #define SWITCH_LIST 59 ! #define SWITCH_LOWERCASE 60 ! #define SWITCH_ME 61 ! #define SWITCH_MEMBERS 62 ! #define SWITCH_MOD 63 ! #define SWITCH_MORTAL 64 ! #define SWITCH_MOTD 65 ! #define SWITCH_MUTE 66 ! #define SWITCH_NAME 67 ! #define SWITCH_NO 68 ! #define SWITCH_NOEVAL 69 ! #define SWITCH_NOFLAGCOPY 70 ! #define SWITCH_NOISY 71 ! #define SWITCH_NOSIG 72 ! #define SWITCH_NOSPACE 73 ! #define SWITCH_NOTIFY 74 ! #define SWITCH_NUKE 75 ! #define SWITCH_OFF 76 ! #define SWITCH_ON 77 ! #define SWITCH_OUTSIDE 78 ! #define SWITCH_OVERRIDE 79 ! #define SWITCH_PANIC 80 ! #define SWITCH_PARANOID 81 ! #define SWITCH_PLAYERS 82 ! #define SWITCH_PORT 83 ! #define SWITCH_PRESERVE 84 ! #define SWITCH_PRINT 85 ! #define SWITCH_PRIVS 86 ! #define SWITCH_PURGE 87 ! #define SWITCH_QUICK 88 ! #define SWITCH_QUIET 89 ! #define SWITCH_READ 90 ! #define SWITCH_REBOOT 91 ! #define SWITCH_RECALL 92 ! #define SWITCH_REGISTER 93 ! #define SWITCH_REMOVE 94 ! #define SWITCH_RENAME 95 ! #define SWITCH_RESTORE 96 ! #define SWITCH_RESTRICT 97 ! #define SWITCH_RETROACTIVE 98 ! #define SWITCH_ROOM 99 ! #define SWITCH_ROOMS 100 ! #define SWITCH_SEE 101 ! #define SWITCH_SEEFLAG 102 ! #define SWITCH_SELF 103 ! #define SWITCH_SEND 104 ! #define SWITCH_SET 105 ! #define SWITCH_SILENT 106 ! #define SWITCH_SKIPDEFAULTS 107 ! #define SWITCH_SPEAK 108 ! #define SWITCH_STATS 109 ! #define SWITCH_SUMMARY 110 ! #define SWITCH_TABLES 111 ! #define SWITCH_TAG 112 ! #define SWITCH_TELEPORT 113 ! #define SWITCH_TF 114 ! #define SWITCH_THINGS 115 ! #define SWITCH_TITLE 116 ! #define SWITCH_TRACE 117 ! #define SWITCH_UNCLEAR 118 ! #define SWITCH_UNFOLDER 119 ! #define SWITCH_UNGAG 120 ! #define SWITCH_UNHIDE 121 ! #define SWITCH_UNMUTE 122 ! #define SWITCH_UNTAG 123 ! #define SWITCH_UNTIL 124 ! #define SWITCH_URGENT 125 ! #define SWITCH_USEFLAG 126 ! #define SWITCH_WHAT 127 ! #define SWITCH_WHO 128 ! #define SWITCH_WIPE 129 ! #define SWITCH_WIZ 130 ! #define SWITCH_WIZARD 131 ! #define SWITCH_YES 132 ! #define SWITCH_ZONE 133 --- 13,137 ---- #define SWITCH_BUFFER 12 #define SWITCH_CHECK 13 #define SWITCH_CHOWN 14 ! #define SWITCH_CHUNKS 15 ! #define SWITCH_CLEAR 16 ! #define SWITCH_CMD 17 ! #define SWITCH_COMMANDS 18 ! #define SWITCH_CONN 19 ! #define SWITCH_CONNECT 20 ! #define SWITCH_CONNECTED 21 ! #define SWITCH_CONTENTS 22 ! #define SWITCH_COSTS 23 ! #define SWITCH_COUNT 24 ! #define SWITCH_CREATE 25 ! #define SWITCH_DATABASE 26 ! #define SWITCH_DB 27 ! #define SWITCH_DEBUG 28 ! #define SWITCH_DECOMPILE 29 ! #define SWITCH_DEFAULTS 30 ! #define SWITCH_DELETE 31 ! #define SWITCH_DELIMIT 32 ! #define SWITCH_DESCRIBE 33 ! #define SWITCH_DESTROY 34 ! #define SWITCH_DISABLE 35 ! #define SWITCH_DOWN 36 ! #define SWITCH_DSTATS 37 ! #define SWITCH_EMIT 38 ! #define SWITCH_ENABLE 39 ! #define SWITCH_ERR 40 ! #define SWITCH_EXITS 41 ! #define SWITCH_FILE 42 ! #define SWITCH_FIRST 43 ! #define SWITCH_FLAGS 44 ! #define SWITCH_FOLDERS 45 ! #define SWITCH_FORWARD 46 ! #define SWITCH_FSTATS 47 ! #define SWITCH_FULL 48 ! #define SWITCH_FUNCTIONS 49 ! #define SWITCH_FWD 50 ! #define SWITCH_GAG 51 ! #define SWITCH_GLOBALS 52 ! #define SWITCH_HEADER 53 ! #define SWITCH_HERE 54 ! #define SWITCH_HIDE 55 ! #define SWITCH_ILIST 56 ! #define SWITCH_INVENTORY 57 ! #define SWITCH_IPRINT 58 ! #define SWITCH_JOIN 59 ! #define SWITCH_LIST 60 ! #define SWITCH_LOWERCASE 61 ! #define SWITCH_ME 62 ! #define SWITCH_MEMBERS 63 ! #define SWITCH_MOD 64 ! #define SWITCH_MORTAL 65 ! #define SWITCH_MOTD 66 ! #define SWITCH_MUTE 67 ! #define SWITCH_NAME 68 ! #define SWITCH_NO 69 ! #define SWITCH_NOEVAL 70 ! #define SWITCH_NOFLAGCOPY 71 ! #define SWITCH_NOISY 72 ! #define SWITCH_NOSIG 73 ! #define SWITCH_NOSPACE 74 ! #define SWITCH_NOTIFY 75 ! #define SWITCH_NUKE 76 ! #define SWITCH_OFF 77 ! #define SWITCH_ON 78 ! #define SWITCH_OUTSIDE 79 ! #define SWITCH_OVERRIDE 80 ! #define SWITCH_PAGING 81 ! #define SWITCH_PANIC 82 ! #define SWITCH_PARANOID 83 ! #define SWITCH_PLAYERS 84 ! #define SWITCH_PORT 85 ! #define SWITCH_PRESERVE 86 ! #define SWITCH_PRINT 87 ! #define SWITCH_PRIVS 88 ! #define SWITCH_PURGE 89 ! #define SWITCH_QUICK 90 ! #define SWITCH_QUIET 91 ! #define SWITCH_READ 92 ! #define SWITCH_REBOOT 93 ! #define SWITCH_RECALL 94 ! #define SWITCH_REGIONS 95 ! #define SWITCH_REGISTER 96 ! #define SWITCH_REMOVE 97 ! #define SWITCH_RENAME 98 ! #define SWITCH_RESTORE 99 ! #define SWITCH_RESTRICT 100 ! #define SWITCH_RETROACTIVE 101 ! #define SWITCH_ROOM 102 ! #define SWITCH_ROOMS 103 ! #define SWITCH_SEE 104 ! #define SWITCH_SEEFLAG 105 ! #define SWITCH_SELF 106 ! #define SWITCH_SEND 107 ! #define SWITCH_SET 108 ! #define SWITCH_SILENT 109 ! #define SWITCH_SKIPDEFAULTS 110 ! #define SWITCH_SPEAK 111 ! #define SWITCH_STATS 112 ! #define SWITCH_SUMMARY 113 ! #define SWITCH_TABLES 114 ! #define SWITCH_TAG 115 ! #define SWITCH_TELEPORT 116 ! #define SWITCH_TF 117 ! #define SWITCH_THINGS 118 ! #define SWITCH_TITLE 119 ! #define SWITCH_TRACE 120 ! #define SWITCH_UNCLEAR 121 ! #define SWITCH_UNFOLDER 122 ! #define SWITCH_UNGAG 123 ! #define SWITCH_UNHIDE 124 ! #define SWITCH_UNMUTE 125 ! #define SWITCH_UNTAG 126 ! #define SWITCH_UNTIL 127 ! #define SWITCH_URGENT 128 ! #define SWITCH_USEFLAG 129 ! #define SWITCH_WHAT 130 ! #define SWITCH_WHO 131 ! #define SWITCH_WIPE 132 ! #define SWITCH_WIZ 133 ! #define SWITCH_WIZARD 134 ! #define SWITCH_YES 135 ! #define SWITCH_ZONE 136 *** 1_7_7.366/hdrs/htab.h Sat, 01 Mar 2003 23:51:32 -0600 dunemush (pennmush/d/23_htab.h 1.7 660) --- 1_7_7.446(w)/hdrs/htab.h Thu, 29 May 2003 10:01:46 -0500 dunemush (pennmush/d/23_htab.h 1.8 660) *************** *** 33,39 **** int entry_size; /**< Size of each entry */ }; - #define hashval(x,y) hash_val(x,y) #define get_hashmask(x) hash_getmask(x) #define hashinit(x,y, z) hash_init(x,y, z) #define hashfind(key,tab) hash_value(hash_find(tab,key)) --- 33,38 ---- *************** *** 42,48 **** #define hashdelete(key,tab) hash_delete(tab,hash_find(tab,key)) #define hashflush(tab, size) hash_flush(tab,size) #define hashfree(tab) hash_flush(tab, 0) - extern int hash_val(const char *key, int hashmask); extern int hash_getmask(int *size); extern void hash_init(HASHTAB *htab, int size, int data_size); extern HASHENT *hash_find(HASHTAB *htab, const char *key); --- 41,46 ---- *** 1_7_7.366/hdrs/command.h Thu, 27 Feb 2003 23:22:28 -0600 dunemush (pennmush/d/24_command.h 1.23 660) --- 1_7_7.446(w)/hdrs/command.h Thu, 29 May 2003 10:01:43 -0500 dunemush (pennmush/d/24_command.h 1.24 660) *************** *** 58,64 **** #define CMD_T_RS_SPACE CMD_T_ARG_SPACE<<4 #define CMD_T_RS_NOPARSE CMD_T_NOPARSE<<4 ! /* COMMAND prototype Passed arguments: executor : Object issuing command. sw : switch_mask, check with the SW_ macros. --- 58,65 ---- #define CMD_T_RS_SPACE CMD_T_ARG_SPACE<<4 #define CMD_T_RS_NOPARSE CMD_T_NOPARSE<<4 ! /** COMMAND prototype. ! * \verbatim Passed arguments: executor : Object issuing command. sw : switch_mask, check with the SW_ macros. *************** *** 75,80 **** --- 76,82 ---- Special case: If the NOEVAL switch is given, AND EQSPLIT is defined, the right-side will not be parsed. If NOEVAL is givean the EQSPLIT isn't defined, the left-side won't be parsed. + * \endverbatim */ #define COMMAND(command_name) \ *************** *** 94,99 **** --- 96,102 ---- char *arg_right __attribute__ ((__unused__)), \ char *args_right[MAX_ARG] __attribute__ ((__unused__))) + /** Common command prototype macro */ #define COMMAND_PROTO(command_name) \ void command_name (COMMAND_INFO *cmd, dbref player, dbref cause, switch_mask sw,char *raw, char *switches, char *args_raw, \ char *arg_left, char *args_left[MAX_ARG], \ *** 1_7_7.366/hdrs/attrib.h Thu, 30 Jan 2003 13:16:01 -0600 dunemush (pennmush/d/25_attrib.h 1.4.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.2.1.1.2.3.1.1 660) --- 1_7_7.446(w)/hdrs/attrib.h Thu, 29 May 2003 10:01:40 -0500 dunemush (pennmush/d/25_attrib.h 1.4.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.2.1.1.2.3.1.5 660) *************** *** 1,6 **** --- 1,8 ---- #ifndef _ATTRIB_H #define _ATTRIB_H + #include "chunk.h" + /** An attribute on an object. * This structure represents an attribute set on an object. * Attributes form a linked list on an object, sorted alphabetically. *************** *** 8,14 **** struct attr { char const *name; /**< Name of attribute */ int flags; /**< Attribute flags */ ! unsigned char *value; /**< The attribute's value, compressed */ dbref creator; /**< The attribute's creator's dbref */ ATTR *next; /**< Pointer to next attribute in list */ }; --- 10,16 ---- struct attr { char const *name; /**< Name of attribute */ int flags; /**< Attribute flags */ ! chunk_reference_t data; /**< The attribute's value, compressed */ dbref creator; /**< The attribute's creator's dbref */ ATTR *next; /**< Pointer to next attribute in list */ }; *************** *** 29,35 **** extern int good_atr_name(char const *s); extern ATTR *atr_match(char const *string); extern void atr_new_add(dbref thing, char const *RESTRICT atr, ! char const *RESTRICT s, dbref player, int flags); extern int atr_add(dbref thing, char const *RESTRICT atr, char const *RESTRICT s, dbref player, int flags); extern int atr_clr(dbref thing, char const *atr, dbref player); --- 31,38 ---- extern int good_atr_name(char const *s); extern ATTR *atr_match(char const *string); extern void atr_new_add(dbref thing, char const *RESTRICT atr, ! char const *RESTRICT s, dbref player, int flags, ! unsigned char derefs); extern int atr_add(dbref thing, char const *RESTRICT atr, char const *RESTRICT s, dbref player, int flags); extern int atr_clr(dbref thing, char const *atr, dbref player); *************** *** 52,57 **** --- 55,64 ---- extern void do_atrchown(dbref player, char const *arg1, char const *arg2); extern int string_to_atrflag(dbref player, const char *p); extern void init_atr_name_tree(void); + extern unsigned char *const atr_get_compressed_data(ATTR *atr); + extern char *const atr_value(ATTR *atr); + extern char *safe_atr_value(ATTR *atr); + extern void atr_migrate(int amount); /* possible attribute flags */ *************** *** 81,89 **** #define AL_ATTR(alist) (alist) #define AL_NAME(alist) ((alist)->name) ! #define AL_STR(alist) ((alist)->value) #define AL_NEXT(alist) ((alist)->next) #define AL_CREATOR(alist) ((alist)->creator) #define AL_FLAGS(alist) ((alist)->flags) #endif /* __ATTRIB_H */ --- 88,97 ---- #define AL_ATTR(alist) (alist) #define AL_NAME(alist) ((alist)->name) ! #define AL_STR(alist) (atr_get_compressed_data((alist))) #define AL_NEXT(alist) ((alist)->next) #define AL_CREATOR(alist) ((alist)->creator) #define AL_FLAGS(alist) ((alist)->flags) + #define AL_DEREFS(alist) ((alist)->data?chunk_derefs((alist)->data):0) #endif /* __ATTRIB_H */ *** 1_7_7.366/hdrs/atr_tab.h Sun, 09 Feb 2003 21:34:34 -0600 dunemush (pennmush/d/26_atr_tab.h 1.15.1.8 660) --- 1_7_7.446(w)/hdrs/atr_tab.h Thu, 29 May 2003 10:01:40 -0500 dunemush (pennmush/d/26_atr_tab.h 1.15.1.10 660) *************** *** 7,231 **** /* attribute list */ ATTR attr[] = { ! {"AAHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ACLONE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ACONNECT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ADEATH", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ADESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, {"ADESTROY", AF_WIZARD | AF_PRIVATE | AF_NOCOPY | AF_PREFIXMATCH, ! NULL, 0, NULL}, ! {"ADISCONNECT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ADROP", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AEFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AFAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AGIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AIDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ALEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ALFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ALIAS", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, #ifdef USE_MAILER ! {"AMAIL", AF_WIZARD | AF_PREFIXMATCH, NULL, 0, NULL}, #endif ! {"AMHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"APAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ARECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ASUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ATPORT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AUFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AUNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AUSE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AWAY", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"AZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"CHARGES", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"COMMENT", AF_NOPROG | AF_MDARK | AF_WIZARD | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"CONFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"COST", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"DEATH", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"DESCFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"DESCRIBE", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"DROP", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"EALIAS", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"EFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"EXITFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"EXITTO", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"FAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"FILTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"FOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, {"FOLLOWING", AF_NOPROG | AF_PRIVATE | AF_WIZARD | AF_NOCOPY, ! NULL, 0 | AF_PREFIXMATCH, NULL}, {"FOLLOWERS", AF_NOPROG | AF_PRIVATE | AF_WIZARD | AF_NOCOPY, ! NULL, 0 | AF_PREFIXMATCH, NULL}, ! {"FORWARDLIST", AF_NOPROG | AF_PRIVATE | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"GIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"HAVEN", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"IDESCFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"IDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"IDLE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"INFILTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"INPREFIX", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LALIAS", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LAST", AF_VISUAL | AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, ! NULL}, ! {"LASTIP", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LASTFAILED", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, ! NULL}, ! {"LASTLOGOUT", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, ! NULL}, ! {"LASTPAGED", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LASTSITE", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"LISTEN", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, #ifdef USE_MAILER {"MAILCURF", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL, 0, NULL}, {"MAILFILTERS", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL, 0, NULL}, {"MAILFOLDERS", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL, 0, NULL}, ! {"MAILSIGNATURE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, #endif ! {"MOVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"NAMEACCENT", AF_NOPROG | AF_VISUAL | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"NAMEFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ODEATH", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ODESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ODROP", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OEFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OFAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OGIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OIDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OLFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OPAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ORECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OXMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OSUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OTPORT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OUFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OUNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OUSE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OXENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OXLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OXTPORT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"OZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"PAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"PREFIX", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"RECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"QUEUE", AF_WIZARD | AF_PRIVATE | AF_NOCOPY | AF_NODUMP, NULL, 0, NULL}, {"REGISTERED_EMAIL", ! AF_WIZARD | AF_LOCKED | AF_PRIVATE | AF_NOCOPY, NULL, 0, NULL}, ! {"RQUOTA", AF_MDARK | AF_LOCKED, NULL, 0, NULL}, ! {"RUNOUT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"SEMAPHORE", AF_LOCKED | AF_PRIVATE | AF_NOCOPY | AF_NODUMP, NULL, 0, NULL}, ! {"SEX", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"STARTUP", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"SUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, {"TFPREFIX", AF_NOPROG | AF_NOCOPY | AF_PRIVATE | AF_PREFIXMATCH, ! NULL, 0, NULL}, ! {"TPORT", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"UFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"UNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"USE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"VA", 0, NULL, 0, NULL}, ! {"VB", 0, NULL, 0, NULL}, ! {"VC", 0, NULL, 0, NULL}, ! {"VD", 0, NULL, 0, NULL}, ! {"VE", 0, NULL, 0, NULL}, ! {"VF", 0, NULL, 0, NULL}, ! {"VG", 0, NULL, 0, NULL}, ! {"VH", 0, NULL, 0, NULL}, ! {"VI", 0, NULL, 0, NULL}, ! {"VJ", 0, NULL, 0, NULL}, ! {"VK", 0, NULL, 0, NULL}, ! {"VL", 0, NULL, 0, NULL}, ! {"VM", 0, NULL, 0, NULL}, ! {"VN", 0, NULL, 0, NULL}, ! {"VO", 0, NULL, 0, NULL}, ! {"VP", 0, NULL, 0, NULL}, ! {"VQ", 0, NULL, 0, NULL}, ! {"VR", 0, NULL, 0, NULL}, ! {"VRML_URL", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"VS", 0, NULL, 0, NULL}, ! {"VT", 0, NULL, 0, NULL}, ! {"VU", 0, NULL, 0, NULL}, ! {"VV", 0, NULL, 0, NULL}, ! {"VW", 0, NULL, 0, NULL}, ! {"VX", 0, NULL, 0, NULL}, ! {"VY", 0, NULL, 0, NULL}, ! {"VZ", 0, NULL, 0, NULL}, ! {"WA", 0, NULL, 0, NULL}, ! {"WB", 0, NULL, 0, NULL}, ! {"WC", 0, NULL, 0, NULL}, ! {"WD", 0, NULL, 0, NULL}, ! {"WE", 0, NULL, 0, NULL}, ! {"WF", 0, NULL, 0, NULL}, ! {"WG", 0, NULL, 0, NULL}, ! {"WH", 0, NULL, 0, NULL}, ! {"WI", 0, NULL, 0, NULL}, ! {"WJ", 0, NULL, 0, NULL}, ! {"WK", 0, NULL, 0, NULL}, ! {"WL", 0, NULL, 0, NULL}, ! {"WM", 0, NULL, 0, NULL}, ! {"WN", 0, NULL, 0, NULL}, ! {"WO", 0, NULL, 0, NULL}, ! {"WP", 0, NULL, 0, NULL}, ! {"WQ", 0, NULL, 0, NULL}, ! {"WR", 0, NULL, 0, NULL}, ! {"WS", 0, NULL, 0, NULL}, ! {"WT", 0, NULL, 0, NULL}, ! {"WU", 0, NULL, 0, NULL}, ! {"WV", 0, NULL, 0, NULL}, ! {"WW", 0, NULL, 0, NULL}, ! {"WX", 0, NULL, 0, NULL}, ! {"WY", 0, NULL, 0, NULL}, ! {"WZ", 0, NULL, 0, NULL}, ! {"XA", 0, NULL, 0, NULL}, ! {"XB", 0, NULL, 0, NULL}, ! {"XC", 0, NULL, 0, NULL}, ! {"XD", 0, NULL, 0, NULL}, ! {"XE", 0, NULL, 0, NULL}, ! {"XF", 0, NULL, 0, NULL}, ! {"XG", 0, NULL, 0, NULL}, ! {"XH", 0, NULL, 0, NULL}, ! {"XI", 0, NULL, 0, NULL}, ! {"XJ", 0, NULL, 0, NULL}, ! {"XK", 0, NULL, 0, NULL}, ! {"XL", 0, NULL, 0, NULL}, ! {"XM", 0, NULL, 0, NULL}, ! {"XN", 0, NULL, 0, NULL}, ! {"XO", 0, NULL, 0, NULL}, ! {"XP", 0, NULL, 0, NULL}, ! {"XQ", 0, NULL, 0, NULL}, ! {"XR", 0, NULL, 0, NULL}, ! {"XS", 0, NULL, 0, NULL}, ! {"XT", 0, NULL, 0, NULL}, ! {"XU", 0, NULL, 0, NULL}, ! {"XV", 0, NULL, 0, NULL}, ! {"XW", 0, NULL, 0, NULL}, ! {"XX", 0, NULL, 0, NULL}, ! {"XY", 0, NULL, 0, NULL}, ! {"XZ", 0, NULL, 0, NULL}, {"XYXXY", AF_INTERNAL | AF_NOPROG | AF_LOCKED | AF_WIZARD, ! NULL, 0, NULL}, ! {"ZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {"ZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL, 0, NULL}, ! {NULL, 0, NULL, 0, NULL} }; #endif /* __ATR_TAB_H */ --- 7,246 ---- /* attribute list */ ATTR attr[] = { ! {"AAHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ACLONE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ACONNECT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ADEATH", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ADESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, {"ADESTROY", AF_WIZARD | AF_PRIVATE | AF_NOCOPY | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ADISCONNECT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ADROP", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AEFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AFAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AGIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AIDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ALEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ALFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ALIAS", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, #ifdef USE_MAILER ! {"AMAIL", AF_WIZARD | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, #endif ! {"AMHEAR", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"APAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ARECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ASUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ATPORT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AUFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AUNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AUSE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AWAY", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"AZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"CHARGES", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"COMMENT", AF_NOPROG | AF_MDARK | AF_WIZARD | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"CONFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"COST", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"DEATH", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"DESCFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"DESCRIBE", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"DROP", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"EALIAS", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"EFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"EXITFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"EXITTO", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"FAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"FILTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"FOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, {"FOLLOWING", AF_NOPROG | AF_PRIVATE | AF_WIZARD | AF_NOCOPY, ! NULL_CHUNK_REFERENCE, 0 | AF_PREFIXMATCH, NULL}, {"FOLLOWERS", AF_NOPROG | AF_PRIVATE | AF_WIZARD | AF_NOCOPY, ! NULL_CHUNK_REFERENCE, 0 | AF_PREFIXMATCH, NULL}, ! {"FORWARDLIST", AF_NOPROG | AF_PRIVATE | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, ! 0, ! NULL}, ! {"GIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"HAVEN", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"IDESCFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"IDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"IDLE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"INFILTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"INPREFIX", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"LALIAS", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"LAST", AF_VISUAL | AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"LASTIP", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"LASTFAILED", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, ! 0, NULL}, ! {"LASTLOGOUT", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, ! 0, ! NULL}, ! {"LASTPAGED", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"LASTSITE", AF_WIZARD | AF_LOCKED | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"LEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"LFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"LISTEN", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, #ifdef USE_MAILER {"MAILCURF", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, {"MAILFILTERS", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, {"MAILFOLDERS", AF_WIZARD | AF_NOPROG | AF_LOCKED | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"MAILSIGNATURE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, #endif ! {"MOVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"NAMEACCENT", AF_NOPROG | AF_VISUAL | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, ! 0, NULL}, ! {"NAMEFORMAT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ODEATH", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ODESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ODROP", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OEFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OFAILURE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OGIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OIDESCRIBE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OLFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OPAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ORECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OXMOVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OSUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OTPORT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OUFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OUNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OUSE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OXENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OXLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OXTPORT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"OZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"PAYMENT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"PREFIX", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"RECEIVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"QUEUE", AF_WIZARD | AF_PRIVATE | AF_NOCOPY | AF_NODUMP, ! NULL_CHUNK_REFERENCE, 0, NULL}, {"REGISTERED_EMAIL", ! AF_WIZARD | AF_LOCKED | AF_PRIVATE | AF_NOCOPY, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"RQUOTA", AF_MDARK | AF_LOCKED, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"RUNOUT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"SEMAPHORE", AF_LOCKED | AF_PRIVATE | AF_NOCOPY | AF_NODUMP, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"SEX", AF_VISUAL | AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, ! NULL}, ! {"STARTUP", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"SUCCESS", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, {"TFPREFIX", AF_NOPROG | AF_NOCOPY | AF_PRIVATE | AF_PREFIXMATCH, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"TPORT", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"UFAIL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"UNFOLLOW", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"USE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VA", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VB", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VC", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VD", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VE", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VF", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VG", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VH", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VI", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VJ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VK", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VL", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VM", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VN", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VO", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VP", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VQ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VR", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VRML_URL", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VS", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VT", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VU", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VV", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VW", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VX", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VY", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"VZ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WA", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WB", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WC", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WD", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WE", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WF", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WG", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WH", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WI", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WJ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WK", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WL", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WM", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WN", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WO", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WP", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WQ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WR", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WS", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WT", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WU", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WV", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WW", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WX", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WY", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"WZ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XA", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XB", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XC", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XD", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XE", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XF", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XG", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XH", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XI", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XJ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XK", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XL", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XM", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XN", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XO", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XP", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XQ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XR", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XS", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XT", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XU", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XV", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XW", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XX", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XY", 0, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"XZ", 0, NULL_CHUNK_REFERENCE, 0, NULL}, {"XYXXY", AF_INTERNAL | AF_NOPROG | AF_LOCKED | AF_WIZARD, ! NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ZENTER", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {"ZLEAVE", AF_NOPROG | AF_PREFIXMATCH, NULL_CHUNK_REFERENCE, 0, NULL}, ! {NULL, 0, NULL_CHUNK_REFERENCE, 0, NULL} }; #endif /* __ATR_TAB_H */ *** 1_7_7.366/MANIFEST Tue, 18 Mar 2003 16:19:42 -0600 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.3 600) --- 1_7_7.446(w)/MANIFEST Mon, 28 Apr 2003 22:09:10 -0500 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.4 600) *************** *** 13,18 **** --- 13,19 ---- hdrs/attrib.h hdrs/boolexp.h hdrs/command.h + hdrs/chunk.h hdrs/htab.h hdrs/privtab.h hdrs/switches.h *************** *** 53,58 **** --- 54,60 ---- src/attrib.c src/boolexp.c src/bsd.c + src/chunk.c src/cmds.c src/command.c src/comp_h.c *** 1_7_7.366/src/strtree.c Sun, 30 Mar 2003 22:39:28 -0600 dunemush (pennmush/d/35_strtree.c 1.14.1.8 660) --- 1_7_7.446(w)/src/strtree.c Thu, 29 May 2003 10:01:36 -0500 dunemush (pennmush/d/35_strtree.c 1.14.1.9 660) *************** *** 45,59 **** #include "confmagic.h" #ifndef UCHAR_MAX ! #define UCHAR_MAX 255 #endif /* Various constants. Their import is either bleedingly obvious * or explained below. */ ! #define ST_MAX_DEPTH 64 ! #define ST_RED 1 ! #define ST_BLACK 0 ! #define ST_COLOR 1 #define ST_USE_STEP 2 #define ST_USE_LIMIT (UCHAR_MAX - ST_USE_STEP + 1) --- 45,59 ---- #include "confmagic.h" #ifndef UCHAR_MAX ! #define UCHAR_MAX 255 /**< Largest unsigned character */ #endif /* Various constants. Their import is either bleedingly obvious * or explained below. */ ! #define ST_MAX_DEPTH 64 /**< Max depth of the tree */ ! #define ST_RED 1 /**< This node is red */ ! #define ST_BLACK 0 /**< This node is black */ ! #define ST_COLOR 1 /**< Bit mask for colors */ #define ST_USE_STEP 2 #define ST_USE_LIMIT (UCHAR_MAX - ST_USE_STEP + 1) *************** *** 67,73 **** */ static StrNode *path[ST_MAX_DEPTH]; ! unsigned long st_mem = 0; static void st_left_rotate(int tree_depth, StrNode **root); static void st_right_rotate(int tree_depth, StrNode **root); --- 67,73 ---- */ static StrNode *path[ST_MAX_DEPTH]; ! unsigned long st_mem = 0; /**< Memory used by string trees */ static void st_left_rotate(int tree_depth, StrNode **root); static void st_right_rotate(int tree_depth, StrNode **root); *** 1_7_7.366/src/pcre.c Thu, 05 Sep 2002 09:29:20 -0500 dunemush (pennmush/d/36_pcre.c 1.4.1.3.1.3.1.1.1.1.1.1.1.3 660) --- 1_7_7.446(w)/src/pcre.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/d/36_pcre.c 1.4.1.3.1.3.1.1.1.1.1.1.1.4 660) *************** *** 2,15 **** * Perl-Compatible Regular Expressions * *************************************************/ ! /* ! This is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. See the file Tech.Notes for some information on the internals. Written by: Philip Hazel ! Copyright (c) 1997-2001 University of Cambridge ----------------------------------------------------------------------------- Permission is granted to anyone to use this software for any purpose on any --- 2,15 ---- * Perl-Compatible Regular Expressions * *************************************************/ ! ! /* This is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. See the file Tech.Notes for some information on the internals. Written by: Philip Hazel ! Copyright (c) 1997-2003 University of Cambridge ----------------------------------------------------------------------------- Permission is granted to anyone to use this software for any purpose on any *************** *** 32,68 **** ----------------------------------------------------------------------------- */ ! /*********** THIS IS AN ALTERED VERSION! ****************/ ! /* ! This is an altered version of pcre. It has been altered by ! Alan Schwartz ("Javelin") and Shawn Wagner ("Raevnos") for ! inclusion in PennMUSH. ! Some changes include: ! * All pcre code combined into pcre.c/pcre.h pair ! * includes of config/confmagic.h ! * pcre_malloc and pcre_free changed to malloc and free ! * Deleted some never-used functions (pcre_version, pcre_info, etc.) ! * Tries to avoid infinitely-long backtracking (Raevnos) ! * Remove unused UTF-8 support. ! */ #include "config.h" - #define BACKTRACK_LIMIT 2500 - - #include #include #include #include #include #include - #ifdef BACKTRACK_LIMIT - #include - #include - extern sig_atomic_t cpu_time_limit_hit; - #endif #include "pcre.h" #include "confmagic.h" /* These are the public options that can change during matching. */ #define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) --- 32,100 ---- ----------------------------------------------------------------------------- */ ! /* Modified by Shawn Wagner for PennMUSH to fit in one file and remove ! things we don't use, like a bunch of API functions and utf-8 ! support. If you want the full thing, see http://www.pcre.org. */ #include "config.h" #include #include #include #include #include #include "pcre.h" #include "confmagic.h" + /* Bits of PCRE's config.h */ + #define LINK_SIZE 2 + #define MATCH_LIMIT 100000 + #define NEWLINE '\n' + + /* Bits of internal.h */ + /* This header contains definitions that are shared between the different + modules, but which are not relevant to the outside. */ + + + /* PCRE keeps offsets in its compiled code as 2-byte quantities by default. + These are used, for example, to link from the start of a subpattern to its + alternatives and its end. The use of 2 bytes per offset limits the size of the + compiled regex to around 64K, which is big enough for almost everybody. + However, I received a request for an even bigger limit. For this reason, and + also to make the code easier to maintain, the storing and loading of offsets + from the byte string is now handled by the macros that are defined here. + + The macros are controlled by the value of LINK_SIZE. This defaults to 2 in + the config.h file, but can be overridden by using -D on the command line. This + is automated on Unix systems via the "configure" command. */ + + #define PUT(a,n,d) \ + (a[n] = (d) >> 8), \ + (a[(n)+1] = (d) & 255) + + #define GET(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + + #define MAX_PATTERN_SIZE (1 << 16) + + + /* Convenience macro defined in terms of the others */ + + #define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE + + + /* PCRE uses some other 2-byte quantities that do not change when the size of + offsets changes. There are used for repeat counts and for other things such as + capturing parenthesis numbers in back references. */ + + #define PUT2(a,n,d) \ + a[n] = (d) >> 8; \ + a[(n)+1] = (d) & 255 + + #define GET2(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + + #define PUT2INC(a,n,d) PUT2(a,n,d), a += 2 + /* These are the public options that can change during matching. */ #define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) *************** *** 73,83 **** significant end. Make sure they don't overlap, though now that we have expanded to four bytes there is plenty of space. */ ! #define PCRE_FIRSTSET 0x40000000 /* first_char is set */ ! #define PCRE_REQCHSET 0x20000000 /* req_char is set */ #define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ ! #define PCRE_INGROUP 0x08000000 /* compiling inside a group */ ! #define PCRE_ICHANGED 0x04000000 /* i option changes within regex */ /* Options for the "extra" block produced by pcre_study(). */ --- 105,114 ---- significant end. Make sure they don't overlap, though now that we have expanded to four bytes there is plenty of space. */ ! #define PCRE_FIRSTSET 0x40000000 /* first_byte is set */ ! #define PCRE_REQCHSET 0x20000000 /* req_byte is set */ #define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ ! #define PCRE_ICHANGED 0x08000000 /* i option changes within regex */ /* Options for the "extra" block produced by pcre_study(). */ *************** *** 88,94 **** #define PUBLIC_OPTIONS \ (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ ! PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8) #define PUBLIC_EXEC_OPTIONS \ (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) --- 119,126 ---- #define PUBLIC_OPTIONS \ (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ ! PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ ! PCRE_NO_AUTO_CAPTURE) #define PUBLIC_EXEC_OPTIONS \ (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) *************** *** 99,104 **** --- 131,147 ---- #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ + /* Negative values for the firstchar and reqchar variables */ + + #define REQ_UNSET (-2) + #define REQ_NONE (-1) + + /* Flags added to firstbyte or reqbyte; a "non-literal" item is either a + variable-length repeat, or a anything other than literal characters. */ + + #define REQ_CASELESS 0x0100 /* indicates caselessness */ + #define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ + /* Miscellaneous definitions */ typedef int BOOL; *************** *** 106,254 **** #define FALSE 0 #define TRUE 1 - #ifndef NEWLINE - #define NEWLINE '\n' - #endif - /* Escape items that are just an encoding of a particular data value. Note that ! ESC_N is defined as yet another macro, which is set in config.h to either \n (the default) or \r (which some people want). */ ! #ifndef ESC_E ! #define ESC_E 27 #endif ! #ifndef ESC_F ! #define ESC_F '\f' #endif ! #ifndef ESC_N ! #define ESC_N NEWLINE #endif ! #ifndef ESC_R ! #define ESC_R '\r' #endif ! #ifndef ESC_T ! #define ESC_T '\t' #endif /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns their negation. Also, they must appear in the same order as in the opcode ! definitions below, up to ESC_z. The final one must be ESC_REF as subsequent ! values are used for \1, \2, \3, etc. There is a test in the code for an escape ! greater than ESC_b and less than ESC_Z to detect the types that may be ! repeated. If any new escapes are put in-between that don't consume a character, ! that code will have to change. */ ! enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ! ESC_Z, ESC_z, ESC_REF }; /* Opcode table: OP_BRA must be last, as all values >= it are used for brackets that extract substrings. Starting from 1 (i.e. after OP_END), the values up to ! OP_EOD must correspond in order to the list of escapes immediately above. */ enum { ! OP_END, /* End of pattern */ /* Values corresponding to backslashed metacharacters */ ! OP_SOD, /* Start of data: \A */ ! OP_NOT_WORD_BOUNDARY, /* \B */ ! OP_WORD_BOUNDARY, /* \b */ ! OP_NOT_DIGIT, /* \D */ ! OP_DIGIT, /* \d */ ! OP_NOT_WHITESPACE, /* \S */ ! OP_WHITESPACE, /* \s */ ! OP_NOT_WORDCHAR, /* \W */ ! OP_WORDCHAR, /* \w */ ! OP_EODN, /* End of data or \n at end of data: \Z. */ ! OP_EOD, /* End of data: \z */ ! ! OP_OPT, /* Set runtime options */ ! OP_CIRC, /* Start of line - varies with multiline switch */ ! OP_DOLL, /* End of line - varies with multiline switch */ ! OP_ANY, /* Match any character */ ! OP_CHARS, /* Match string of characters */ ! OP_NOT, /* Match anything but the following char */ ! ! OP_STAR, /* The maximizing and minimizing versions of */ ! OP_MINSTAR, /* all these opcodes must come in pairs, with */ ! OP_PLUS, /* the minimizing one second. */ ! OP_MINPLUS, /* This first set applies to single characters */ ! OP_QUERY, ! OP_MINQUERY, ! OP_UPTO, /* From 0 to n matches */ ! OP_MINUPTO, ! OP_EXACT, /* Exactly n matches */ ! ! OP_NOTSTAR, /* The maximizing and minimizing versions of */ ! OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ ! OP_NOTPLUS, /* the minimizing one second. */ ! OP_NOTMINPLUS, /* This first set applies to "not" single characters */ ! OP_NOTQUERY, ! OP_NOTMINQUERY, ! OP_NOTUPTO, /* From 0 to n matches */ ! OP_NOTMINUPTO, ! OP_NOTEXACT, /* Exactly n matches */ ! ! OP_TYPESTAR, /* The maximizing and minimizing versions of */ ! OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ ! OP_TYPEPLUS, /* the minimizing one second. These codes must */ ! OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ ! OP_TYPEQUERY, /* This set applies to character types such as \d */ ! OP_TYPEMINQUERY, ! OP_TYPEUPTO, /* From 0 to n matches */ ! OP_TYPEMINUPTO, ! OP_TYPEEXACT, /* Exactly n matches */ ! ! OP_CRSTAR, /* The maximizing and minimizing versions of */ ! OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ ! OP_CRPLUS, /* the minimizing one second. These codes must */ ! OP_CRMINPLUS, /* be in exactly the same order as those above. */ ! OP_CRQUERY, /* These are for character classes and back refs */ ! OP_CRMINQUERY, ! OP_CRRANGE, /* These are different to the three seta above. */ ! OP_CRMINRANGE, ! ! OP_CLASS, /* Match a character class */ ! OP_REF, /* Match a back reference */ ! OP_RECURSE, /* Match this pattern recursively */ ! ! OP_ALT, /* Start of alternation */ ! OP_KET, /* End of group that doesn't have an unbounded repeat */ ! OP_KETRMAX, /* These two must remain together and in this */ ! OP_KETRMIN, /* order. They are for groups the repeat for ever. */ /* The assertions must come before ONCE and COND */ ! OP_ASSERT, /* Positive lookahead */ ! OP_ASSERT_NOT, /* Negative lookahead */ ! OP_ASSERTBACK, /* Positive lookbehind */ ! OP_ASSERTBACK_NOT, /* Negative lookbehind */ ! OP_REVERSE, /* Move pointer back - used in lookbehind assertions */ /* ONCE and COND must come after the assertions, with ONCE first, as there's a test for >= ONCE for a subpattern that isn't an assertion. */ ! OP_ONCE, /* Once matched, don't back up into the subpattern */ ! OP_COND, /* Conditional group */ ! OP_CREF, /* Used to hold an extraction string number (cond ref) */ ! OP_BRAZERO, /* These two must remain together and in this */ ! OP_BRAMINZERO, /* order. */ ! OP_BRANUMBER, /* Used for extracting brackets whose number is greater than can fit into an opcode. */ ! OP_BRA /* This and greater values are used for brackets that extract substrings up to a basic limit. After that, use is made of OP_BRANUMBER. */ }; /* The highest extraction number before we have to start using additional bytes. (Originally PCRE didn't have support for extraction counts highter than this number.) The value is limited by the number of opcodes left after OP_BRA, --- 149,391 ---- #define FALSE 0 #define TRUE 1 /* Escape items that are just an encoding of a particular data value. Note that ! ESC_n is defined as yet another macro, which is set in config.h to either \n (the default) or \r (which some people want). */ ! #ifndef ESC_e ! #define ESC_e 27 #endif ! #ifndef ESC_f ! #define ESC_f '\f' #endif ! #ifndef ESC_n ! #define ESC_n NEWLINE #endif ! #ifndef ESC_r ! #define ESC_r '\r' #endif ! #ifndef ESC_t ! #define ESC_t '\t' #endif /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns their negation. Also, they must appear in the same order as in the opcode ! definitions below, up to ESC_z. There's a dummy for OP_ANY because it ! corresponds to "." rather than an escape sequence. The final one must be ! ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two ! tests in the code for an escape greater than ESC_b and less than ESC_Z to ! detect the types that may be repeated. These are the types that consume a ! character. If any new escapes are put in between that don't consume a ! character, that code will have to change. */ ! enum { ESC_A = 1, ESC_G, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ! ESC_w, ESC_dum1, ESC_C, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_REF }; + /* Flag bits and data types for the extended class (OP_XCLASS) for classes that + contain UTF-8 characters with values greater than 255. */ + + #define XCL_NOT 0x01 /* Flag: this is a negative class */ + #define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + + #define XCL_END 0 /* Marks end of individual items */ + #define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ + #define XCL_RANGE 2 /* A range (two multibyte chars) follows */ + + /* Opcode table: OP_BRA must be last, as all values >= it are used for brackets that extract substrings. Starting from 1 (i.e. after OP_END), the values up to ! OP_EOD must correspond in order to the list of escapes immediately above. ! Note that whenever this list is updated, the two macro definitions that follow ! must also be updated to match. */ enum { ! OP_END, /* 0 End of pattern */ /* Values corresponding to backslashed metacharacters */ ! OP_SOD, /* 1 Start of data: \A */ ! OP_SOM, /* 2 Start of match (subject + offset): \G */ ! OP_NOT_WORD_BOUNDARY, /* 3 \B */ ! OP_WORD_BOUNDARY, /* 4 \b */ ! OP_NOT_DIGIT, /* 5 \D */ ! OP_DIGIT, /* 6 \d */ ! OP_NOT_WHITESPACE, /* 7 \S */ ! OP_WHITESPACE, /* 8 \s */ ! OP_NOT_WORDCHAR, /* 9 \W */ ! OP_WORDCHAR, /* 10 \w */ ! OP_ANY, /* 11 Match any character */ ! OP_ANYBYTE, /* 12 Match any byte (\C); different to OP_ANY for UTF-8 */ ! OP_EODN, /* 13 End of data or \n at end of data: \Z. */ ! OP_EOD, /* 14 End of data: \z */ ! ! OP_OPT, /* 15 Set runtime options */ ! OP_CIRC, /* 16 Start of line - varies with multiline switch */ ! OP_DOLL, /* 17 End of line - varies with multiline switch */ ! OP_CHARS, /* 18 Match string of characters */ ! OP_NOT, /* 19 Match anything but the following char */ ! ! OP_STAR, /* 20 The maximizing and minimizing versions of */ ! OP_MINSTAR, /* 21 all these opcodes must come in pairs, with */ ! OP_PLUS, /* 22 the minimizing one second. */ ! OP_MINPLUS, /* 23 This first set applies to single characters */ ! OP_QUERY, /* 24 */ ! OP_MINQUERY, /* 25 */ ! OP_UPTO, /* 26 From 0 to n matches */ ! OP_MINUPTO, /* 27 */ ! OP_EXACT, /* 28 Exactly n matches */ ! ! OP_NOTSTAR, /* 29 The maximizing and minimizing versions of */ ! OP_NOTMINSTAR, /* 30 all these opcodes must come in pairs, with */ ! OP_NOTPLUS, /* 31 the minimizing one second. */ ! OP_NOTMINPLUS, /* 32 This set applies to "not" single characters */ ! OP_NOTQUERY, /* 33 */ ! OP_NOTMINQUERY, /* 34 */ ! OP_NOTUPTO, /* 35 From 0 to n matches */ ! OP_NOTMINUPTO, /* 36 */ ! OP_NOTEXACT, /* 37 Exactly n matches */ ! ! OP_TYPESTAR, /* 38 The maximizing and minimizing versions of */ ! OP_TYPEMINSTAR, /* 39 all these opcodes must come in pairs, with */ ! OP_TYPEPLUS, /* 40 the minimizing one second. These codes must */ ! OP_TYPEMINPLUS, /* 41 be in exactly the same order as those above. */ ! OP_TYPEQUERY, /* 42 This set applies to character types such as \d */ ! OP_TYPEMINQUERY, /* 43 */ ! OP_TYPEUPTO, /* 44 From 0 to n matches */ ! OP_TYPEMINUPTO, /* 45 */ ! OP_TYPEEXACT, /* 46 Exactly n matches */ ! ! OP_CRSTAR, /* 47 The maximizing and minimizing versions of */ ! OP_CRMINSTAR, /* 48 all these opcodes must come in pairs, with */ ! OP_CRPLUS, /* 49 the minimizing one second. These codes must */ ! OP_CRMINPLUS, /* 50 be in exactly the same order as those above. */ ! OP_CRQUERY, /* 51 These are for character classes and back refs */ ! OP_CRMINQUERY, /* 52 */ ! OP_CRRANGE, /* 53 These are different to the three seta above. */ ! OP_CRMINRANGE, /* 54 */ ! ! OP_CLASS, /* 55 Match a character class, chars < 256 only */ ! OP_NCLASS, /* 56 Same, but the bitmap was created from a negative ! class - the difference is relevant only when a UTF-8 ! character > 255 is encountered. */ ! ! OP_XCLASS, /* 56 Extended class for handling UTF-8 chars within the ! class. This does both positive and negative. */ ! ! OP_REF, /* 57 Match a back reference */ ! OP_RECURSE, /* 58 Match a numbered subpattern (possibly recursive) */ ! OP_CALLOUT, /* 59 Call out to external function if provided */ ! ! OP_ALT, /* 60 Start of alternation */ ! OP_KET, /* 61 End of group that doesn't have an unbounded repeat */ ! OP_KETRMAX, /* 62 These two must remain together and in this */ ! OP_KETRMIN, /* 63 order. They are for groups the repeat for ever. */ /* The assertions must come before ONCE and COND */ ! OP_ASSERT, /* 64 Positive lookahead */ ! OP_ASSERT_NOT, /* 65 Negative lookahead */ ! OP_ASSERTBACK, /* 66 Positive lookbehind */ ! OP_ASSERTBACK_NOT, /* 67 Negative lookbehind */ ! OP_REVERSE, /* 68 Move pointer back - used in lookbehind assertions */ /* ONCE and COND must come after the assertions, with ONCE first, as there's a test for >= ONCE for a subpattern that isn't an assertion. */ ! OP_ONCE, /* 69 Once matched, don't back up into the subpattern */ ! OP_COND, /* 70 Conditional group */ ! OP_CREF, /* 71 Used to hold an extraction string number (cond ref) */ ! OP_BRAZERO, /* 72 These two must remain together and in this */ ! OP_BRAMINZERO, /* 73 order. */ ! OP_BRANUMBER, /* 74 Used for extracting brackets whose number is greater than can fit into an opcode. */ ! OP_BRA /* 75 This and greater values are used for brackets that extract substrings up to a basic limit. After that, use is made of OP_BRANUMBER. */ }; + /* WARNING: There is an implicit assumption in study.c that all opcodes are + less than 128 in value. This makes handling UTF-8 character sequences easier. + */ + + + /* This macro defines textual names for all the opcodes. There are used only + for debugging, in pcre.c when DEBUG is defined, and also in pcretest.c. The + macro is referenced only in printint.c. */ + + #define OP_NAME_LIST \ + "End", "\\A", "\\G", "\\B", "\\b", "\\D", "\\d", \ + "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", "\\Z", "\\z", \ + "Opt", "^", "$", "chars", "not", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", \ + "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \ + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \ + "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cond ref",\ + "Brazero", "Braminzero", "Branumber", "Bra" + + + /* This macro defines the length of fixed length operations in the compiled + regex. The lengths are used when searching for specific things, and also in the + debugging printing of a compiled regex. We use a macro so that it can be + incorporated both into pcre.c and pcretest.c without being publicly exposed. + + As things have been extended, some of these are no longer fixed lenths, but are + minima instead. For example, the length of a single-character repeat may vary + in UTF-8 mode. The code that uses this table must know about such things. */ + + #define OP_LENGTHS \ + 1, /* End */ \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* \A, \G, \B, \B, \D, \d, \S, \s, \W, \w */ \ + 1, 1, 1, 1, 2, 1, 1, /* Any, Anybyte, \Z, \z, Opt, ^, $ */ \ + 2, /* Chars - the minimum length */ \ + 2, /* not */ \ + /* Positive single-char repeats */ \ + 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** These are */ \ + 4, 4, 4, /* upto, minupto, exact ** minima */ \ + /* Negative single-char repeats */ \ + 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* NOT upto, minupto, exact */ \ + /* Positive type repeats */ \ + 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* Type upto, minupto, exact */ \ + /* Character class & ref repeats */ \ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ + 5, 5, /* CRRANGE, CRMINRANGE */ \ + 33, /* CLASS */ \ + 33, /* NCLASS */ \ + 0, /* XCLASS - variable length */ \ + 3, /* REF */ \ + 1+LINK_SIZE, /* RECURSE */ \ + 2, /* CALLOUT */ \ + 1+LINK_SIZE, /* Alt */ \ + 1+LINK_SIZE, /* Ket */ \ + 1+LINK_SIZE, /* KetRmax */ \ + 1+LINK_SIZE, /* KetRmin */ \ + 1+LINK_SIZE, /* Assert */ \ + 1+LINK_SIZE, /* Assert not */ \ + 1+LINK_SIZE, /* Assert behind */ \ + 1+LINK_SIZE, /* Assert behind not */ \ + 1+LINK_SIZE, /* Reverse */ \ + 1+LINK_SIZE, /* Once */ \ + 1+LINK_SIZE, /* COND */ \ + 3, /* CREF */ \ + 1, 1, /* BRAZERO, BRAMINZERO */ \ + 3, /* BRANUMBER */ \ + 1+LINK_SIZE /* BRA */ \ + + /* The highest extraction number before we have to start using additional bytes. (Originally PCRE didn't have support for extraction counts highter than this number.) The value is limited by the number of opcodes left after OP_BRA, *************** *** 257,262 **** --- 394,403 ---- #define EXTRACT_BASIC_MAX 150 + /* A magic value for OP_CREF to indicate the "in recursion" condition. */ + + #define CREF_RECURSE 0xffff + /* The texts of compile-time error messages are defined as macros here so that they can be accessed by the POSIX wrapper and converted into error codes. Yes, I could have used error codes in the first place, but didn't feel like changing *************** *** 274,282 **** #define ERR10 "operand of unlimited repeat could match the empty string" #define ERR11 "internal error: unexpected repeat" #define ERR12 "unrecognized character after (?" ! #define ERR13 "unused error" #define ERR14 "missing )" ! #define ERR15 "back reference to non-existent subpattern" #define ERR16 "erroffset passed as NULL" #define ERR17 "unknown option bit(s) set" #define ERR18 "missing ) after comment" --- 415,423 ---- #define ERR10 "operand of unlimited repeat could match the empty string" #define ERR11 "internal error: unexpected repeat" #define ERR12 "unrecognized character after (?" ! #define ERR13 "POSIX named classes are supported only within a class" #define ERR14 "missing )" ! #define ERR15 "reference to non-existent subpattern" #define ERR16 "erroffset passed as NULL" #define ERR17 "unknown option bit(s) set" #define ERR18 "missing ) after comment" *************** *** 290,302 **** #define ERR26 "malformed number after (?(" #define ERR27 "conditional group contains more than two branches" #define ERR28 "assertion expected after (?(" ! #define ERR29 "(?p must be followed by )" #define ERR30 "unknown POSIX class name" #define ERR31 "POSIX collating elements are not supported" #define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support" ! #define ERR33 "characters with values > 255 are not yet supported in classes" #define ERR34 "character value in \\x{...} sequence is too large" #define ERR35 "invalid condition (?(0)" /* All character handling must be done as unsigned characters. Otherwise there are problems with top-bit-set characters and functions such as isspace(). --- 431,451 ---- #define ERR26 "malformed number after (?(" #define ERR27 "conditional group contains more than two branches" #define ERR28 "assertion expected after (?(" ! #define ERR29 "(?R or (?digits must be followed by )" #define ERR30 "unknown POSIX class name" #define ERR31 "POSIX collating elements are not supported" #define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support" ! #define ERR33 "spare error" #define ERR34 "character value in \\x{...} sequence is too large" #define ERR35 "invalid condition (?(0)" + #define ERR36 "\\C not allowed in lookbehind assertion" + #define ERR37 "PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X" + #define ERR38 "number after (?C is > 255" + #define ERR39 "closing ) for (?C expected" + #define ERR40 "recursive call could loop indefinitely" + #define ERR41 "unrecognized character after (?P" + #define ERR42 "syntax error after (?P" + #define ERR43 "two named groups have the same name" /* All character handling must be done as unsigned characters. Otherwise there are problems with top-bit-set characters and functions such as isspace(). *************** *** 307,334 **** typedef unsigned char uschar; ! /* The real format of the start of the pcre block; the actual code vector ! runs on as long as necessary after the end. */ typedef struct real_pcre { unsigned long int magic_number; ! size_t size; ! const unsigned char *tables; unsigned long int options; unsigned short int top_bracket; unsigned short int top_backref; ! uschar first_char; ! uschar req_char; ! uschar code[1]; } real_pcre; ! /* The real format of the extra block returned by pcre_study(). */ ! typedef struct real_pcre_extra { uschar options; uschar start_bits[32]; ! } real_pcre_extra; ! /* Structure for passing "static" information around between the functions doing the compiling, so that they are thread-safe. */ --- 456,484 ---- typedef unsigned char uschar; ! /* The real format of the start of the pcre block; the index of names and the ! code vector run on as long as necessary after the end. */ typedef struct real_pcre { unsigned long int magic_number; ! size_t size; /* Total that was malloced */ ! const unsigned char *tables; /* Pointer to tables */ unsigned long int options; unsigned short int top_bracket; unsigned short int top_backref; ! unsigned short int first_byte; ! unsigned short int req_byte; ! unsigned short int name_entry_size; /* Size of any name items; 0 => none */ ! unsigned short int name_count; /* Number of name items */ } real_pcre; ! /* The format of the block used to store data from pcre_study(). */ ! typedef struct pcre_study_data { ! size_t size; /* Total that was malloced */ uschar options; uschar start_bits[32]; ! } pcre_study_data; /* Structure for passing "static" information around between the functions doing the compiling, so that they are thread-safe. */ *************** *** 338,350 **** const uschar *fcc; /* Points to case-flipping table */ const uschar *cbits; /* Points to character type table */ const uschar *ctypes; /* Points to table of type maps */ } compile_data; /* Structure for passing "static" information around between the functions doing the matching, so that they are thread-safe. */ typedef struct match_data { ! int errorcode; /* As it says */ int *offset_vector; /* Offset vector */ int offset_end; /* One past the end */ int offset_max; /* The maximum usable for return data */ --- 488,528 ---- const uschar *fcc; /* Points to case-flipping table */ const uschar *cbits; /* Points to character type table */ const uschar *ctypes; /* Points to table of type maps */ + const uschar *start_code; /* The start of the compiled code */ + uschar *name_table; /* The name/number table */ + int names_found; /* Number of entries so far */ + int name_entry_size; /* Size of each entry */ + int top_backref; /* Maximum back reference */ + unsigned int backref_map; /* Bitmap of low back refs */ + int req_varyopt; /* "After variable item" flag for reqbyte */ } compile_data; + /* Structure for maintaining a chain of pointers to the currently incomplete + branches, for testing for left recursion. */ + + typedef struct branch_chain { + struct branch_chain *outer; + uschar *current; + } branch_chain; + + /* Structure for items in a linked list that represents an explicit recursive + call within the pattern. */ + + typedef struct recursion_info { + struct recursion_info *prev; /* Previous recursion record (or NULL) */ + int group_num; /* Number of group that was called */ + const uschar *after_call; /* "Return value": points after the call in the expr */ + const uschar *save_start; /* Old value of md->start_match */ + int *offset_save; /* Pointer to start of saved offsets */ + int saved_max; /* Number of saved offsets */ + } recursion_info; + /* Structure for passing "static" information around between the functions doing the matching, so that they are thread-safe. */ typedef struct match_data { ! unsigned long int match_call_count; /* As it says */ ! unsigned long int match_limit; /* As it says */ int *offset_vector; /* Offset vector */ int offset_end; /* One past the end */ int offset_max; /* The maximum usable for return data */ *************** *** 356,367 **** BOOL utf8; /* UTF8 flag */ BOOL endonly; /* Dollar not before final \n */ BOOL notempty; /* Empty string match not wanted */ ! const uschar *start_pattern; /* For use when recursing */ const uschar *start_subject; /* Start of the subject string */ const uschar *end_subject; /* End of the subject string */ const uschar *start_match; /* Start of this match attempt */ const uschar *end_match_ptr; /* Subject position at end match */ int end_offset_top; /* Highwater mark at end of match */ } match_data; /* Bit definitions for entries in the pcre_ctypes table. */ --- 534,549 ---- BOOL utf8; /* UTF8 flag */ BOOL endonly; /* Dollar not before final \n */ BOOL notempty; /* Empty string match not wanted */ ! const uschar *start_code; /* For use when recursing */ const uschar *start_subject; /* Start of the subject string */ const uschar *end_subject; /* End of the subject string */ const uschar *start_match; /* Start of this match attempt */ const uschar *end_match_ptr; /* Subject position at end match */ int end_offset_top; /* Highwater mark at end of match */ + int capture_last; /* Most recent capture number */ + int start_offset; /* The start offset value */ + recursion_info *recursive; /* Linked list of recursion data */ + void *callout_data; /* To pass back to callouts */ } match_data; /* Bit definitions for entries in the pcre_ctypes table. */ *************** *** 398,560 **** #define tables_length (ctypes_offset + 256) /* End of internal.h */ ! ! /* Define DEBUG to get debugging output on stdout. */ ! ! /* #define DEBUG */ ! ! /* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef ! inline, and there are *still* stupid compilers about that don't like indented ! pre-processor statements. I suppose it's only been 10 years... */ ! ! #ifdef DEBUG ! #define DPRINTF(p) printf p ! #else ! #define DPRINTF(p) /*nothing */ ! #endif ! ! #ifdef BACKTRACK_LIMIT ! jmp_buf backtrack_jmp; ! static unsigned int backtrack_count = 0; ! #endif ! ! /* Maximum number of items on the nested bracket stacks at compile time. This ! applies to the nesting of all kinds of parentheses. It does not limit ! un-nested, non-capturing parentheses. This number can be made bigger if ! necessary - it is used to dimension one int and one unsigned char vector at ! compile time. */ ! ! #define BRASTACK_SIZE 200 ! ! ! /* The number of bytes in a literal character string above which we can't add ! any more is different when UTF-8 characters may be encountered. */ ! ! #define MAXLIT 255 ! ! ! /* Min and max values for the common repeats; for the maxima, 0 => infinity */ ! ! static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; ! static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; ! ! /* Text forms of OP_ values and things, for debugging (not all used) */ ! ! #ifdef DEBUG ! static const char *OP_names[] = { ! "End", "\\A", "\\B", "\\b", "\\D", "\\d", ! "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z", ! "Opt", "^", "$", "Any", "chars", "not", ! "*", "*?", "+", "+?", "?", "??", "{", "{", "{", ! "*", "*?", "+", "+?", "?", "??", "{", "{", "{", ! "*", "*?", "+", "+?", "?", "??", "{", "{", "{", ! "*", "*?", "+", "+?", "?", "??", "{", "{", ! "class", "Ref", "Recurse", ! "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", ! "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref", ! "Brazero", "Braminzero", "Branumber", "Bra" ! }; ! #endif ! ! /* Table for handling escaped characters in the range '0'-'z'. Positive returns ! are simple data values; negative values are for special things like \d and so ! on. Zero means further processing is needed (for things like \x), or the escape ! is invalid. */ ! ! static const short int escapes[] = { ! 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ ! 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ ! '@', -ESC_A, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ ! 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ ! 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ ! 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ ! '`', 7, -ESC_b, 0, -ESC_d, ESC_E, ESC_F, 0, /* ` - g */ ! 0, 0, 0, 0, 0, 0, ESC_N, 0, /* h - o */ ! 0, 0, ESC_R, -ESC_s, ESC_T, 0, 0, -ESC_w, /* p - w */ ! 0, 0, -ESC_z /* x - z */ ! }; ! ! /* Tables of names of POSIX character classes and their lengths. The list is ! terminated by a zero length entry. The first three must be alpha, upper, lower, ! as this is assumed for handling case independence. */ ! ! static const char *posix_names[] = { ! "alpha", "lower", "upper", ! "alnum", "ascii", "cntrl", "digit", "graph", ! "print", "punct", "space", "word", "xdigit" ! }; ! ! static const uschar posix_name_lengths[] = { ! 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 ! }; ! ! /* Table of class bit maps for each POSIX class; up to three may be combined ! to form the class. */ ! ! static const int posix_class_maps[] = { ! cbit_lower, cbit_upper, -1, /* alpha */ ! cbit_lower, -1, -1, /* lower */ ! cbit_upper, -1, -1, /* upper */ ! cbit_digit, cbit_lower, cbit_upper, /* alnum */ ! cbit_print, cbit_cntrl, -1, /* ascii */ ! cbit_cntrl, -1, -1, /* cntrl */ ! cbit_digit, -1, -1, /* digit */ ! cbit_graph, -1, -1, /* graph */ ! cbit_print, -1, -1, /* print */ ! cbit_punct, -1, -1, /* punct */ ! cbit_space, -1, -1, /* space */ ! cbit_word, -1, -1, /* word */ ! cbit_xdigit, -1, -1 /* xdigit */ ! }; ! ! ! /* Definition to allow mutual recursion */ ! ! static BOOL ! compile_regex(int, int, int *, uschar **, const uschar **, const char **, ! BOOL, int, int *, int *, compile_data *); ! ! /* Structure for building a chain of data that actually lives on the ! stack, for holding the values of the subject pointer at the start of each ! subpattern, so as to detect when an empty string has been matched by a ! subpattern - to break infinite loops. */ ! ! typedef struct eptrblock { ! struct eptrblock *prev; ! const uschar *saved_eptr; ! } eptrblock; ! ! /* Flag bits for the match() function */ ! ! #define match_condassert 0x01 /* Called to check a condition assertion */ ! #define match_isgroup 0x02 /* Set if start of bracketed group */ ! ! ! ! /************************************************* ! * Global variables * ! *************************************************/ ! ! /* PCRE is thread-clean and doesn't use any global variables in the normal ! sense. However, it calls memory allocation and free functions via the two ! indirections below, which are can be changed by the caller, but are shared ! between all threads. */ ! ! #define pcre_malloc malloc ! #define pcre_free free ! ! /************************************************* ! * Macros and tables for character handling * ! *************************************************/ ! ! /* When UTF-8 encoding is being used, a character is no longer just a single ! byte. The macros for character handling generate simple sequences when used in ! byte-mode, and more complicated ones for UTF-8 characters. */ ! ! #define GETCHARINC(c, eptr) c = *eptr++; ! #define GETCHARLEN(c, eptr, len) c = *eptr; ! #define BACKCHAR(eptr) ! /************************************************* * Perl-Compatible Regular Expressions * *************************************************/ --- 580,586 ---- #define tables_length (ctypes_offset + 256) /* End of internal.h */ ! /* chartables.c */ /************************************************* * Perl-Compatible Regular Expressions * *************************************************/ *************** *** 705,711 **** */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0- 7 */ ! 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 8- 15 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16- 23 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24- 31 */ 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* - ' */ --- 731,737 ---- */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0- 7 */ ! 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, /* 8- 15 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16- 23 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24- 31 */ 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* - ' */ *************** *** 739,833 **** }; /* 248-255 */ /* End of chartables.c */ ! ! ! ! #ifdef DEBUG /************************************************* ! * Debugging function to print chars * *************************************************/ ! /* Print a sequence of chars in printable format, stopping at the end of the ! subject if the requested. Arguments: ! p points to characters ! length number to print ! is_subject TRUE if printing from within md->start_subject ! md pointer to matching data block, if is_subject is TRUE ! Returns: nothing */ ! static void ! pchars(const uschar * p, int length, BOOL is_subject, match_data * md) { ! int c; ! if (is_subject && length > md->end_subject - p) ! length = md->end_subject - p; ! while (length-- > 0) ! if (isprint(c = *(p++))) ! printf("%c", c); ! else ! printf("\\x%02x", c); } - #endif - - - /************************************************* ! * Handle escapes * *************************************************/ ! /* This function is called when a \ has been encountered. It either returns a ! positive value for a simple escape such as \n, or a negative value which ! encodes one of the more complicated things such as \d. When UTF-8 is enabled, ! a positive value greater than 255 may be returned. On entry, ptr is pointing at ! the \. On exit, it is on the final character of the escape sequence. ! ! Arguments: ! ptrptr points to the pattern position pointer ! errorptr points to the pointer to the error message ! bracount number of previous extracting brackets ! options the options bits ! isclass TRUE if inside a character class ! cd pointer to char tables block ! Returns: zero or positive => a data character ! negative => a special escape sequence ! on error, errorptr is set */ ! static int ! check_escape(const uschar ** ptrptr, const char **errorptr, int bracount, ! int options, BOOL isclass, compile_data * cd) { ! const uschar *ptr = *ptrptr; ! int c, i; ! /* If backslash is at the end of the pattern, it's an error. */ ! c = *(++ptr); ! if (c == 0) ! *errorptr = ERR1; ! /* Digits or letters may have special meaning; all others are literals. */ ! else if (c < '0' || c > 'z') { ! } ! /* Do an initial lookup in a table. A non-zero result is something that can be ! returned immediately. Otherwise further processing may be required. */ ! else if ((i = escapes[c - '0']) != 0) ! c = i; /* Escapes that need further processing, or are illegal. */ else { const uschar *oldptr; switch (c) { /* The handling of escape sequences consisting of a string of digits starting with one that is not zero is not straightforward. By experiment, the way Perl works seems to be as follows: --- 765,1521 ---- }; /* 248-255 */ /* End of chartables.c */ ! /* get.c */ /************************************************* ! * Copy captured string to given buffer * *************************************************/ ! /* This function copies a single captured substring into a given buffer. ! Note that we use memcpy() rather than strncpy() in case there are binary zeros ! in the string. Arguments: ! subject the subject string that was matched ! ovector pointer to the offsets table ! stringcount the number of substrings that were captured ! (i.e. the yield of the pcre_exec call, unless ! that was zero, in which case it should be 1/3 ! of the offset table size) ! stringnumber the number of the required substring ! buffer where to put the substring ! size the size of the buffer ! Returns: if successful: ! the length of the copied string, not including the zero ! that is put on the end; can be zero ! if not successful: ! PCRE_ERROR_NOMEMORY (-6) buffer too small ! PCRE_ERROR_NOSUBSTRING (-7) no such captured substring */ ! int ! pcre_copy_substring(const char *subject, int *ovector, int stringcount, ! int stringnumber, char *buffer, int size) { ! int yield; ! if (stringnumber < 0 || stringnumber >= stringcount) ! return PCRE_ERROR_NOSUBSTRING; ! stringnumber *= 2; ! yield = ovector[stringnumber + 1] - ovector[stringnumber]; ! if (size < yield + 1) ! return PCRE_ERROR_NOMEMORY; ! memcpy(buffer, subject + ovector[stringnumber], yield); ! buffer[yield] = 0; ! return yield; } + /* End of get.c */ + /* maketables.c */ /************************************************* ! * Create PCRE character tables * *************************************************/ ! /* This function builds a set of character tables for use by PCRE and returns ! a pointer to them. They are build using the ctype functions, and consequently ! their contents will depend upon the current locale setting. When compiled as ! part of the library, the store is obtained via malloc(), but when compiled ! inside dftables, use malloc(). ! Arguments: none ! Returns: pointer to the contiguous block of data */ ! const unsigned char * ! pcre_maketables(void) { ! unsigned char *yield, *p; ! int i; ! yield = (unsigned char *) malloc(tables_length); ! if (yield == NULL) ! return NULL; ! p = yield; ! /* First comes the lower casing table */ ! for (i = 0; i < 256; i++) ! *p++ = tolower(i); ! /* Next the case-flipping table */ ! for (i = 0; i < 256; i++) ! *p++ = islower(i) ? toupper(i) : tolower(i); ! ! /* Then the character class tables. Don't try to be clever and save effort ! on exclusive ones - in some locales things may be different. Note that the ! table for "space" includes everything "isspace" gives, including VT in the ! default locale. This makes it work for the POSIX class [:space:]. */ ! ! memset(p, 0, cbit_length); ! for (i = 0; i < 256; i++) { ! if (isdigit(i)) { ! p[cbit_digit + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); ! } ! if (isupper(i)) { ! p[cbit_upper + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); ! } ! if (islower(i)) { ! p[cbit_lower + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); ! } ! if (i == '_') ! p[cbit_word + i / 8] |= 1 << (i & 7); ! if (isspace(i)) ! p[cbit_space + i / 8] |= 1 << (i & 7); ! if (isxdigit(i)) ! p[cbit_xdigit + i / 8] |= 1 << (i & 7); ! if (isgraph(i)) ! p[cbit_graph + i / 8] |= 1 << (i & 7); ! if (isprint(i)) ! p[cbit_print + i / 8] |= 1 << (i & 7); ! if (ispunct(i)) ! p[cbit_punct + i / 8] |= 1 << (i & 7); ! if (iscntrl(i)) ! p[cbit_cntrl + i / 8] |= 1 << (i & 7); ! } ! p += cbit_length; ! ! /* Finally, the character type table. In this, we exclude VT from the white ! space chars, because Perl doesn't recognize it as such for \s and for comments ! within regexes. */ ! ! for (i = 0; i < 256; i++) { ! int x = 0; ! if (i != 0x0b && isspace(i)) ! x += ctype_space; ! if (isalpha(i)) ! x += ctype_letter; ! if (isdigit(i)) ! x += ctype_digit; ! if (isxdigit(i)) ! x += ctype_xdigit; ! if (isalnum(i) || i == '_') ! x += ctype_word; ! if (strchr("*+?{^.$|()[", i) != 0) ! x += ctype_meta; ! *p++ = x; ! } ! ! return yield; ! } ! ! /* End of maketables.c */ ! /* study.c */ ! ! /************************************************* ! * Set a bit and maybe its alternate case * ! *************************************************/ ! ! /* Given a character, set its bit in the table, and also the bit for the other ! version of a letter if we are caseless. ! ! Arguments: ! start_bits points to the bit map ! c is the character ! caseless the caseless flag ! cd the block with char table pointers ! ! Returns: nothing ! */ ! ! static void ! set_bit(uschar * start_bits, int c, BOOL caseless, compile_data * cd) ! { ! start_bits[c / 8] |= (1 << (c & 7)); ! if (caseless && (cd->ctypes[c] & ctype_letter) != 0) ! start_bits[cd->fcc[c] / 8] |= (1 << (cd->fcc[c] & 7)); ! } ! ! ! ! /************************************************* ! * Create bitmap of starting chars * ! *************************************************/ ! ! /* This function scans a compiled unanchored expression and attempts to build a ! bitmap of the set of initial characters. If it can't, it returns FALSE. As time ! goes by, we may be able to get more clever at doing this. ! ! Arguments: ! code points to an expression ! start_bits points to a 32-byte table, initialized to 0 ! caseless the current state of the caseless flag ! utf8 TRUE if in UTF-8 mode ! cd the block with char table pointers ! ! Returns: TRUE if table built, FALSE otherwise ! */ ! ! static BOOL ! set_start_bits(const uschar * code, uschar * start_bits, BOOL caseless, ! BOOL utf8, compile_data * cd) ! { ! register int c; ! ! /* This next statement and the later reference to dummy are here in order to ! trick the optimizer of the IBM C compiler for OS/2 into generating correct ! code. Apparently IBM isn't going to fix the problem, and we would rather not ! disable optimization (in this module it actually makes a big difference, and ! the pcre module can use all the optimization it can get). */ ! ! volatile int dummy; ! ! do { ! const uschar *tcode = code + 1 + LINK_SIZE; ! BOOL try_next = TRUE; ! ! while (try_next) { ! /* If a branch starts with a bracket or a positive lookahead assertion, ! recurse to set bits from within them. That's all for this branch. */ ! ! if ((int) *tcode >= OP_BRA || *tcode == OP_ASSERT) { ! if (!set_start_bits(tcode, start_bits, caseless, utf8, cd)) ! return FALSE; ! try_next = FALSE; ! } ! ! else ! switch (*tcode) { ! default: ! return FALSE; ! ! /* Skip over callout */ ! ! case OP_CALLOUT: ! tcode += 2; ! break; ! ! /* Skip over extended extraction bracket number */ ! ! case OP_BRANUMBER: ! tcode += 3; ! break; ! ! /* Skip over lookbehind and negative lookahead assertions */ ! ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK: ! case OP_ASSERTBACK_NOT: ! do ! tcode += GET(tcode, 1); ! while (*tcode == OP_ALT); ! tcode += 1 + LINK_SIZE; ! break; ! ! /* Skip over an option setting, changing the caseless flag */ ! ! case OP_OPT: ! caseless = (tcode[1] & PCRE_CASELESS) != 0; ! tcode += 2; ! break; ! ! /* BRAZERO does the bracket, but carries on. */ ! ! case OP_BRAZERO: ! case OP_BRAMINZERO: ! if (!set_start_bits(++tcode, start_bits, caseless, utf8, cd)) ! return FALSE; ! dummy = 1; ! do ! tcode += GET(tcode, 1); ! while (*tcode == OP_ALT); ! tcode += 1 + LINK_SIZE; ! break; ! ! /* Single-char * or ? sets the bit and tries the next item */ ! ! case OP_STAR: ! case OP_MINSTAR: ! case OP_QUERY: ! case OP_MINQUERY: ! set_bit(start_bits, tcode[1], caseless, cd); ! tcode += 2; ! break; ! ! /* Single-char upto sets the bit and tries the next */ ! ! case OP_UPTO: ! case OP_MINUPTO: ! set_bit(start_bits, tcode[3], caseless, cd); ! tcode += 4; ! break; ! ! /* At least one single char sets the bit and stops */ ! ! case OP_EXACT: /* Fall through */ ! tcode++; ! ! case OP_CHARS: /* Fall through */ ! tcode++; ! ! case OP_PLUS: ! case OP_MINPLUS: ! set_bit(start_bits, tcode[1], caseless, cd); ! try_next = FALSE; ! break; ! ! /* Single character type sets the bits and stops */ ! ! case OP_NOT_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_digit]; ! try_next = FALSE; ! break; ! ! case OP_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_digit]; ! try_next = FALSE; ! break; ! ! case OP_NOT_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_space]; ! try_next = FALSE; ! break; ! ! case OP_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_space]; ! try_next = FALSE; ! break; ! ! case OP_NOT_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_word]; ! try_next = FALSE; ! break; ! ! case OP_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_word]; ! try_next = FALSE; ! break; ! ! /* One or more character type fudges the pointer and restarts, knowing ! it will hit a single character type and stop there. */ ! ! case OP_TYPEPLUS: ! case OP_TYPEMINPLUS: ! tcode++; ! break; ! ! case OP_TYPEEXACT: ! tcode += 3; ! break; ! ! /* Zero or more repeats of character types set the bits and then ! try again. */ ! ! case OP_TYPEUPTO: ! case OP_TYPEMINUPTO: ! tcode += 2; /* Fall through */ ! ! case OP_TYPESTAR: ! case OP_TYPEMINSTAR: ! case OP_TYPEQUERY: ! case OP_TYPEMINQUERY: ! switch (tcode[1]) { ! case OP_NOT_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_digit]; ! break; ! ! case OP_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_digit]; ! break; ! ! case OP_NOT_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_space]; ! break; ! ! case OP_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_space]; ! break; ! ! case OP_NOT_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_word]; ! break; ! ! case OP_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_word]; ! break; ! } ! ! tcode += 2; ! break; ! ! /* Character class where all the information is in a bit map: set the ! bits and either carry on or not, according to the repeat count. If it was ! a negative class, and we are operating with UTF-8 characters, any byte ! with the top-bit set is a potentially valid starter because it may start ! a character with a value > 255. (This is sub-optimal in that the ! character may be in the range 128-255, and those characters might be ! unwanted, but that's as far as we go for the moment.) */ ! ! case OP_NCLASS: ! if (utf8) ! memset(start_bits + 16, 0xff, 16); ! /* Fall through */ ! ! case OP_CLASS: ! { ! tcode++; ! for (c = 0; c < 32; c++) ! start_bits[c] |= tcode[c]; ! tcode += 32; ! switch (*tcode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! tcode++; ! break; ! ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! if (((tcode[1] << 8) + tcode[2]) == 0) ! tcode += 5; ! else ! try_next = FALSE; ! break; ! ! default: ! try_next = FALSE; ! break; ! } ! } ! break; /* End of bitmap class handling */ ! ! } /* End of switch */ ! } /* End of try_next loop */ ! ! code += GET(code, 1); /* Advance to next branch */ ! } ! while (*code == OP_ALT); ! return TRUE; ! } ! ! ! ! /************************************************* ! * Study a compiled expression * ! *************************************************/ ! ! /* This function is handed a compiled expression that it must study to produce ! information that will speed up the matching. It returns a pcre_extra block ! which then gets handed back to pcre_exec(). ! ! Arguments: ! re points to the compiled expression ! options contains option bits ! errorptr points to where to place error messages; ! set NULL unless error ! ! Returns: pointer to a pcre_extra block, with study_data filled in and the ! appropriate flag set; ! NULL on error or if no optimization possible ! */ ! ! pcre_extra * ! pcre_study(const pcre * external_re, int options, const char **errorptr) ! { ! uschar start_bits[32]; ! pcre_extra *extra; ! pcre_study_data *study; ! const real_pcre *re = (const real_pcre *) external_re; ! uschar *code = (uschar *) re + sizeof(real_pcre) + ! (re->name_count * re->name_entry_size); ! compile_data compile_block; ! ! *errorptr = NULL; ! ! if (re == NULL || re->magic_number != MAGIC_NUMBER) { ! *errorptr = "argument is not a compiled regular expression"; ! return NULL; ! } ! ! if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) { ! *errorptr = "unknown or incorrect option bit(s) set"; ! return NULL; ! } ! ! /* For an anchored pattern, or an unanchored pattern that has a first char, or ! a multiline pattern that matches only at "line starts", no further processing ! at present. */ ! ! if ((re->options & (PCRE_ANCHORED | PCRE_FIRSTSET | PCRE_STARTLINE)) != 0) ! return NULL; ! ! /* Set the character tables in the block which is passed around */ ! ! compile_block.lcc = re->tables + lcc_offset; ! compile_block.fcc = re->tables + fcc_offset; ! compile_block.cbits = re->tables + cbits_offset; ! compile_block.ctypes = re->tables + ctypes_offset; ! ! /* See if we can find a fixed set of initial characters for the pattern. */ ! ! memset(start_bits, 0, 32 * sizeof(uschar)); ! if (!set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0, ! (re->options & PCRE_UTF8) != 0, &compile_block)) ! return NULL; ! ! /* Get a pcre_extra block and a pcre_study_data block. The study data is put in ! the latter, which is pointed to by the former, which may also get additional ! data set later by the calling program. At the moment, the size of ! pcre_study_data is fixed. We nevertheless save it in a field for returning via ! the pcre_fullinfo() function so that if it becomes variable in the future, we ! don't have to change that code. */ ! ! extra = (pcre_extra *) (malloc) ! (sizeof(pcre_extra) + sizeof(pcre_study_data)); ! ! if (extra == NULL) { ! *errorptr = "failed to get memory"; ! return NULL; ! } ! ! study = (pcre_study_data *) ((char *) extra + sizeof(pcre_extra)); ! extra->flags = PCRE_EXTRA_STUDY_DATA; ! extra->study_data = study; ! ! study->size = sizeof(pcre_study_data); ! study->options = PCRE_STUDY_MAPPED; ! memcpy(study->start_bits, start_bits, sizeof(start_bits)); ! ! return extra; ! } ! ! /* End of study.c */ ! /* pcre.c */ ! #define DPRINTF(p) /*nothing */ ! ! /* Maximum number of items on the nested bracket stacks at compile time. This ! applies to the nesting of all kinds of parentheses. It does not limit ! un-nested, non-capturing parentheses. This number can be made bigger if ! necessary - it is used to dimension one int and one unsigned char vector at ! compile time. */ ! ! #define BRASTACK_SIZE 200 ! ! ! /* Maximum number of ints of offset to save on the stack for recursive calls. ! If the offset vector is bigger, malloc is used. This should be a multiple of 3, ! because the offset vector is always a multiple of 3 long. */ ! ! #define REC_STACK_SAVE_MAX 30 ! ! ! /* The number of bytes in a literal character string above which we can't add ! any more is set at 250 in order to allow for UTF-8 characters. (In theory it ! could be 255 when UTF-8 support is excluded, but that means that some of the ! test output would be different, which just complicates things.) */ ! ! #define MAXLIT 250 ! ! ! /* The maximum remaining length of subject we are prepared to search for a ! req_byte match. */ ! ! #define REQ_BYTE_MAX 1000 ! ! ! /* Table of sizes for the fixed-length opcodes. It's defined in a macro so that ! the definition is next to the definition of the opcodes in internal.h. */ ! ! static uschar OP_lengths[] = { OP_LENGTHS }; ! ! /* Min and max values for the common repeats; for the maxima, 0 => infinity */ ! ! static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; ! static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; ! ! /* Table for handling escaped characters in the range '0'-'z'. Positive returns ! are simple data values; negative values are for special things like \d and so ! on. Zero means further processing is needed (for things like \x), or the escape ! is invalid. */ ! ! static const short int escapes[] = { ! 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ ! 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ ! '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* @ - G */ ! 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ ! 0, -ESC_Q, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ ! 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ ! '`', 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, /* ` - g */ ! 0, 0, 0, 0, 0, 0, ESC_n, 0, /* h - o */ ! 0, 0, ESC_r, -ESC_s, ESC_t, 0, 0, -ESC_w, /* p - w */ ! 0, 0, -ESC_z /* x - z */ ! }; ! ! /* Tables of names of POSIX character classes and their lengths. The list is ! terminated by a zero length entry. The first three must be alpha, upper, lower, ! as this is assumed for handling case independence. */ ! ! static const char *posix_names[] = { ! "alpha", "lower", "upper", ! "alnum", "ascii", "blank", "cntrl", "digit", "graph", ! "print", "punct", "space", "word", "xdigit" ! }; ! ! static const uschar posix_name_lengths[] = { ! 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 ! }; ! ! /* Table of class bit maps for each POSIX class; up to three may be combined ! to form the class. The table for [:blank:] is dynamically modified to remove ! the vertical space characters. */ ! ! static const int posix_class_maps[] = { ! cbit_lower, cbit_upper, -1, /* alpha */ ! cbit_lower, -1, -1, /* lower */ ! cbit_upper, -1, -1, /* upper */ ! cbit_digit, cbit_lower, cbit_upper, /* alnum */ ! cbit_print, cbit_cntrl, -1, /* ascii */ ! cbit_space, -1, -1, /* blank - a GNU extension */ ! cbit_cntrl, -1, -1, /* cntrl */ ! cbit_digit, -1, -1, /* digit */ ! cbit_graph, -1, -1, /* graph */ ! cbit_print, -1, -1, /* print */ ! cbit_punct, -1, -1, /* punct */ ! cbit_space, -1, -1, /* space */ ! cbit_word, -1, -1, /* word - a Perl extension */ ! cbit_xdigit, -1, -1 /* xdigit */ ! }; ! ! ! /* Definition to allow mutual recursion */ ! ! static BOOL ! compile_regex(int, int, int *, uschar **, const uschar **, const char **, ! BOOL, int, int *, int *, branch_chain *, compile_data *); ! ! /* Structure for building a chain of data that actually lives on the ! stack, for holding the values of the subject pointer at the start of each ! subpattern, so as to detect when an empty string has been matched by a ! subpattern - to break infinite loops. */ ! ! typedef struct eptrblock { ! struct eptrblock *prev; ! const uschar *saved_eptr; ! } eptrblock; ! ! /* Flag bits for the match() function */ ! ! #define match_condassert 0x01 /* Called to check a condition assertion */ ! #define match_isgroup 0x02 /* Set if start of bracketed group */ ! ! /* Non-error returns from the match() function. Error returns are externally ! defined PCRE_ERROR_xxx codes, which are all negative. */ ! ! #define MATCH_MATCH 1 ! #define MATCH_NOMATCH 0 ! ! ! ! /************************************************* ! * Global variables * ! *************************************************/ ! ! /* PCRE is thread-clean and doesn't use any global variables in the normal ! sense. However, it calls memory allocation and free functions via the two ! indirections below, and it can optionally do callouts. These values can be ! changed by the caller, but are shared between all threads. However, when ! compiling for Virtual Pascal, things are done differently (see pcre.in). */ ! ! int (*pcre_callout) (pcre_callout_block *) = NULL; ! ! ! /************************************************* ! * Macros and tables for character handling * ! *************************************************/ ! ! #define GETCHAR(c, eptr) c = *eptr; ! #define GETCHARINC(c, eptr) c = *eptr++; ! #define GETCHARINCTEST(c, eptr) c = *eptr++; ! #define GETCHARLEN(c, eptr, len) c = *eptr; ! #define BACKCHAR(eptr) ! ! /************************************************* ! * Handle escapes * ! *************************************************/ ! ! /* This function is called when a \ has been encountered. It either returns a ! positive value for a simple escape such as \n, or a negative value which ! encodes one of the more complicated things such as \d. When UTF-8 is enabled, ! a positive value greater than 255 may be returned. On entry, ptr is pointing at ! the \. On exit, it is on the final character of the escape sequence. ! ! Arguments: ! ptrptr points to the pattern position pointer ! errorptr points to the pointer to the error message ! bracount number of previous extracting brackets ! options the options bits ! isclass TRUE if inside a character class ! cd pointer to char tables block ! ! Returns: zero or positive => a data character ! negative => a special escape sequence ! on error, errorptr is set ! */ ! ! static int ! check_escape(const uschar ** ptrptr, const char **errorptr, int bracount, ! int options, BOOL isclass, compile_data * cd) ! { ! const uschar *ptr = *ptrptr; ! int c, i; ! ! /* If backslash is at the end of the pattern, it's an error. */ ! ! c = *(++ptr); ! if (c == 0) ! *errorptr = ERR1; ! ! /* Digits or letters may have special meaning; all others are literals. */ ! ! else if (c < '0' || c > 'z') { ! } ! ! /* Do an initial lookup in a table. A non-zero result is something that can be ! returned immediately. Otherwise further processing may be required. */ ! ! else if ((i = escapes[c - '0']) != 0) ! c = i; /* Escapes that need further processing, or are illegal. */ else { const uschar *oldptr; switch (c) { + /* A number of Perl escapes are not handled by PCRE. We give an explicit + error. */ + + case 'l': + case 'L': + case 'N': + case 'p': + case 'P': + case 'u': + case 'U': + case 'X': + *errorptr = ERR37; + break; + /* The handling of escape sequences consisting of a string of digits starting with one that is not zero is not straightforward. By experiment, the way Perl works seems to be as follows: *************** *** 1036,1052 **** /************************************************* * Find the fixed length of a pattern * *************************************************/ /* Scan a pattern and compute the fixed length of subject that will match it, if the length is fixed. This is needed for dealing with backward assertions. Arguments: code points to the start of the pattern (the bracket) options the compiling options ! Returns: the fixed length, or -1 if there is no fixed length */ static int --- 1724,1796 ---- /************************************************* + * Find first significant op code * + *************************************************/ + + /* This is called by several functions that scan a compiled expression looking + for a fixed first character, or an anchoring op code etc. It skips over things + that do not influence this. For some calls, a change of option is important. + + Arguments: + code pointer to the start of the group + options pointer to external options + optbit the option bit whose changing is significant, or + zero if none are + + Returns: pointer to the first significant opcode + */ + + static const uschar * + first_significant_code(const uschar * code, int *options, int optbit) + { + for (;;) { + switch ((int) *code) { + case OP_OPT: + if (optbit > 0 && ((int) code[1] & optbit) != (*options & optbit)) + *options = (int) code[1]; + code += 2; + break; + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do + code += GET(code, 1); + while (*code == OP_ALT); + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_BRANUMBER: + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + code += OP_lengths[*code]; + break; + + default: + return code; + } + } + /* Control never reaches here */ + } + + + + + /************************************************* * Find the fixed length of a pattern * *************************************************/ /* Scan a pattern and compute the fixed length of subject that will match it, if the length is fixed. This is needed for dealing with backward assertions. + In UTF8 mode, the result is in characters rather than bytes. Arguments: code points to the start of the pattern (the bracket) options the compiling options ! Returns: the fixed length, or -1 if there is no fixed length, ! or -2 if \C was encountered */ static int *************** *** 1055,1061 **** int length = -1; register int branchlength = 0; ! register uschar *cc = code + 3; /* Scan along the opcodes for this branch. If we get to the end of the branch, check the length against that of the other branches. */ --- 1799,1805 ---- int length = -1; register int branchlength = 0; ! register uschar *cc = code + 1 + LINK_SIZE; /* Scan along the opcodes for this branch. If we get to the end of the branch, check the length against that of the other branches. */ *************** *** 1072,1083 **** case OP_COND: d = find_fixedlength(cc, options); if (d < 0) ! return -1; branchlength += d; do ! cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); ! cc += 3; break; /* Reached end of a branch; if it's a ket it is the end of a nested --- 1816,1827 ---- case OP_COND: d = find_fixedlength(cc, options); if (d < 0) ! return d; branchlength += d; do ! cc += GET(cc, 1); while (*cc == OP_ALT); ! cc += 1 + LINK_SIZE; break; /* Reached end of a branch; if it's a ket it is the end of a nested *************** *** 1095,1101 **** return -1; if (*cc != OP_ALT) return length; ! cc += 3; branchlength = 0; break; --- 1839,1845 ---- return -1; if (*cc != OP_ALT) return length; ! cc += 1 + LINK_SIZE; branchlength = 0; break; *************** *** 1106,1153 **** case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: do ! cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); - cc += 3; - break; - - /* Skip over things that don't match chars */ - - case OP_REVERSE: - case OP_BRANUMBER: - case OP_CREF: - cc++; /* Fall through */ ! case OP_OPT: ! cc++; ! /* Fall through */ case OP_SOD: case OP_EOD: case OP_EODN: case OP_CIRC: case OP_DOLL: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: ! cc++; break; /* Handle char strings. In UTF-8 mode we must count characters, not bytes. This requires a scan of the string, unfortunately. We assume valid UTF-8 ! strings, so all we do is reduce the length by one for byte whose bits are ! 10xxxxxx. */ case OP_CHARS: branchlength += *(++cc); cc += *cc + 1; break; ! /* Handle exact repetitions */ case OP_EXACT: case OP_TYPEEXACT: ! branchlength += (cc[1] << 8) + cc[2]; cc += 4; break; --- 1850,1897 ---- case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: do ! cc += GET(cc, 1); while (*cc == OP_ALT); /* Fall through */ ! /* Skip over things that don't match chars */ + case OP_REVERSE: + case OP_BRANUMBER: + case OP_CREF: + case OP_OPT: + case OP_CALLOUT: case OP_SOD: + case OP_SOM: case OP_EOD: case OP_EODN: case OP_CIRC: case OP_DOLL: case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: ! cc += OP_lengths[*cc]; break; /* Handle char strings. In UTF-8 mode we must count characters, not bytes. This requires a scan of the string, unfortunately. We assume valid UTF-8 ! strings, so all we do is reduce the length by one for every byte whose bits ! are 10xxxxxx. */ case OP_CHARS: branchlength += *(++cc); cc += *cc + 1; break; ! /* Handle exact repetitions. The count is already in characters, but we ! need to skip over a multibyte character in UTF8 mode. */ case OP_EXACT: + branchlength += GET2(cc, 1); + cc += 4; + break; + case OP_TYPEEXACT: ! branchlength += GET2(cc, 1); cc += 4; break; *************** *** 1164,1173 **** --- 1908,1923 ---- cc++; break; + /* The single-byte matcher isn't allowed */ + + case OP_ANYBYTE: + return -2; /* Check a class for variable quantification */ + case OP_CLASS: + case OP_NCLASS: cc += 33; switch (*cc) { *************** *** 1179,1187 **** case OP_CRRANGE: case OP_CRMINRANGE: ! if ((cc[1] << 8) + cc[2] != (cc[3] << 8) + cc[4]) return -1; ! branchlength += (cc[1] << 8) + cc[2]; cc += 5; break; --- 1929,1937 ---- case OP_CRRANGE: case OP_CRMINRANGE: ! if (GET2(cc, 1) != GET2(cc, 3)) return -1; ! branchlength += GET2(cc, 1); cc += 5; break; *************** *** 1203,1208 **** --- 1953,2154 ---- /************************************************* + * Scan compiled regex for numbered bracket * + *************************************************/ + + /* This little function scans through a compiled pattern until it finds a + capturing bracket with the given number. + + Arguments: + code points to start of expression + utf8 TRUE in UTF-8 mode + number the required bracket number + + Returns: pointer to the opcode for the bracket, or NULL if not found + */ + + static const uschar * + find_bracket(const uschar * code, int number) + { + + for (;;) { + register int c = *code; + if (c == OP_END) + return NULL; + else if (c == OP_CHARS) + code += code[1] + OP_lengths[c]; + else if (c > OP_BRA) { + int n = c - OP_BRA; + if (n > EXTRACT_BASIC_MAX) + n = GET2(code, 2 + LINK_SIZE); + if (n == number) + return (uschar *) code; + code += OP_lengths[OP_BRA]; + } else { + code += OP_lengths[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed + by a multi-byte character. The length in the table is a minimum, so we have + to scan along to skip the extra characters. All opcodes are less than 128, + so we can use relatively efficient code. */ + + } + } + } + + + + /************************************************* + * Scan compiled branch for non-emptiness * + *************************************************/ + + /* This function scans through a branch of a compiled pattern to see whether it + can match the empty string or not. It is called only from could_be_empty() + below. Note that first_significant_code() skips over assertions. If we hit an + unclosed bracket, we return "empty" - this means we've struck an inner bracket + whose current branch will already have been scanned. + + Arguments: + code points to start of search + endcode points to where to stop + utf8 TRUE if in UTF8 mode + + Returns: TRUE if what is matched could be empty + */ + + static BOOL + could_be_empty_branch(const uschar * code, const uschar * endcode, BOOL utf8) + { + register int c; + for (code = first_significant_code(code + 1 + LINK_SIZE, NULL, 0); + code < endcode; + code = first_significant_code(code + OP_lengths[c], NULL, 0)) { + const uschar *ccode; + + c = *code; + + if (c >= OP_BRA) { + BOOL empty_branch; + if (GET(code, 1) == 0) + return TRUE; /* Hit unclosed bracket */ + + /* Scan a closed bracket */ + + empty_branch = FALSE; + do { + if (!empty_branch && could_be_empty_branch(code, endcode, utf8)) + empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); + if (!empty_branch) + return FALSE; /* All branches are non-empty */ + code += 1 + LINK_SIZE; + c = *code; + } + + else + switch (c) { + /* Check for quantifiers after a class */ + + + case OP_CLASS: + case OP_NCLASS: + ccode = code + 33; + + + switch (*ccode) { + case OP_CRSTAR: /* These could be empty; continue */ + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + break; + + default: /* Non-repeat => class must match */ + case OP_CRPLUS: /* These repeats aren't empty */ + case OP_CRMINPLUS: + return FALSE; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(ccode, 1) > 0) + return FALSE; /* Minimum > 0 */ + break; + } + break; + + /* Opcodes that must match a character */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ANYBYTE: + case OP_CHARS: + case OP_NOT: + case OP_PLUS: + case OP_MINPLUS: + case OP_EXACT: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTEXACT: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEEXACT: + return FALSE; + + /* End of branch */ + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + return TRUE; + + } + } + + return TRUE; + } + + + + /************************************************* + * Scan compiled regex for non-emptiness * + *************************************************/ + + /* This function is called to check for left recursive calls. We want to check + the current branch of the current pattern to see if it could match the empty + string. If it could, we must look outwards for branches at other levels, + stopping when we pass beyond the bracket which is the subject of the recursion. + + Arguments: + code points to start of the recursion + endcode points to where to stop (current RECURSE item) + bcptr points to the chain of current (unclosed) branch starts + utf8 TRUE if in UTF-8 mode + + Returns: TRUE if what is matched could be empty + */ + + static BOOL + could_be_empty(const uschar * code, const uschar * endcode, + branch_chain * bcptr, BOOL utf8) + { + while (bcptr != NULL && bcptr->current >= code) { + if (!could_be_empty_branch(bcptr->current, endcode, utf8)) + return FALSE; + bcptr = bcptr->outer; + } + return TRUE; + } + + + + /************************************************* * Check for POSIX class syntax * *************************************************/ *************** *** 1273,1336 **** * Compile one branch * *************************************************/ ! /* Scan the pattern, compiling it into the code vector. Arguments: ! options the option bits ! brackets points to number of extracting brackets used ! code points to the pointer to the current code point ! ptrptr points to the current pattern pointer ! errorptr points to pointer to error message ! optchanged set to the value of the last OP_OPT item compiled ! reqchar set to the last literal character required, else -1 ! countlits set to count of mandatory literal characters ! cd contains pointers to tables ! Returns: TRUE on success ! FALSE, with *errorptr set on error */ static BOOL ! compile_branch(int options, int *brackets, uschar ** codeptr, ! const uschar ** ptrptr, const char **errorptr, int *optchanged, ! int *reqchar, int *countlits, compile_data * cd) { int repeat_type, op_type; ! int repeat_min, repeat_max; ! int bravalue, length; int greedy_default, greedy_non_default; ! int prevreqchar; int condcount = 0; ! int subcountlits = 0; register int c; register uschar *code = *codeptr; uschar *tempcode; const uschar *ptr = *ptrptr; const uschar *tempptr; uschar *previous = NULL; uschar class[32]; /* Set up the default and non-default settings for greediness */ greedy_default = ((options & PCRE_UNGREEDY) != 0); greedy_non_default = greedy_default ^ 1; ! /* Initialize no required char, and count of literals */ ! *reqchar = prevreqchar = -1; ! *countlits = 0; /* Switch on next character until the end of the branch */ for (;; ptr++) { BOOL negate_class; int class_charcount; int class_lastchar; int newoptions; int skipbytes; ! int subreqchar; c = *ptr; if ((options & PCRE_EXTENDED) != 0) { if ((cd->ctypes[c] & ctype_space) != 0) continue; --- 2219,2311 ---- * Compile one branch * *************************************************/ ! /* Scan the pattern, compiling it into the code vector. If the options are ! changed during the branch, the pointer is used to change the external options ! bits. Arguments: ! optionsptr pointer to the option bits ! brackets points to number of extracting brackets used ! code points to the pointer to the current code point ! ptrptr points to the current pattern pointer ! errorptr points to pointer to error message ! firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) ! reqbyteptr set to the last literal character required, else < 0 ! bcptr points to current branch chain ! cd contains pointers to tables etc. ! Returns: TRUE on success ! FALSE, with *errorptr set on error */ static BOOL ! compile_branch(int *optionsptr, int *brackets, uschar ** codeptr, ! const uschar ** ptrptr, const char **errorptr, int *firstbyteptr, ! int *reqbyteptr, branch_chain * bcptr, compile_data * cd) { int repeat_type, op_type; ! int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ ! int bravalue = 0; ! int length; int greedy_default, greedy_non_default; ! int firstbyte, reqbyte; ! int zeroreqbyte, zerofirstbyte; ! int req_caseopt, reqvary, tempreqvary; int condcount = 0; ! int options = *optionsptr; register int c; register uschar *code = *codeptr; uschar *tempcode; + BOOL inescq = FALSE; + BOOL groupsetfirstbyte = FALSE; const uschar *ptr = *ptrptr; const uschar *tempptr; uschar *previous = NULL; uschar class[32]; + BOOL utf8 = FALSE; + /* Set up the default and non-default settings for greediness */ greedy_default = ((options & PCRE_UNGREEDY) != 0); greedy_non_default = greedy_default ^ 1; ! /* Initialize no first char, no required char. REQ_UNSET means "no char ! matching encountered yet". It gets changed to REQ_NONE if we hit something that ! matches a non-fixed char first char; reqbyte just remains unset if we never ! find one. ! ! When we hit a repeat whose minimum is zero, we may have to adjust these values ! to take the zero repeat into account. This is implemented by setting them to ! zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual ! item types that can be repeated set these backoff variables appropriately. */ ! ! firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET; ! ! /* The variable req_caseopt contains either the REQ_CASELESS value or zero, ! according to the current setting of the caseless flag. REQ_CASELESS is a bit ! value > 255. It is added into the firstbyte or reqbyte variables to record the ! case status of the value. */ ! req_caseopt = ((options & PCRE_CASELESS) != 0) ? REQ_CASELESS : 0; /* Switch on next character until the end of the branch */ for (;; ptr++) { BOOL negate_class; + BOOL possessive_quantifier; int class_charcount; int class_lastchar; int newoptions; + int recno; int skipbytes; ! int subreqbyte; ! int subfirstbyte; c = *ptr; + if (inescq && c != 0) + goto NORMAL_CHAR; + if ((options & PCRE_EXTENDED) != 0) { if ((cd->ctypes[c] & ctype_space) != 0) continue; *************** *** 1338,1344 **** /* The space before the ; is to avoid a warning on a silly compiler on the Macintosh. */ while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! continue; } } --- 2313,2320 ---- /* The space before the ; is to avoid a warning on a silly compiler on the Macintosh. */ while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! if (c != 0) ! continue; /* Else fall through to handle end of string */ } } *************** *** 1348,1360 **** case 0: case '|': case ')': *codeptr = code; *ptrptr = ptr; return TRUE; ! /* Handle single-character metacharacters */ case '^': previous = NULL; *code++ = OP_CIRC; break; --- 2324,2343 ---- case 0: case '|': case ')': + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; *codeptr = code; *ptrptr = ptr; return TRUE; ! /* Handle single-character metacharacters. In multiline mode, ^ disables ! the setting of any following char as a first character. */ case '^': + if ((options & PCRE_MULTILINE) != 0) { + if (firstbyte == REQ_UNSET) + firstbyte = REQ_NONE; + } previous = NULL; *code++ = OP_CIRC; break; *************** *** 1364,1418 **** *code++ = OP_DOLL; break; case '.': previous = code; *code++ = OP_ANY; break; ! /* Character classes. These always build a 32-byte bitmap of the permitted ! characters, except in the special case where there is only one character. ! For negated classes, we build the map as usual, then invert it at the end. */ case '[': previous = code; ! *code++ = OP_CLASS; /* If the first character is '^', set the negation flag and skip it. */ if ((c = *(++ptr)) == '^') { negate_class = TRUE; c = *(++ptr); ! } else negate_class = FALSE; ! /* Keep a count of chars so that we can optimize the case of just a single ! character. */ class_charcount = 0; class_lastchar = -1; /* Initialize the 32-char bit map to all zeros. We have to build the map in a temporary bit of store, in case the class contains only 1 ! character, because in that case the compiled code doesn't use the bit map. */ memset(class, 0, 32 * sizeof(uschar)); /* Process characters until ] is reached. By writing this as a "do" it ! means that an initial ] is taken as a data character. */ do { ! if (c == 0) { ! *errorptr = ERR6; ! goto FAILED; } /* Handle POSIX class names. Perl allows a negation extension of the ! form [:^name]. A square bracket that doesn't match the syntax is treated as a literal. We also recognize the POSIX constructions [.ch.] and [=ch=] ("collating elements") and fault them, as Perl ! 5.6 does. */ if (c == '[' && (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && --- 2347,2436 ---- *code++ = OP_DOLL; break; + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqbyte doesn't change either. */ + case '.': + if (firstbyte == REQ_UNSET) + firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; previous = code; *code++ = OP_ANY; break; ! /* Character classes. If the included characters are all < 255 in value, we ! build a 32-byte bitmap of the permitted characters, except in the special ! case where there is only one such character. For negated classes, we build ! the map as usual, then invert it at the end. However, we use a different ! opcode so that data characters > 255 can be handled correctly. ! ! If the class contains characters outside the 0-255 range, a different ! opcode is compiled. It may optionally have a bit map for characters < 256, ! but those above are are explicitly listed afterwards. A flag byte tells ! whether the bitmap is present, and whether this is a negated class or not. */ case '[': previous = code; ! ! /* PCRE supports POSIX class stuff inside a class. Perl gives an error if ! they are encountered at the top level, so we'll do that too. */ ! ! if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && ! check_posix_syntax(ptr, &tempptr, cd)) { ! *errorptr = (ptr[1] == ':') ? ERR13 : ERR31; ! goto FAILED; ! } /* If the first character is '^', set the negation flag and skip it. */ if ((c = *(++ptr)) == '^') { negate_class = TRUE; c = *(++ptr); ! } else { negate_class = FALSE; + } ! /* Keep a count of chars with values < 256 so that we can optimize the case ! of just a single character (as long as it's < 256). For higher valued UTF-8 ! characters, we don't yet do any optimization. */ class_charcount = 0; class_lastchar = -1; + /* Initialize the 32-char bit map to all zeros. We have to build the map in a temporary bit of store, in case the class contains only 1 ! character (< 256), because in that case the compiled code doesn't use the bit map. */ memset(class, 0, 32 * sizeof(uschar)); /* Process characters until ] is reached. By writing this as a "do" it ! means that an initial ] is taken as a data character. The first pass ! through the regex checked the overall syntax, so we don't need to be very ! strict here. At the start of the loop, c contains the first byte of the ! character. */ do { ! ! /* Inside \Q...\E everything is literal except \E */ ! ! if (inescq) { ! if (c == '\\' && ptr[1] == 'E') { ! inescq = FALSE; ! ptr++; ! continue; ! } else ! goto LONE_SINGLE_CHARACTER; } /* Handle POSIX class names. Perl allows a negation extension of the ! form [:^name:]. A square bracket that doesn't match the syntax is treated as a literal. We also recognize the POSIX constructions [.ch.] and [=ch=] ("collating elements") and fault them, as Perl ! 5.6 and 5.8 do. */ if (c == '[' && (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && *************** *** 1446,1469 **** posix_class = 0; /* Or into the map we are building up to 3 of the static class ! tables, or their negations. */ posix_class *= 3; for (i = 0; i < 3; i++) { int taboffset = posix_class_maps[posix_class + i]; if (taboffset < 0) break; ! if (local_negate) for (c = 0; c < 32; c++) class[c] |= ~cbits[c + taboffset]; ! else for (c = 0; c < 32; c++) class[c] |= cbits[c + taboffset]; } ptr = tempptr + 1; class_charcount = 10; /* Set > 1; assumes more than 1 per class */ ! continue; } /* Backslash may introduce a single character, or it may introduce one --- 2464,2495 ---- posix_class = 0; /* Or into the map we are building up to 3 of the static class ! tables, or their negations. The [:blank:] class sets up the same ! chars as the [:space:] class (all white space). We remove the vertical ! white space chars afterwards. */ posix_class *= 3; for (i = 0; i < 3; i++) { + BOOL isblank = strncmp((char *) ptr, "blank", 5) == 0; int taboffset = posix_class_maps[posix_class + i]; if (taboffset < 0) break; ! if (local_negate) { for (c = 0; c < 32; c++) class[c] |= ~cbits[c + taboffset]; ! if (isblank) ! class[1] |= 0x3c; ! } else { for (c = 0; c < 32; c++) class[c] |= cbits[c + taboffset]; + if (isblank) + class[1] &= ~0x3c; + } } ptr = tempptr + 1; class_charcount = 10; /* Set > 1; assumes more than 1 per class */ ! continue; /* End of POSIX syntax handling */ } /* Backslash may introduce a single character, or it may introduce one *************** *** 1472,1486 **** Inside a class (and only there) it is treated as backspace. Elsewhere it marks a word boundary. Other escapes have preset maps ready to or into the one we are building. We assume they have more than one ! character in them, so set class_count bigger than one. */ if (c == '\\') { c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); if (-c == ESC_b) ! c = '\b'; else if (c < 0) { register const uschar *cbits = cd->cbits; ! class_charcount = 10; switch (-c) { case ESC_d: for (c = 0; c < 32; c++) --- 2498,2521 ---- Inside a class (and only there) it is treated as backspace. Elsewhere it marks a word boundary. Other escapes have preset maps ready to or into the one we are building. We assume they have more than one ! character in them, so set class_charcount bigger than one. */ if (c == '\\') { c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); if (-c == ESC_b) ! c = '\b'; /* \b is backslash in a class */ ! ! if (-c == ESC_Q) { /* Handle start of quoted string */ ! if (ptr[1] == '\\' && ptr[2] == 'E') { ! ptr += 2; /* avoid empty string */ ! } else ! inescq = TRUE; ! continue; ! } ! else if (c < 0) { register const uschar *cbits = cd->cbits; ! class_charcount = 10; /* Greater than 1 is what matters */ switch (-c) { case ESC_d: for (c = 0; c < 32; c++) *************** *** 1505,1541 **** case ESC_s: for (c = 0; c < 32; c++) class[c] |= cbits[c + cbit_space]; continue; case ESC_S: for (c = 0; c < 32; c++) class[c] |= ~cbits[c + cbit_space]; continue; default: ! *errorptr = ERR7; ! goto FAILED; } } ! /* Fall through if single character, but don't at present allow ! chars > 255 in UTF-8 mode. */ } /* A single character may be followed by '-' to form a range. However, Perl does not permit ']' to be the end of the range. A '-' character here is treated as a literal. */ - if (ptr[1] == '-' && ptr[2] != ']') { int d; ptr += 2; - d = *ptr; ! if (d == 0) { ! *errorptr = ERR6; ! goto FAILED; ! } /* The second part of a range can be a single-character escape, but not any of the other escapes. Perl 5.6 treats a hyphen as a literal --- 2540,2582 ---- case ESC_s: for (c = 0; c < 32; c++) class[c] |= cbits[c + cbit_space]; + class[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */ continue; case ESC_S: for (c = 0; c < 32; c++) class[c] |= ~cbits[c + cbit_space]; + class[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */ continue; + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + default: ! if ((options & PCRE_EXTRA) != 0) { ! *errorptr = ERR7; ! goto FAILED; ! } ! c = *ptr; /* The final character */ } } ! /* Fall through if we have a single character (c >= 0). This may be ! > 256 in UTF-8 mode. */ } + + /* End of backslash handling */ /* A single character may be followed by '-' to form a range. However, Perl does not permit ']' to be the end of the range. A '-' character here is treated as a literal. */ if (ptr[1] == '-' && ptr[2] != ']') { int d; ptr += 2; ! d = *ptr; /* The second part of a range can be a single-character escape, but not any of the other escapes. Perl 5.6 treats a hyphen as a literal *************** *** 1552,1567 **** d = '\b'; else { ptr = oldptr - 2; ! goto SINGLE_CHARACTER; /* A few lines below */ } } } if (d < c) { *errorptr = ERR8; goto FAILED; } for (; c <= d; c++) { class[c / 8] |= (1 << (c & 7)); if ((options & PCRE_CASELESS) != 0) { --- 2593,2619 ---- d = '\b'; else { ptr = oldptr - 2; ! goto LONE_SINGLE_CHARACTER; /* A few lines below */ } } } + /* Check that the two values are in the correct order */ + if (d < c) { *errorptr = ERR8; goto FAILED; } + /* If d is greater than 255, we can't just use the bit map, so set up + for the UTF-8 supporting class type. If we are not caseless, we can + just set up a single range. If we are caseless, the characters < 256 + are handled with a bitmap, in order to get the case-insensitive + handling. */ + + /* We use the bit map if the range is entirely < 255, or if part of it + is < 255 and matching is caseless. */ + for (; c <= d; c++) { class[c / 8] |= (1 << (c & 7)); if ((options & PCRE_CASELESS) != 0) { *************** *** 1571,1624 **** class_charcount++; /* in case a one-char range */ class_lastchar = c; } continue; /* Go get the next char in the class */ } /* Handle a lone single character - we can get here for a normal non-escape char, or after \ that introduces a single character. */ ! SINGLE_CHARACTER: ! class[c / 8] |= (1 << (c & 7)); ! if ((options & PCRE_CASELESS) != 0) { ! c = cd->fcc[c]; /* flip case */ class[c / 8] |= (1 << (c & 7)); } - class_charcount++; - class_lastchar = c; } /* Loop until ']' reached; the check for end of string happens inside the loop. This "while" is the end of the "do" above. */ ! while ((c = *(++ptr)) != ']'); ! /* If class_charcount is 1 and class_lastchar is not negative, we saw ! precisely one character. This doesn't need the whole 32-byte bit map. ! We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if ! it's negative. */ ! if (class_charcount == 1 && class_lastchar >= 0) { if (negate_class) { ! code[-1] = OP_NOT; } else { ! code[-1] = OP_CHARS; *code++ = 1; } *code++ = class_lastchar; } - /* Otherwise, negate the 32-byte map if necessary, and copy it into - the code vector. */ ! else { ! if (negate_class) ! for (c = 0; c < 32; c++) ! code[c] = ~class[c]; ! else ! memcpy(code, class, 32); ! code += 32; } break; /* Various kinds of repeat */ --- 2623,2716 ---- class_charcount++; /* in case a one-char range */ class_lastchar = c; } + continue; /* Go get the next char in the class */ } /* Handle a lone single character - we can get here for a normal non-escape char, or after \ that introduces a single character. */ ! LONE_SINGLE_CHARACTER: ! /* Handle a single-byte character */ ! { class[c / 8] |= (1 << (c & 7)); + if ((options & PCRE_CASELESS) != 0) { + c = cd->fcc[c]; /* flip case */ + class[c / 8] |= (1 << (c & 7)); + } + class_charcount++; + class_lastchar = c; } } /* Loop until ']' reached; the check for end of string happens inside the loop. This "while" is the end of the "do" above. */ ! while ((c = *(++ptr)) != ']' || inescq); ! /* If class_charcount is 1, we saw precisely one character with a value < ! 256. In UTF-8 mode, we can optimize if there were no characters >= 256 and ! the one character is < 128. In non-UTF-8 mode we can always optimize. ! ! The optimization throws away the bit map. We turn the item into a ! 1-character OP_CHARS if it's positive, or OP_NOT if it's negative. Note ! that OP_NOT does not support multibyte characters. In the positive case, it ! can cause firstbyte to be set. Otherwise, there can be no first char if ! this item is first, whatever repeat count may follow. In the case of ! reqbyte, save the previous value for reinstating. */ ! if (class_charcount == 1) { ! zeroreqbyte = reqbyte; if (negate_class) { ! if (firstbyte == REQ_UNSET) ! firstbyte = REQ_NONE; ! zerofirstbyte = firstbyte; ! *code++ = OP_NOT; } else { ! if (firstbyte == REQ_UNSET) { ! zerofirstbyte = REQ_NONE; ! firstbyte = class_lastchar | req_caseopt; ! } else { ! zerofirstbyte = firstbyte; ! reqbyte = class_lastchar | req_caseopt | cd->req_varyopt; ! } ! *code++ = OP_CHARS; *code++ = 1; } *code++ = class_lastchar; + break; /* End of class handling */ } ! /* End of 1-byte optimization */ ! /* Otherwise, if this is the first thing in the branch, there can be no ! first char setting, whatever the repeat count. Any reqbyte setting must ! remain unchanged after any kind of repeat. */ ! if (firstbyte == REQ_UNSET) ! firstbyte = REQ_NONE; ! zerofirstbyte = firstbyte; ! zeroreqbyte = reqbyte; ! ! /* If there are characters with values > 255, we have to compile an ! extended class, with its own opcode. If there are no characters < 256, ! we can omit the bitmap. */ ! ! ! /* If there are no characters > 255, negate the 32-byte map if necessary, ! and copy it into the code vector. If this is the first thing in the branch, ! there can be no first char setting, whatever the repeat count. Any reqbyte ! setting must remain unchanged after any kind of repeat. */ ! ! if (negate_class) { ! *code++ = OP_NCLASS; ! for (c = 0; c < 32; c++) ! code[c] = ~class[c]; ! } else { ! *code++ = OP_CLASS; ! memcpy(code, class, 32); } + code += 32; break; /* Various kinds of repeat */ *************** *** 1651,1696 **** goto FAILED; } ! /* If the next character is '?' this is a minimizing repeat, by default, ! but if PCRE_UNGREEDY is set, it works the other way round. Advance to the ! next character. */ ! if (ptr[1] == '?') { repeat_type = greedy_non_default; ptr++; } else repeat_type = greedy_default; /* If previous was a string of characters, chop off the last one and use it as the subject of the repeat. If there was only one character, we can ! abolish the previous item altogether. A repeat with a zero minimum wipes ! out any reqchar setting, backing up to the previous value. We must also ! adjust the countlits value. */ if (*previous == OP_CHARS) { ! int len = previous[1]; - if (repeat_min == 0) - *reqchar = prevreqchar; - *countlits += repeat_min - 1; ! if (len == 1) { ! c = previous[2]; ! code = previous; ! } else { ! c = previous[len + 1]; ! previous[1]--; ! code--; } ! op_type = 0; /* Use single-char op codes */ goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ } /* If previous was a single negated character ([^a] or similar), we use one of the special opcodes, replacing it. The code is shared with single- ! character repeats by adding a suitable offset into repeat_type. */ ! else if ((int) *previous == OP_NOT) { op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ c = previous[1]; code = previous; --- 2743,2832 ---- goto FAILED; } ! if (repeat_min == 0) { ! firstbyte = zerofirstbyte; /* Adjust for zero repeat */ ! reqbyte = zeroreqbyte; /* Ditto */ ! } ! /* Remember whether this is a variable length repeat */ ! ! reqvary = (repeat_min == repeat_max) ? 0 : REQ_VARY; ! ! op_type = 0; /* Default single-char op codes */ ! possessive_quantifier = FALSE; /* Default not possessive quantifier */ ! ! /* Save start of previous item, in case we have to move it up to make space ! for an inserted OP_ONCE for the additional '+' extension. */ ! ! tempcode = previous; ! ! /* If the next character is '+', we have a possessive quantifier. This ! implies greediness, whatever the setting of the PCRE_UNGREEDY option. ! If the next character is '?' this is a minimizing repeat, by default, ! but if PCRE_UNGREEDY is set, it works the other way round. We change the ! repeat type to the non-default. */ ! ! if (ptr[1] == '+') { ! repeat_type = 0; /* Force greedy */ ! possessive_quantifier = TRUE; ! ptr++; ! } else if (ptr[1] == '?') { repeat_type = greedy_non_default; ptr++; } else repeat_type = greedy_default; + /* If previous was a recursion, we need to wrap it inside brackets so that + it can be replicated if necessary. */ + + if (*previous == OP_RECURSE) { + memmove(previous + 1 + LINK_SIZE, previous, 1 + LINK_SIZE); + code += 1 + LINK_SIZE; + *previous = OP_BRA; + PUT(previous, 1, code - previous); + *code = OP_KET; + PUT(code, 1, code - previous); + code += 1 + LINK_SIZE; + } + /* If previous was a string of characters, chop off the last one and use it as the subject of the repeat. If there was only one character, we can ! abolish the previous item altogether. If a one-char item has a minumum of ! more than one, ensure that it is set in reqbyte - it might not be if a ! sequence such as x{3} is the first thing in a branch because the x will ! have gone into firstbyte instead. */ if (*previous == OP_CHARS) { ! /* Deal with UTF-8 characters that take up more than one byte. It's ! easier to write this out separately than try to macrify it. Use c to ! hold the length of the character in bytes, plus 0x80 to flag that it's a ! length rather than a small character. */ ! /* Handle the case of a single byte - either with no UTF8 support, or ! with UTF-8 disabled, or for a UTF-8 character < 128. */ ! ! { ! c = *(--code); ! if (code == previous + 2) { /* There was only one character */ ! code = previous; /* Abolish the previous item */ ! if (repeat_min > 1) ! reqbyte = c | req_caseopt | cd->req_varyopt; ! } else { ! previous[1]--; /* adjust length */ ! tempcode = code; /* Adjust position to be moved for '+' */ ! } } ! goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ } /* If previous was a single negated character ([^a] or similar), we use one of the special opcodes, replacing it. The code is shared with single- ! character repeats by setting opt_type to add a suitable offset into ! repeat_type. OP_NOT is currently used only for single-byte chars. */ ! else if (*previous == OP_NOT) { op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ c = previous[1]; code = previous; *************** *** 1699,1707 **** /* If previous was a character type match (\d or similar), abolish it and create a suitable repeat item. The code is shared with single-character ! repeats by adding a suitable offset into repeat_type. */ ! else if ((int) *previous < OP_EODN || *previous == OP_ANY) { op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ c = *previous; code = previous; --- 2835,2843 ---- /* If previous was a character type match (\d or similar), abolish it and create a suitable repeat item. The code is shared with single-character ! repeats by setting op_type to add a suitable offset into repeat_type. */ ! else if (*previous < OP_EODN) { op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ c = *previous; code = previous; *************** *** 1728,1735 **** *code++ = OP_QUERY + repeat_type; else { *code++ = OP_UPTO + repeat_type; ! *code++ = repeat_max >> 8; ! *code++ = (repeat_max & 255); } } --- 2864,2870 ---- *code++ = OP_QUERY + repeat_type; else { *code++ = OP_UPTO + repeat_type; ! PUT2INC(code, 0, repeat_max); } } *************** *** 1744,1751 **** else { if (repeat_min != 1) { *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ ! *code++ = repeat_min >> 8; ! *code++ = (repeat_min & 255); } /* If the mininum is 1 and the previous item was a character string, --- 2879,2885 ---- else { if (repeat_min != 1) { *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ ! PUT2INC(code, 0, repeat_min); } /* If the mininum is 1 and the previous item was a character string, *************** *** 1753,1800 **** length was 1, or add the character back onto the end of a longer string. For a character type nothing need be done; it will just get put back naturally. Note that the final character is always going to ! get added below. */ else if (*previous == OP_CHARS) { if (code == previous) code += 2; else previous[1]++; } /* For a single negated character we also have to put back the ! item that got cancelled. */ else if (*previous == OP_NOT) code++; ! /* If the maximum is unlimited, insert an OP_STAR. */ if (repeat_max < 0) { *code++ = c; *code++ = OP_STAR + repeat_type; } ! /* Else insert an UPTO if the max is greater than the min. */ else if (repeat_max != repeat_min) { *code++ = c; repeat_max -= repeat_min; *code++ = OP_UPTO + repeat_type; ! *code++ = repeat_max >> 8; ! *code++ = (repeat_max & 255); } } /* The character or character type itself comes last in all cases. */ *code++ = c; } /* If previous was a character class or a back reference, we put the repeat stuff after it, but just skip the item if the repeat was {0,0}. */ ! else if (*previous == OP_CLASS || *previous == OP_REF) { if (repeat_max == 0) { code = previous; goto END_REPEAT; --- 2887,2942 ---- length was 1, or add the character back onto the end of a longer string. For a character type nothing need be done; it will just get put back naturally. Note that the final character is always going to ! get added below, so we leave code ready for its insertion. */ else if (*previous == OP_CHARS) { if (code == previous) code += 2; else + /* In UTF-8 mode, a multibyte char has its length in c, with the 0x80 + bit set as a flag. The length will always be between 2 and 6. */ + previous[1]++; } /* For a single negated character we also have to put back the ! item that got cancelled. At present this applies only to single byte ! characters in any mode. */ else if (*previous == OP_NOT) code++; ! /* If the maximum is unlimited, insert an OP_STAR. Before doing so, ! we have to insert the character for the previous code. In UTF-8 mode, ! long characters have their length in c, with the 0x80 bit as a flag. */ if (repeat_max < 0) { *code++ = c; *code++ = OP_STAR + repeat_type; } ! /* Else insert an UPTO if the max is greater than the min, again ! preceded by the character, for the previously inserted code. */ else if (repeat_max != repeat_min) { *code++ = c; repeat_max -= repeat_min; *code++ = OP_UPTO + repeat_type; ! PUT2INC(code, 0, repeat_max); } } /* The character or character type itself comes last in all cases. */ + *code++ = c; } /* If previous was a character class or a back reference, we put the repeat stuff after it, but just skip the item if the repeat was {0,0}. */ ! else if (*previous == OP_CLASS || ! *previous == OP_NCLASS || *previous == OP_REF) { if (repeat_max == 0) { code = previous; goto END_REPEAT; *************** *** 1807,1826 **** *code++ = OP_CRQUERY + repeat_type; else { *code++ = OP_CRRANGE + repeat_type; ! *code++ = repeat_min >> 8; ! *code++ = repeat_min & 255; if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ ! *code++ = repeat_max >> 8; ! *code++ = repeat_max & 255; } } /* If previous was a bracket group, we may have to replicate it in certain cases. */ ! else if ((int) *previous >= OP_BRA || (int) *previous == OP_ONCE || ! (int) *previous == OP_COND) { register int i; int ketoffset = 0; int len = code - previous; --- 2949,2966 ---- *code++ = OP_CRQUERY + repeat_type; else { *code++ = OP_CRRANGE + repeat_type; ! PUT2INC(code, 0, repeat_min); if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ ! PUT2INC(code, 0, repeat_max); } } /* If previous was a bracket group, we may have to replicate it in certain cases. */ ! else if (*previous >= OP_BRA || *previous == OP_ONCE || ! *previous == OP_COND) { register int i; int ketoffset = 0; int len = code - previous; *************** *** 1835,1841 **** if (repeat_max == -1) { register uschar *ket = previous; do ! ket += (ket[1] << 8) + ket[2]; while (*ket != OP_KET); ketoffset = code - ket; } --- 2975,2981 ---- if (repeat_max == -1) { register uschar *ket = previous; do ! ket += GET(ket, 1); while (*ket != OP_KET); ketoffset = code - ket; } *************** *** 1848,1861 **** minimum is zero. */ if (repeat_min == 0) { - /* If we set up a required char from the bracket, we must back off - to the previous value and reset the countlits value too. */ - - if (subcountlits > 0) { - *reqchar = prevreqchar; - *countlits -= subcountlits; - } - /* If the maximum is also zero, we just omit the group from the output altogether. */ --- 2988,2993 ---- *************** *** 1882,1889 **** else { int offset; ! memmove(previous + 4, previous, len); ! code += 4; *previous++ = OP_BRAZERO + repeat_type; *previous++ = OP_BRA; --- 3014,3021 ---- else { int offset; ! memmove(previous + 2 + LINK_SIZE, previous, len); ! code += 2 + LINK_SIZE; *previous++ = OP_BRAZERO + repeat_type; *previous++ = OP_BRA; *************** *** 1892,1899 **** offset = (bralink == NULL) ? 0 : previous - bralink; bralink = previous; ! *previous++ = offset >> 8; ! *previous++ = offset & 255; } repeat_max--; --- 3024,3030 ---- offset = (bralink == NULL) ? 0 : previous - bralink; bralink = previous; ! PUTINC(previous, 0, offset); } repeat_max--; *************** *** 1901,1912 **** /* If the minimum is greater than zero, replicate the group as many times as necessary, and adjust the maximum to the number of subsequent ! copies that we need. */ else { ! for (i = 1; i < repeat_min; i++) { ! memcpy(code, previous, len); ! code += len; } if (repeat_max > 0) repeat_max -= repeat_min; --- 3032,3048 ---- /* If the minimum is greater than zero, replicate the group as many times as necessary, and adjust the maximum to the number of subsequent ! copies that we need. If we set a first char from the group, and didn't ! set a required char, copy the latter from the former. */ else { ! if (repeat_min > 1) { ! if (groupsetfirstbyte && reqbyte < 0) ! reqbyte = firstbyte; ! for (i = 1; i < repeat_min; i++) { ! memcpy(code, previous, len); ! code += len; ! } } if (repeat_max > 0) repeat_max -= repeat_min; *************** *** 1930,1937 **** *code++ = OP_BRA; offset = (bralink == NULL) ? 0 : code - bralink; bralink = code; ! *code++ = offset >> 8; ! *code++ = offset & 255; } memcpy(code, previous, len); --- 3066,3072 ---- *code++ = OP_BRA; offset = (bralink == NULL) ? 0 : code - bralink; bralink = code; ! PUTINC(code, 0, offset); } memcpy(code, previous, len); *************** *** 1945,1955 **** int oldlinkoffset; int offset = code - bralink + 1; uschar *bra = code - offset; ! oldlinkoffset = (bra[1] << 8) + bra[2]; bralink = (oldlinkoffset == 0) ? NULL : bralink - oldlinkoffset; *code++ = OP_KET; ! *code++ = bra[1] = offset >> 8; ! *code++ = bra[2] = (offset & 255); } } --- 3080,3090 ---- int oldlinkoffset; int offset = code - bralink + 1; uschar *bra = code - offset; ! oldlinkoffset = GET(bra, 1); bralink = (oldlinkoffset == 0) ? NULL : bralink - oldlinkoffset; *code++ = OP_KET; ! PUTINC(code, 0, offset); ! PUT(bra, 1, offset); } } *************** *** 1969,1978 **** goto FAILED; } ! /* In all case we no longer have a previous item. */ END_REPEAT: previous = NULL; break; --- 3104,3133 ---- goto FAILED; } ! /* If the character following a repeat is '+', we wrap the entire repeated ! item inside OP_ONCE brackets. This is just syntactic sugar, taken from ! Sun's Java package. The repeated item starts at tempcode, not at previous, ! which might be the first part of a string whose (former) last char we ! repeated. However, we don't support '+' after a greediness '?'. */ ! ! if (possessive_quantifier) { ! int len = code - tempcode; ! memmove(tempcode + 1 + LINK_SIZE, tempcode, len); ! code += 1 + LINK_SIZE; ! len += 1 + LINK_SIZE; ! tempcode[0] = OP_ONCE; ! *code++ = OP_KET; ! PUTINC(code, 0, len); ! PUT(tempcode, 1, len); ! } ! ! /* In all case we no longer have a previous item. We also set the ! "follows varying string" flag for subsequently encountered reqbytes if ! it isn't already set and we have just passed a varying length item. */ END_REPEAT: previous = NULL; + cd->req_varyopt |= reqvary; break; *************** *** 2005,2012 **** case '(': bravalue = OP_COND; /* Conditional group */ ! if ((cd->ctypes[*(++ptr)] & ctype_digit) != 0) { ! int condref = *ptr - '0'; while (*(++ptr) != ')') condref = condref * 10 + *ptr - '0'; if (condref == 0) { --- 3160,3180 ---- case '(': bravalue = OP_COND; /* Conditional group */ ! ! /* Condition to test for recursion */ ! ! if (ptr[1] == 'R') { ! code[1 + LINK_SIZE] = OP_CREF; ! PUT2(code, 2 + LINK_SIZE, CREF_RECURSE); ! skipbytes = 3; ! ptr += 3; ! } ! ! /* Condition to test for a numbered subpattern match */ ! ! else if ((cd->ctypes[ptr[1]] & ctype_digit) != 0) { ! int condref; /* Don't amalgamate; some compilers */ ! condref = *(++ptr) - '0'; /* grumble at autoincrement in declaration */ while (*(++ptr) != ')') condref = condref * 10 + *ptr - '0'; if (condref == 0) { *************** *** 2014,2025 **** goto FAILED; } ptr++; ! code[3] = OP_CREF; ! code[4] = condref >> 8; ! code[5] = condref & 255; skipbytes = 3; ! } else ! ptr--; break; case '=': /* Positive lookahead */ --- 3182,3193 ---- goto FAILED; } ptr++; ! code[1 + LINK_SIZE] = OP_CREF; ! PUT2(code, 2 + LINK_SIZE, condref); skipbytes = 3; ! } ! /* For conditions that are assertions, we just fall through, having ! set bravalue above. */ break; case '=': /* Positive lookahead */ *************** *** 2043,2065 **** bravalue = OP_ASSERTBACK_NOT; ptr++; break; ! default: /* Syntax error */ ! *errorptr = ERR24; ! goto FAILED; ! } ! break; ! case '>': /* One-time brackets */ ! bravalue = OP_ONCE; ! ptr++; ! break; ! case 'R': /* Pattern recursion */ ! *code++ = OP_RECURSE; ! ptr++; continue; default: /* Option setting */ set = unset = 0; optset = &set; --- 3211,3373 ---- bravalue = OP_ASSERTBACK_NOT; ptr++; break; + } + break; + + case '>': /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + case 'C': /* Callout - may be followed by digits */ + *code++ = OP_CALLOUT; + { + int n = 0; + while ((cd->ctypes[*(++ptr)] & ctype_digit) != 0) + n = n * 10 + *ptr - '0'; + if (n > 255) { + *errorptr = ERR38; + goto FAILED; + } + *code++ = n; + } + previous = NULL; + continue; + + case 'P': /* Named subpattern handling */ + if (*(++ptr) == '<') { /* Definition */ + int i, namelen; + uschar *slot = cd->name_table; + const uschar *name; /* Don't amalgamate; some compilers */ + name = ++ptr; /* grumble at autoincrement in declaration */ + + while (*ptr++ != '>') ; + namelen = ptr - name - 1; + + for (i = 0; i < cd->names_found; i++) { + int crc = memcmp(name, slot + 2, namelen); + if (crc == 0) { + if (slot[2 + namelen] == 0) { + *errorptr = ERR43; + goto FAILED; + } + crc = -1; /* Current name is substring */ + } + if (crc < 0) { + memmove(slot + cd->name_entry_size, slot, + (cd->names_found - i) * cd->name_entry_size); + break; + } + slot += cd->name_entry_size; + } + + PUT2(slot, 0, *brackets + 1); + memcpy(slot + 2, name, namelen); + slot[2 + namelen] = 0; + cd->names_found++; + goto NUMBERED_GROUP; + } + + if (*ptr == '=' || *ptr == '>') { /* Reference or recursion */ + int i, namelen; + int type = *ptr++; + const uschar *name = ptr; + uschar *slot = cd->name_table; + + while (*ptr != ')') + ptr++; + namelen = ptr - name; + + for (i = 0; i < cd->names_found; i++) { + if (strncmp((char *) name, (char *) slot + 2, namelen) == 0) + break; + slot += cd->name_entry_size; + } + if (i >= cd->names_found) { + *errorptr = ERR15; + goto FAILED; + } + + recno = GET2(slot, 0); + + if (type == '>') + goto HANDLE_RECURSION; /* A few lines below */ + + /* Back reference */ + + previous = code; + *code++ = OP_REF; + PUT2INC(code, 0, recno); + cd->backref_map |= (recno < 32) ? (1 << recno) : 1; + if (recno > cd->top_backref) + cd->top_backref = recno; + continue; + } + + /* Should never happen */ + break; + + case 'R': /* Pattern recursion */ + ptr++; /* Same as (?0) */ + /* Fall through */ + + /* Recursion or "subroutine" call */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + const uschar *called; + recno = 0; + + while ((cd->ctypes[*ptr] & ctype_digit) != 0) + recno = recno * 10 + *ptr++ - '0'; + + /* Come here from code above that handles a named recursion */ + + HANDLE_RECURSION: + + previous = code; + + /* Find the bracket that is being referenced. Temporarily end the + regex in case it doesn't exist. */ + + *code = OP_END; + called = (recno == 0) ? + cd->start_code : find_bracket(cd->start_code, recno); + + if (called == NULL) { + *errorptr = ERR15; + goto FAILED; + } ! /* If the subpattern is still open, this is a recursive call. We ! check to see if this is a left recursion that could loop for ever, ! and diagnose that case. */ ! ! if (GET(called, 1) == 0 ! && could_be_empty(called, code, bcptr, utf8)) { ! *errorptr = ERR40; ! goto FAILED; ! } ! /* Insert the recursion/subroutine item */ ! *code = OP_RECURSE; ! PUT(code, 1, called - cd->start_code); ! code += 1 + LINK_SIZE; ! } continue; + /* Character after (? not specially recognized */ + default: /* Option setting */ set = unset = 0; optset = &set; *************** *** 2088,2097 **** case 'X': *optset |= PCRE_EXTRA; break; - - default: - *errorptr = ERR12; - goto FAILED; } } --- 3396,3401 ---- *************** *** 2100,2120 **** newoptions = (options | set) & (~unset); /* If the options ended with ')' this is not the start of a nested ! group with option changes, so the options change at this level. At top ! level there is nothing else to be done (the options will in fact have ! been set from the start of compiling as a result of the first pass) but ! at an inner level we must compile code to change the ims options if ! necessary, and pass the new setting back so that it can be put at the ! start of any following branches, and when this group ends, a resetting ! item can be compiled. */ if (*ptr == ')') { ! if ((options & PCRE_INGROUP) != 0 && ! (options & PCRE_IMS) != (newoptions & PCRE_IMS)) { *code++ = OP_OPT; ! *code++ = *optchanged = newoptions & PCRE_IMS; } ! options = newoptions; /* Change options at this level */ previous = NULL; /* This item can't be repeated */ continue; /* It is complete */ } --- 3404,3434 ---- newoptions = (options | set) & (~unset); /* If the options ended with ')' this is not the start of a nested ! group with option changes, so the options change at this level. Compile ! code to change the ims options if this setting actually changes any of ! them. We also pass the new setting back so that it can be put at the ! start of any following branches, and when this group ends (if we are in ! a group), a resetting item can be compiled. ! ! Note that if this item is right at the start of the pattern, the ! options will have been abstracted and made global, so there will be no ! change to compile. */ if (*ptr == ')') { ! if ((options & PCRE_IMS) != (newoptions & PCRE_IMS)) { *code++ = OP_OPT; ! *code++ = newoptions & PCRE_IMS; } ! ! /* Change options at this level, and pass them back for use ! in subsequent branches. Reset the greedy defaults and the case ! value for firstbyte and reqbyte. */ ! ! *optionsptr = options = newoptions; ! greedy_default = ((newoptions & PCRE_UNGREEDY) != 0); ! greedy_non_default = greedy_default ^ 1; ! req_caseopt = ((options & PCRE_CASELESS) != 0) ? REQ_CASELESS : 0; ! previous = NULL; /* This item can't be repeated */ continue; /* It is complete */ } *************** *** 2129,2144 **** } } /* Else we have a referencing group; adjust the opcode. If the bracket number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and arrange for the true number to follow later, in an OP_BRANUMBER item. */ else { if (++(*brackets) > EXTRACT_BASIC_MAX) { bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; ! code[3] = OP_BRANUMBER; ! code[4] = *brackets >> 8; ! code[5] = *brackets & 255; skipbytes = 3; } else bravalue = OP_BRA + *brackets; --- 3443,3465 ---- } } + /* If PCRE_NO_AUTO_CAPTURE is set, all unadorned brackets become + non-capturing and behave like (?:...) brackets */ + + else if ((options & PCRE_NO_AUTO_CAPTURE) != 0) { + bravalue = OP_BRA; + } + /* Else we have a referencing group; adjust the opcode. If the bracket number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and arrange for the true number to follow later, in an OP_BRANUMBER item. */ else { + NUMBERED_GROUP: if (++(*brackets) > EXTRACT_BASIC_MAX) { bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; ! code[1 + LINK_SIZE] = OP_BRANUMBER; ! PUT2(code, 2 + LINK_SIZE, *brackets); skipbytes = 3; } else bravalue = OP_BRA + *brackets; *************** *** 2152,2168 **** previous = (bravalue >= OP_ONCE) ? code : NULL; *code = bravalue; tempcode = code; ! if (!compile_regex(options | PCRE_INGROUP, /* Set for all nested groups */ ! ((options & PCRE_IMS) != (newoptions & PCRE_IMS)) ? newoptions & PCRE_IMS : -1, /* Pass ims options if changed */ brackets, /* Extracting bracket count */ &tempcode, /* Where to put code (updated) */ &ptr, /* Input pointer (updated) */ errorptr, /* Where to put an error message */ (bravalue == OP_ASSERTBACK || bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ skipbytes, /* Skip over OP_COND/OP_BRANUMBER */ ! &subreqchar, /* For possible last char */ ! &subcountlits, /* For literal count */ cd)) /* Tables block */ goto FAILED; --- 3473,3491 ---- previous = (bravalue >= OP_ONCE) ? code : NULL; *code = bravalue; tempcode = code; + tempreqvary = cd->req_varyopt; /* Save value before bracket */ ! if (!compile_regex(newoptions, /* The complete new option state */ ! options & PCRE_IMS, /* The previous ims option state */ brackets, /* Extracting bracket count */ &tempcode, /* Where to put code (updated) */ &ptr, /* Input pointer (updated) */ errorptr, /* Where to put an error message */ (bravalue == OP_ASSERTBACK || bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ skipbytes, /* Skip over OP_COND/OP_BRANUMBER */ ! &subfirstbyte, /* For possible first char */ ! &subreqbyte, /* For possible last char */ ! bcptr, /* Current branch chain */ cd)) /* Tables block */ goto FAILED; *************** *** 2180,2186 **** do { condcount++; ! tc += (tc[1] << 8) | tc[2]; } while (*tc != OP_KET); --- 3503,3509 ---- do { condcount++; ! tc += GET(tc, 1); } while (*tc != OP_KET); *************** *** 2188,2212 **** *errorptr = ERR27; goto FAILED; } } ! /* Handle updating of the required character. If the subpattern didn't ! set one, leave it as it was. Otherwise, update it for normal brackets of ! all kinds, forward assertions, and conditions with two branches. Don't ! update the literal count for forward assertions, however. If the bracket ! is followed by a quantifier with zero repeat, we have to back off. Hence ! the definition of prevreqchar and subcountlits outside the main loop so ! that they can be accessed for the back off. */ ! ! if (subreqchar > 0 && ! (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_ASSERT || ! (bravalue == OP_COND && condcount == 2))) { ! prevreqchar = *reqchar; ! *reqchar = subreqchar; ! if (bravalue != OP_ASSERT) ! *countlits += subcountlits; } /* Now update the main code pointer to the end of the group. */ code = tempcode; --- 3511,3575 ---- *errorptr = ERR27; goto FAILED; } + + /* If there is just one branch, we must not make use of its firstbyte or + reqbyte, because this is equivalent to an empty second branch. */ + + if (condcount == 1) + subfirstbyte = subreqbyte = REQ_NONE; } ! /* Handle updating of the required and first characters. Update for normal ! brackets of all kinds, and conditions with two branches (see code above). ! If the bracket is followed by a quantifier with zero repeat, we have to ! back off. Hence the definition of zeroreqbyte and zerofirstbyte outside the ! main loop so that they can be accessed for the back off. */ ! ! zeroreqbyte = reqbyte; ! zerofirstbyte = firstbyte; ! groupsetfirstbyte = FALSE; ! ! if (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_COND) { ! /* If we have not yet set a firstbyte in this branch, take it from the ! subpattern, remembering that it was set here so that a repeat of more ! than one can replicate it as reqbyte if necessary. If the subpattern has ! no firstbyte, set "none" for the whole branch. In both cases, a zero ! repeat forces firstbyte to "none". */ ! ! if (firstbyte == REQ_UNSET) { ! if (subfirstbyte >= 0) { ! firstbyte = subfirstbyte; ! groupsetfirstbyte = TRUE; ! } else ! firstbyte = REQ_NONE; ! zerofirstbyte = REQ_NONE; ! } ! ! /* If firstbyte was previously set, convert the subpattern's firstbyte ! into reqbyte if there wasn't one, using the vary flag that was in ! existence beforehand. */ ! ! else if (subfirstbyte >= 0 && subreqbyte < 0) ! subreqbyte = subfirstbyte | tempreqvary; ! ! /* If the subpattern set a required byte (or set a first byte that isn't ! really the first byte - see above), set it. */ ! ! if (subreqbyte >= 0) ! reqbyte = subreqbyte; } + /* For a forward assertion, we take the reqbyte, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead + of a firstbyte. This is overcome by a scan at the end if there's no + firstbyte, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqbyte >= 0) + reqbyte = subreqbyte; + /* Now update the main code pointer to the end of the group. */ code = tempcode; *************** *** 2235,5910 **** have to change if any new ones are ever created. */ if (c < 0) { ! if (-c >= ESC_REF) { ! int number = -c - ESC_REF; ! previous = code; ! *code++ = OP_REF; ! *code++ = number >> 8; ! *code++ = number & 255; ! } else { ! previous = (-c > ESC_b && -c < ESC_Z) ? code : NULL; ! *code++ = -c; ! } ! continue; ! } ! ! /* Data character: reset and fall through */ ! ! ptr = tempptr; ! c = '\\'; ! ! /* Handle a run of data characters until a metacharacter is encountered. ! The first character is guaranteed not to be whitespace or # when the ! extended flag is set. */ ! ! NORMAL_CHAR: ! default: ! previous = code; ! *code = OP_CHARS; ! code += 2; ! length = 0; ! ! do { ! if ((options & PCRE_EXTENDED) != 0) { ! if ((cd->ctypes[c] & ctype_space) != 0) ! continue; ! if (c == '#') { ! /* The space before the ; is to avoid a warning on a silly compiler ! on the Macintosh. */ ! while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! if (c == 0) ! break; ! continue; ! } ! } ! ! /* Backslash may introduce a data char or a metacharacter. Escaped items ! are checked for validity in the pre-compiling pass. Stop the string ! before a metaitem. */ ! ! if (c == '\\') { ! tempptr = ptr; ! c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); ! if (c < 0) { ! ptr = tempptr; ! break; ! } ! ! /* If a character is > 127 in UTF-8 mode, we have to turn it into ! two or more characters in the UTF-8 encoding. */ ! } ! /* Ordinary character or single-char escape */ ! ! *code++ = c; ! length++; ! } ! ! /* This "while" is the end of the "do" above. */ ! ! while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); ! ! /* Update the last character and the count of literals */ ! ! prevreqchar = (length > 1) ? code[-2] : *reqchar; ! *reqchar = code[-1]; ! *countlits += length; ! ! /* Compute the length and set it in the data vector, and advance to ! the next state. */ ! ! previous[1] = length; ! if (length < MAXLIT) ! ptr--; ! break; ! } ! } /* end of big loop */ ! ! /* Control never reaches here by falling through, only by a goto for all the ! error states. Pass back the position in the pattern so that it can be displayed ! to the user for diagnosing the error. */ ! ! FAILED: ! *ptrptr = ptr; ! return FALSE; ! } ! ! ! ! ! /************************************************* ! * Compile sequence of alternatives * ! *************************************************/ ! ! /* On entry, ptr is pointing past the bracket character, but on return ! it points to the closing bracket, or vertical bar, or end of string. ! The code variable is pointing at the byte into which the BRA operator has been ! stored. If the ims options are changed at the start (for a (?ims: group) or ! during any branch, we need to insert an OP_OPT item at the start of every ! following branch to ensure they get set correctly at run time, and also pass ! the new options into every subsequent branch compile. ! ! Argument: ! options the option bits ! optchanged new ims options to set as if (?ims) were at the start, or -1 ! for no change ! brackets -> int containing the number of extracting brackets used ! codeptr -> the address of the current code pointer ! ptrptr -> the address of the current pattern pointer ! errorptr -> pointer to error message ! lookbehind TRUE if this is a lookbehind assertion ! skipbytes skip this many bytes at start (for OP_COND, OP_BRANUMBER) ! reqchar -> place to put the last required character, or a negative number ! countlits -> place to put the shortest literal count of any branch ! cd points to the data block with tables pointers ! ! Returns: TRUE on success ! */ ! ! static BOOL ! compile_regex(int options, int optchanged, int *brackets, uschar ** codeptr, ! const uschar ** ptrptr, const char **errorptr, BOOL lookbehind, ! int skipbytes, int *reqchar, int *countlits, compile_data * cd) ! { ! const uschar *ptr = *ptrptr; ! uschar *code = *codeptr; ! uschar *last_branch = code; ! uschar *start_bracket = code; ! uschar *reverse_count = NULL; ! int oldoptions = options & PCRE_IMS; ! int branchreqchar, branchcountlits; ! ! *reqchar = -1; ! *countlits = INT_MAX; ! code += 3 + skipbytes; ! ! /* Loop for each alternative branch */ ! ! for (;;) { ! int length; ! ! /* Handle change of options */ ! ! if (optchanged >= 0) { ! *code++ = OP_OPT; ! *code++ = optchanged; ! options = (options & ~PCRE_IMS) | optchanged; ! } ! ! /* Set up dummy OP_REVERSE if lookbehind assertion */ ! ! if (lookbehind) { ! *code++ = OP_REVERSE; ! reverse_count = code; ! *code++ = 0; ! *code++ = 0; ! } ! ! /* Now compile the branch */ ! ! if (!compile_branch(options, brackets, &code, &ptr, errorptr, &optchanged, ! &branchreqchar, &branchcountlits, cd)) { ! *ptrptr = ptr; ! return FALSE; ! } ! ! /* Fill in the length of the last branch */ ! ! length = code - last_branch; ! last_branch[1] = length >> 8; ! last_branch[2] = length & 255; ! ! /* Save the last required character if all branches have the same; a current ! value of -1 means unset, while -2 means "previous branch had no last required ! char". */ ! ! if (*reqchar != -2) { ! if (branchreqchar >= 0) { ! if (*reqchar == -1) ! *reqchar = branchreqchar; ! else if (*reqchar != branchreqchar) ! *reqchar = -2; ! } else ! *reqchar = -2; ! } ! ! /* Keep the shortest literal count */ ! ! if (branchcountlits < *countlits) ! *countlits = branchcountlits; ! DPRINTF(("literal count = %d min=%d\n", branchcountlits, *countlits)); ! ! /* If lookbehind, check that this branch matches a fixed-length string, ! and put the length into the OP_REVERSE item. Temporarily mark the end of ! the branch with OP_END. */ ! ! if (lookbehind) { ! *code = OP_END; ! length = find_fixedlength(last_branch, options); ! DPRINTF(("fixed length = %d\n", length)); ! if (length < 0) { ! *errorptr = ERR25; ! *ptrptr = ptr; ! return FALSE; ! } ! reverse_count[0] = (length >> 8); ! reverse_count[1] = length & 255; ! } ! ! /* Reached end of expression, either ')' or end of pattern. Insert a ! terminating ket and the length of the whole bracketed item, and return, ! leaving the pointer at the terminating char. If any of the ims options ! were changed inside the group, compile a resetting op-code following. */ ! ! if (*ptr != '|') { ! length = code - start_bracket; ! *code++ = OP_KET; ! *code++ = length >> 8; ! *code++ = length & 255; ! if (optchanged >= 0) { ! *code++ = OP_OPT; ! *code++ = oldoptions; ! } ! *codeptr = code; ! *ptrptr = ptr; ! return TRUE; ! } ! ! /* Another branch follows; insert an "or" node and advance the pointer. */ ! ! *code = OP_ALT; ! last_branch = code; ! code += 3; ! ptr++; ! } ! /* Control never reaches here */ ! } ! ! ! ! ! /************************************************* ! * Find first significant op code * ! *************************************************/ ! ! /* This is called by several functions that scan a compiled expression looking ! for a fixed first character, or an anchoring op code etc. It skips over things ! that do not influence this. For one application, a change of caseless option is ! important. ! ! Arguments: ! code pointer to the start of the group ! options pointer to external options ! optbit the option bit whose changing is significant, or ! zero if none are ! optstop TRUE to return on option change, otherwise change the options ! value and continue ! ! Returns: pointer to the first significant opcode ! */ ! ! static const uschar * ! first_significant_code(const uschar * code, int *options, int optbit, ! BOOL optstop) ! { ! for (;;) { ! switch ((int) *code) { ! case OP_OPT: ! if (optbit > 0 && ((int) code[1] & optbit) != (*options & optbit)) { ! if (optstop) ! return code; ! *options = (int) code[1]; ! } ! code += 2; ! break; ! ! case OP_CREF: ! case OP_BRANUMBER: ! code += 3; ! break; ! ! case OP_WORD_BOUNDARY: ! case OP_NOT_WORD_BOUNDARY: ! code++; ! break; ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK: ! case OP_ASSERTBACK_NOT: ! do ! code += (code[1] << 8) + code[2]; ! while (*code == OP_ALT); ! code += 3; ! break; ! default: ! return code; ! } ! } ! /* Control never reaches here */ ! } ! /************************************************* ! * Check for anchored expression * ! *************************************************/ ! /* Try to find out if this is an anchored regular expression. Consider each ! alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket ! all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then ! it's anchored. However, if this is a multiline pattern, then only OP_SOD ! counts, since OP_CIRC can match in the middle. ! A branch is also implicitly anchored if it starts with .* and DOTALL is set, ! because that will try the rest of the pattern at all possible matching points, ! so there is no point trying them again. ! Arguments: ! code points to start of expression (the bracket) ! options points to the options setting ! Returns: TRUE or FALSE ! */ ! static BOOL ! is_anchored(register const uschar * code, int *options) ! { ! do { ! const uschar *scode = first_significant_code(code + 3, options, ! PCRE_MULTILINE, FALSE); ! register int op = *scode; ! if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) { ! if (!is_anchored(scode, options)) ! return FALSE; ! } else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) && ! (*options & PCRE_DOTALL) != 0) { ! if (scode[1] != OP_ANY) ! return FALSE; ! } else if (op != OP_SOD && ! ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) ! return FALSE; ! code += (code[1] << 8) + code[2]; ! } ! while (*code == OP_ALT); ! return TRUE; ! } ! /************************************************* ! * Check for starting with ^ or .* * ! *************************************************/ ! /* This is called to find out if every branch starts with ^ or .* so that ! "first char" processing can be done to speed things up in multiline ! matching and for non-DOTALL patterns that start with .* (which must start at ! the beginning or after \n). ! Argument: points to start of expression (the bracket) ! Returns: TRUE or FALSE ! */ ! static BOOL ! is_startline(const uschar * code) ! { ! do { ! const uschar *scode = first_significant_code(code + 3, NULL, 0, FALSE); ! register int op = *scode; ! if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) { ! if (!is_startline(scode)) ! return FALSE; ! } else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) { ! if (scode[1] != OP_ANY) ! return FALSE; ! } else if (op != OP_CIRC) ! return FALSE; ! code += (code[1] << 8) + code[2]; ! } ! while (*code == OP_ALT); ! return TRUE; ! } ! /************************************************* ! * Check for fixed first char * ! *************************************************/ ! /* Try to find out if there is a fixed first character. This is called for ! unanchored expressions, as it speeds up their processing quite considerably. ! Consider each alternative branch. If they all start with the same char, or with ! a bracket all of whose alternatives start with the same char (recurse ad lib), ! then we return that char, otherwise -1. ! Arguments: ! code points to start of expression (the bracket) ! options pointer to the options (used to check casing changes) - Returns: -1 or the fixed first char - */ ! static int ! find_firstchar(const uschar * code, int *options) ! { ! register int c = -1; ! do { ! int d; ! const uschar *scode = first_significant_code(code + 3, options, ! PCRE_CASELESS, TRUE); ! register int op = *scode; ! if (op >= OP_BRA) ! op = OP_BRA; ! switch (op) { ! default: ! return -1; ! case OP_BRA: ! case OP_ASSERT: ! case OP_ONCE: ! case OP_COND: ! if ((d = find_firstchar(scode, options)) < 0) ! return -1; ! if (c < 0) ! c = d; ! else if (c != d) ! return -1; ! break; ! case OP_EXACT: /* Fall through */ ! scode++; ! case OP_CHARS: /* Fall through */ ! scode++; ! case OP_PLUS: ! case OP_MINPLUS: ! if (c < 0) ! c = scode[1]; ! else if (c != scode[1]) ! return -1; break; } ! code += (code[1] << 8) + code[2]; ! } ! while (*code == OP_ALT); ! return c; ! } /************************************************* ! * Compile a Regular Expression * *************************************************/ ! /* This function takes a string and returns a pointer to a block of store ! holding a compiled version of the expression. ! Arguments: ! pattern the regular expression ! options various option bits ! errorptr pointer to pointer to error text ! erroroffset ptr offset in pattern where error was detected ! tables pointer to character tables or NULL ! Returns: pointer to compiled data block, or NULL on error, ! with errorptr and erroroffset set */ ! pcre * ! pcre_compile(const char *pattern, int options, const char **errorptr, ! int *erroroffset, const unsigned char *tables) { ! real_pcre *re; ! int length = 3; /* For initial BRA plus length */ ! int runlength; ! int c, reqchar, countlits; ! int bracount = 0; ! int top_backref = 0; ! int branch_extra = 0; ! int branch_newextra; ! unsigned int brastackptr = 0; ! size_t size; ! uschar *code; ! const uschar *ptr; ! compile_data compile_block; ! int brastack[BRASTACK_SIZE]; ! uschar bralenstack[BRASTACK_SIZE]; ! #ifdef DEBUG ! uschar *code_base, *code_end; ! #endif ! /* Can't support UTF8 unless PCRE has been compiled to include the code. */ ! if ((options & PCRE_UTF8) != 0) { ! *errorptr = ERR32; ! return NULL; ! } ! /* We can't pass back an error message if errorptr is NULL; I guess the best we ! can do is just return NULL. */ ! if (errorptr == NULL) ! return NULL; ! *errorptr = NULL; ! /* However, we can give a message for this error */ ! if (erroroffset == NULL) { ! *errorptr = ERR16; ! return NULL; ! } ! *erroroffset = 0; ! if ((options & ~PUBLIC_OPTIONS) != 0) { ! *errorptr = ERR17; ! return NULL; ! } ! /* Set up pointers to the individual character tables */ ! if (tables == NULL) ! tables = pcre_default_tables; ! compile_block.lcc = tables + lcc_offset; ! compile_block.fcc = tables + fcc_offset; ! compile_block.cbits = tables + cbits_offset; ! compile_block.ctypes = tables + ctypes_offset; ! /* Reflect pattern for debugging output */ ! DPRINTF(("------------------------------------------------------------------\n")); ! DPRINTF(("%s\n", pattern)); ! /* The first thing to do is to make a pass over the pattern to compute the ! amount of store required to hold the compiled code. This does not have to be ! perfect as long as errors are overestimates. At the same time we can detect any ! internal flag settings. Make an attempt to correct for any counted white space ! if an "extended" flag setting appears late in the pattern. We can't be so ! clever for #-comments. */ ! ptr = (const uschar *) (pattern - 1); ! while ((c = *(++ptr)) != 0) { ! int min, max; ! int class_charcount; ! int bracket_length; ! if ((options & PCRE_EXTENDED) != 0) { ! if ((compile_block.ctypes[c] & ctype_space) != 0) ! continue; ! if (c == '#') { ! /* The space before the ; is to avoid a warning on a silly compiler ! on the Macintosh. */ ! while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! continue; } } ! switch (c) { ! /* A backslashed item may be an escaped "normal" character or a ! character type. For a "normal" character, put the pointers and ! character back so that tests for whitespace etc. in the input ! are done correctly. */ ! case '\\': ! { ! const uschar *save_ptr = ptr; ! c = ! check_escape(&ptr, errorptr, bracount, options, FALSE, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if (c >= 0) { ! ptr = save_ptr; ! c = '\\'; ! goto NORMAL_CHAR; ! } } ! length++; ! /* A back reference needs an additional 2 bytes, plus either one or 5 ! bytes for a repeat. We also need to keep the value of the highest ! back reference. */ ! if (c <= -ESC_REF) { ! int refnum = -c - ESC_REF; ! if (refnum > top_backref) ! top_backref = refnum; ! length += 2; /* For single back reference */ ! if (ptr[1] == '{' && is_counted_repeat(ptr + 2, &compile_block)) { ! ptr = ! read_repeat_counts(ptr + 2, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) ! length++; ! else ! length += 5; ! if (ptr[1] == '?') ! ptr++; ! } } - continue; ! case '^': ! case '.': ! case '$': ! case '*': /* These repeats won't be after brackets; */ ! case '+': /* those are handled separately */ ! case '?': ! length++; ! continue; ! /* This covers the cases of repeats after a single char, metachar, class, ! or back reference. */ ! case '{': ! if (!is_counted_repeat(ptr + 1, &compile_block)) ! goto NORMAL_CHAR; ! ptr = read_repeat_counts(ptr + 1, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) ! length++; ! else { ! length--; /* Uncount the original char or metachar */ ! if (min == 1) ! length++; ! else if (min > 0) ! length += 4; ! if (max > 0) ! length += 4; ! else ! length += 2; ! } ! if (ptr[1] == '?') ! ptr++; ! continue; ! /* An alternation contains an offset to the next branch or ket. If any ims ! options changed in the previous branch(es), and/or if we are in a ! lookbehind assertion, extra space will be needed at the start of the ! branch. This is handled by branch_extra. */ - case '|': - length += 3 + branch_extra; - continue; - /* A character class uses 33 characters. Don't worry about character types - that aren't allowed in classes - they'll get picked up during the compile. - A character class that contains only one character uses 2 or 3 bytes, - depending on whether it is negated or not. Notice this where we can. */ - case '[': - class_charcount = 0; - if (*(++ptr) == '^') - ptr++; - do { - if (*ptr == '\\') { - int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, - &compile_block); - if (*errorptr != NULL) - goto PCRE_ERROR_RETURN; - if (-ch == ESC_b) - class_charcount++; - else - class_charcount = 10; - } else - class_charcount++; - ptr++; - } - while (*ptr != 0 && *ptr != ']'); ! /* Repeats for negated single chars are handled by the general code */ ! if (class_charcount == 1) ! length += 3; ! else { ! length += 33; ! /* A repeat needs either 1 or 5 bytes. */ ! if (*ptr != 0 && ptr[1] == '{' ! && is_counted_repeat(ptr + 2, &compile_block)) { ! ptr = ! read_repeat_counts(ptr + 2, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) ! length++; ! else ! length += 5; ! if (ptr[1] == '?') ! ptr++; ! } ! } ! continue; ! /* Brackets may be genuine groups or special things */ ! case '(': ! branch_newextra = 0; ! bracket_length = 3; ! /* Handle special forms of bracket, which all start (? */ ! if (ptr[1] == '?') { ! int set, unset; ! int *optset; ! switch (c = ptr[2]) { ! /* Skip over comments entirely */ ! case '#': ! ptr += 3; ! while (*ptr != 0 && *ptr != ')') ! ptr++; ! if (*ptr == 0) { ! *errorptr = ERR18; ! goto PCRE_ERROR_RETURN; ! } ! continue; ! /* Non-referencing groups and lookaheads just move the pointer on, and ! then behave like a non-special bracket, except that they don't increment ! the count of extracting brackets. Ditto for the "once only" bracket, ! which is in Perl from version 5.005. */ ! case ':': ! case '=': ! case '!': ! case '>': ! ptr += 2; ! break; ! /* A recursive call to the regex is an extension, to provide the ! facility which can be obtained by $(?p{perl-code}) in Perl 5.6. */ ! case 'R': ! if (ptr[3] != ')') { ! *errorptr = ERR29; ! goto PCRE_ERROR_RETURN; ! } ! ptr += 3; ! length += 1; ! break; ! /* Lookbehinds are in Perl from version 5.005 */ ! case '<': ! if (ptr[3] == '=' || ptr[3] == '!') { ! ptr += 3; ! branch_newextra = 3; ! length += 3; /* For the first branch */ ! break; ! } ! *errorptr = ERR24; ! goto PCRE_ERROR_RETURN; - /* Conditionals are in Perl from version 5.005. The bracket must either - be followed by a number (for bracket reference) or by an assertion - group. */ - case '(': - if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0) { - ptr += 4; - length += 3; - while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) - ptr++; - if (*ptr != ')') { - *errorptr = ERR26; - goto PCRE_ERROR_RETURN; - } - } else { /* An assertion must follow */ ! ptr++; /* Can treat like ':' as far as spacing is concerned */ ! if (ptr[2] != '?' || ! (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<')) { ! ptr += 2; /* To get right offset in message */ ! *errorptr = ERR28; ! goto PCRE_ERROR_RETURN; ! } ! } ! break; ! /* Else loop checking valid options until ) is met. Anything else is an ! error. If we are without any brackets, i.e. at top level, the settings ! act as if specified in the options, so massage the options immediately. ! This is for backward compatibility with Perl 5.004. */ ! default: ! set = unset = 0; ! optset = &set; ! ptr += 2; ! for (;; ptr++) { ! c = *ptr; ! switch (c) { ! case 'i': ! *optset |= PCRE_CASELESS; ! continue; ! case 'm': ! *optset |= PCRE_MULTILINE; ! continue; ! case 's': ! *optset |= PCRE_DOTALL; ! continue; ! case 'x': ! *optset |= PCRE_EXTENDED; ! continue; ! case 'X': ! *optset |= PCRE_EXTRA; ! continue; ! case 'U': ! *optset |= PCRE_UNGREEDY; ! continue; ! case '-': ! optset = &unset; ! continue; ! /* A termination by ')' indicates an options-setting-only item; ! this is global at top level; otherwise nothing is done here and ! it is handled during the compiling process on a per-bracket-group ! basis. */ - case ')': - if (brastackptr == 0) { - options = (options | set) & (~unset); - set = unset = 0; /* To save length */ - } - /* Fall through */ - /* A termination by ':' indicates the start of a nested group with - the given options set. This is again handled at compile time, but - we must allow for compiled space if any of the ims options are - set. We also have to allow for resetting space at the end of - the group, which is why 4 is added to the length and not just 2. - If there are several changes of options within the same group, this - will lead to an over-estimate on the length, but this shouldn't - matter very much. We also have to allow for resetting options at - the start of any alternations, which we do by setting - branch_newextra to 2. Finally, we record whether the case-dependent - flag ever changes within the regex. This is used by the "required - character" code. */ ! case ':': ! if (((set | unset) & PCRE_IMS) != 0) { ! length += 4; ! branch_newextra = 2; ! if (((set | unset) & PCRE_CASELESS) != 0) ! options |= PCRE_ICHANGED; ! } ! goto END_OPTIONS; ! /* Unrecognized option character */ ! default: ! *errorptr = ERR12; ! goto PCRE_ERROR_RETURN; ! } ! } ! /* If we hit a closing bracket, that's it - this is a freestanding ! option-setting. We need to ensure that branch_extra is updated if ! necessary. The only values branch_newextra can have here are 0 or 2. ! If the value is 2, then branch_extra must either be 2 or 5, depending ! on whether this is a lookbehind group or not. */ ! END_OPTIONS: ! if (c == ')') { ! if (branch_newextra == 2 ! && (branch_extra == 0 || branch_extra == 3)) ! branch_extra += branch_newextra; ! continue; ! } ! /* If options were terminated by ':' control comes here. Fall through ! to handle the group below. */ ! } ! } ! /* Extracting brackets must be counted so we can process escapes in a ! Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to ! need an additional 3 bytes of store per extracting bracket. */ ! else { ! bracount++; ! if (bracount > EXTRACT_BASIC_MAX) ! bracket_length += 3; ! } ! /* Save length for computing whole length at end if there's a repeat that ! requires duplication of the group. Also save the current value of ! branch_extra, and start the new group with the new value. If non-zero, this ! will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ ! if (brastackptr >= sizeof(brastack) / sizeof(int)) { ! *errorptr = ERR19; ! goto PCRE_ERROR_RETURN; ! } ! bralenstack[brastackptr] = branch_extra; ! branch_extra = branch_newextra; ! brastack[brastackptr++] = length; ! length += bracket_length; ! continue; - /* Handle ket. Look for subsequent max/min; for certain sets of values we - have to replicate this bracket up to that many times. If brastackptr is - 0 this is an unmatched bracket which will generate an error, but take care - not to try to access brastack[-1] when computing the length and restoring - the branch_extra value. */ - case ')': - length += 3; - { - int minval = 1; - int maxval = 1; - int duplength; - - if (brastackptr > 0) { - duplength = length - brastack[--brastackptr]; - branch_extra = bralenstack[brastackptr]; - } else - duplength = 0; - /* Leave ptr at the final char; for read_repeat_counts this happens - automatically; for the others we need an increment. */ ! if ((c = ptr[1]) == '{' && is_counted_repeat(ptr + 2, &compile_block)) { ! ptr = read_repeat_counts(ptr + 2, &minval, &maxval, errorptr, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! } else if (c == '*') { ! minval = 0; ! maxval = -1; ! ptr++; ! } else if (c == '+') { ! maxval = -1; ! ptr++; ! } else if (c == '?') { ! minval = 0; ! ptr++; ! } ! /* If the minimum is zero, we have to allow for an OP_BRAZERO before the ! group, and if the maximum is greater than zero, we have to replicate ! maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting ! bracket set - hence the 7. */ ! ! if (minval == 0) { ! length++; ! if (maxval > 0) ! length += (maxval - 1) * (duplength + 7); ! } ! ! /* When the minimum is greater than zero, 1 we have to replicate up to ! minval-1 times, with no additions required in the copies. Then, if ! there is a limited maximum we have to replicate up to maxval-1 times ! allowing for a BRAZERO item before each optional copy and nesting ! brackets for all but one of the optional copies. */ ! else { ! length += (minval - 1) * duplength; ! if (maxval > minval) /* Need this test as maxval=-1 means no limit */ ! length += (maxval - minval) * (duplength + 7) - 6; ! } ! } ! continue; ! /* Non-special character. For a run of such characters the length required ! is the number of characters + 2, except that the maximum run length is 255. ! We won't get a skipped space or a non-data escape or the start of a # ! comment as the first character, so the length can't be zero. */ ! NORMAL_CHAR: ! default: ! length += 2; ! runlength = 0; ! do { ! if ((options & PCRE_EXTENDED) != 0) { ! if ((compile_block.ctypes[c] & ctype_space) != 0) ! continue; ! if (c == '#') { ! /* The space before the ; is to avoid a warning on a silly compiler ! on the Macintosh. */ ! while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! continue; ! } ! } ! /* Backslash may introduce a data char or a metacharacter; stop the ! string before the latter. */ ! if (c == '\\') { ! const uschar *saveptr = ptr; ! c = check_escape(&ptr, errorptr, bracount, options, FALSE, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if (c < 0) { ! ptr = saveptr; ! break; ! } ! } ! /* Ordinary character or single-char escape */ ! runlength++; ! } ! /* This "while" is the end of the "do" above. */ ! while (runlength < MAXLIT && ! (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); ! ptr--; ! length += runlength; ! continue; ! } ! } ! length += 4; /* For final KET and END */ ! if (length > 65539) { ! *errorptr = ERR20; ! return NULL; ! } ! /* Compute the size of data block needed and get it, either from malloc or ! externally provided function. We specify "code[0]" in the offsetof() expression ! rather than just "code", because it has been reported that one broken compiler ! fails on "code" because it is also an independent variable. It should make no ! difference to the value of the offsetof(). */ ! size = length + offsetof(real_pcre, code[0]); ! re = (real_pcre *) (pcre_malloc) (size); ! if (re == NULL) { ! *errorptr = ERR21; ! return NULL; ! } ! /* Put in the magic number, and save the size, options, and table pointer */ ! re->magic_number = MAGIC_NUMBER; ! re->size = size; ! re->options = options; ! re->tables = tables; ! /* Set up a starting, non-extracting bracket, then compile the expression. On ! error, *errorptr will be set non-NULL, so we don't need to look at the result ! of the function here. */ ! ptr = (const uschar *) pattern; ! code = re->code; ! *code = OP_BRA; ! bracount = 0; ! (void) compile_regex(options, -1, &bracount, &code, &ptr, errorptr, FALSE, 0, ! &reqchar, &countlits, &compile_block); ! re->top_bracket = bracount; ! re->top_backref = top_backref; ! /* If not reached end of pattern on success, there's an excess bracket. */ ! if (*errorptr == NULL && *ptr != 0) ! *errorptr = ERR22; ! /* Fill in the terminating state and check for disastrous overflow, but ! if debugging, leave the test till after things are printed out. */ ! *code++ = OP_END; ! #ifndef DEBUG ! if (code - re->code > length) ! *errorptr = ERR23; ! #endif ! /* Give an error if there's back reference to a non-existent capturing ! subpattern. */ ! if (top_backref > re->top_bracket) ! *errorptr = ERR15; ! /* Failed to compile */ ! if (*errorptr != NULL) { ! (pcre_free) (re); ! PCRE_ERROR_RETURN: ! *erroroffset = ptr - (const uschar *) pattern; ! return NULL; ! } ! /* If the anchored option was not passed, set flag if we can determine that the ! pattern is anchored by virtue of ^ characters or \A or anything else (such as ! starting with .* when DOTALL is set). ! Otherwise, see if we can determine what the first character has to be, because ! that speeds up unanchored matches no end. If not, see if we can set the ! PCRE_STARTLINE flag. This is helpful for multiline matches when all branches ! start with ^. and also when all branches start with .* for non-DOTALL matches. ! */ ! if ((options & PCRE_ANCHORED) == 0) { ! int temp_options = options; ! if (is_anchored(re->code, &temp_options)) ! re->options |= PCRE_ANCHORED; ! else { ! int ch = find_firstchar(re->code, &temp_options); ! if (ch >= 0) { ! re->first_char = ch; ! re->options |= PCRE_FIRSTSET; ! } else if (is_startline(re->code)) ! re->options |= PCRE_STARTLINE; ! } ! } ! /* Save the last required character if there are at least two literal ! characters on all paths, or if there is no first character setting. */ ! if (reqchar >= 0 && (countlits > 1 || (re->options & PCRE_FIRSTSET) == 0)) { ! re->req_char = reqchar; ! re->options |= PCRE_REQCHSET; ! } ! /* Print out the compiled data for debugging */ ! #ifdef DEBUG ! printf("Length = %d top_bracket = %d top_backref = %d\n", ! length, re->top_bracket, re->top_backref); ! if (re->options != 0) { ! printf("%s%s%s%s%s%s%s%s%s\n", ! ((re->options & PCRE_ANCHORED) != 0) ? "anchored " : "", ! ((re->options & PCRE_CASELESS) != 0) ? "caseless " : "", ! ((re->options & PCRE_ICHANGED) != 0) ? "case state changed " : "", ! ((re->options & PCRE_EXTENDED) != 0) ? "extended " : "", ! ((re->options & PCRE_MULTILINE) != 0) ? "multiline " : "", ! ((re->options & PCRE_DOTALL) != 0) ? "dotall " : "", ! ((re->options & PCRE_DOLLAR_ENDONLY) != 0) ? "endonly " : "", ! ((re->options & PCRE_EXTRA) != 0) ? "extra " : "", ! ((re->options & PCRE_UNGREEDY) != 0) ? "ungreedy " : ""); ! } ! if ((re->options & PCRE_FIRSTSET) != 0) { ! if (isprint(re->first_char)) ! printf("First char = %c\n", re->first_char); ! else ! printf("First char = \\x%02x\n", re->first_char); ! } ! if ((re->options & PCRE_REQCHSET) != 0) { ! if (isprint(re->req_char)) ! printf("Req char = %c\n", re->req_char); ! else ! printf("Req char = \\x%02x\n", re->req_char); ! } ! code_end = code; ! code_base = code = re->code; ! while (code < code_end) { ! int charlength; ! printf("%3d ", code - code_base); ! if (*code >= OP_BRA) { ! if (*code - OP_BRA > EXTRACT_BASIC_MAX) ! printf("%3d Bra extra", (code[1] << 8) + code[2]); ! else ! printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); ! code += 2; ! } ! else ! switch (*code) { ! case OP_OPT: ! printf(" %.2x %s", code[1], OP_names[*code]); ! code++; ! break; ! case OP_CHARS: ! charlength = *(++code); ! printf("%3d ", charlength); ! while (charlength-- > 0) ! if (isprint(c = *(++code))) ! printf("%c", c); ! else ! printf("\\x%02x", c); ! break; ! case OP_KETRMAX: ! case OP_KETRMIN: ! case OP_ALT: ! case OP_KET: ! case OP_ASSERT: ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK: ! case OP_ASSERTBACK_NOT: ! case OP_ONCE: ! case OP_REVERSE: ! case OP_BRANUMBER: ! case OP_COND: ! case OP_CREF: ! printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); ! code += 2; ! break; ! case OP_STAR: ! case OP_MINSTAR: ! case OP_PLUS: ! case OP_MINPLUS: ! case OP_QUERY: ! case OP_MINQUERY: ! case OP_TYPESTAR: ! case OP_TYPEMINSTAR: ! case OP_TYPEPLUS: ! case OP_TYPEMINPLUS: ! case OP_TYPEQUERY: ! case OP_TYPEMINQUERY: ! if (*code >= OP_TYPESTAR) ! printf(" %s", OP_names[code[1]]); ! else if (isprint(c = code[1])) ! printf(" %c", c); ! else ! printf(" \\x%02x", c); ! printf("%s", OP_names[*code++]); ! break; ! case OP_EXACT: ! case OP_UPTO: ! case OP_MINUPTO: ! if (isprint(c = code[3])) ! printf(" %c{", c); ! else ! printf(" \\x%02x{", c); ! if (*code != OP_EXACT) ! printf("0,"); ! printf("%d}", (code[1] << 8) + code[2]); ! if (*code == OP_MINUPTO) ! printf("?"); ! code += 3; ! break; ! case OP_TYPEEXACT: ! case OP_TYPEUPTO: ! case OP_TYPEMINUPTO: ! printf(" %s{", OP_names[code[3]]); ! if (*code != OP_TYPEEXACT) ! printf(","); ! printf("%d}", (code[1] << 8) + code[2]); ! if (*code == OP_TYPEMINUPTO) ! printf("?"); ! code += 3; ! break; ! case OP_NOT: ! if (isprint(c = *(++code))) ! printf(" [^%c]", c); ! else ! printf(" [^\\x%02x]", c); ! break; ! case OP_NOTSTAR: ! case OP_NOTMINSTAR: ! case OP_NOTPLUS: ! case OP_NOTMINPLUS: ! case OP_NOTQUERY: ! case OP_NOTMINQUERY: ! if (isprint(c = code[1])) ! printf(" [^%c]", c); ! else ! printf(" [^\\x%02x]", c); ! printf("%s", OP_names[*code++]); ! break; ! case OP_NOTEXACT: ! case OP_NOTUPTO: ! case OP_NOTMINUPTO: ! if (isprint(c = code[3])) ! printf(" [^%c]{", c); ! else ! printf(" [^\\x%02x]{", c); ! if (*code != OP_NOTEXACT) ! printf(","); ! printf("%d}", (code[1] << 8) + code[2]); ! if (*code == OP_NOTMINUPTO) ! printf("?"); ! code += 3; ! break; ! case OP_REF: ! printf(" \\%d", (code[1] << 8) | code[2]); ! code += 3; ! goto CLASS_REF_REPEAT; ! case OP_CLASS: ! { ! int i, min, max; ! code++; ! printf(" ["); ! ! for (i = 0; i < 256; i++) { ! if ((code[i / 8] & (1 << (i & 7))) != 0) { ! int j; ! for (j = i + 1; j < 256; j++) ! if ((code[j / 8] & (1 << (j & 7))) == 0) ! break; ! if (i == '-' || i == ']') ! printf("\\"); ! if (isprint(i)) ! printf("%c", i); ! else ! printf("\\x%02x", i); ! if (--j > i) { ! printf("-"); ! if (j == '-' || j == ']') ! printf("\\"); ! if (isprint(j)) ! printf("%c", j); ! else ! printf("\\x%02x", j); ! } ! i = j; } - } - printf("]"); - code += 32; ! CLASS_REF_REPEAT: ! switch (*code) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRPLUS: ! case OP_CRMINPLUS: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! printf("%s", OP_names[*code]); ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! min = (code[1] << 8) + code[2]; ! max = (code[3] << 8) + code[4]; ! if (max == 0) ! printf("{%d,}", min); ! else ! printf("{%d,%d}", min, max); ! if (*code == OP_CRMINRANGE) ! printf("?"); ! code += 4; ! break; ! default: ! code--; } - } - break; ! /* Anything else is just a one-node item */ ! default: ! printf(" %s", OP_names[*code]); ! break; ! } ! code++; ! printf("\n"); ! } ! printf ! ("------------------------------------------------------------------\n"); ! /* This check is done here in the debugging case so that the code that ! was compiled can be seen. */ ! if (code - re->code > length) { ! *errorptr = ERR23; ! (pcre_free) (re); ! *erroroffset = ptr - (uschar *) pattern; ! return NULL; ! } ! #endif ! return (pcre *) re; ! } ! /************************************************* ! * Match a back-reference * ! *************************************************/ ! /* If a back reference hasn't been set, the length that is passed is greater ! than the number of characters left in the string, so the match fails. ! Arguments: ! offset index into the offset vector ! eptr points into the subject ! length length to be matched ! md points to match data block ! ims the ims flags ! Returns: TRUE if matched ! */ ! static BOOL ! match_ref(int offset, register const uschar * eptr, int length, ! volatile match_data * md, unsigned long int ims) ! { ! const uschar *p = md->start_subject + md->offset_vector[offset]; ! #ifdef BACKTRACK_LIMIT ! if (backtrack_count++ > BACKTRACK_LIMIT || cpu_time_limit_hit) ! longjmp(backtrack_jmp, 1); ! #endif ! #ifdef DEBUG ! if (eptr >= md->end_subject) ! printf("matching subject "); ! else { ! printf("matching subject "); ! pchars(eptr, length, TRUE, md); ! } ! printf(" against backref "); ! pchars(p, length, FALSE, md); ! printf("\n"); ! #endif ! /* Always fail if not enough characters left */ ! if (length > md->end_subject - eptr) ! return FALSE; ! /* Separate the caselesss case for speed */ ! if ((ims & PCRE_CASELESS) != 0) { ! while (length-- > 0) ! if (md->lcc[*p++] != md->lcc[*eptr++]) ! return FALSE; ! } else { ! while (length-- > 0) ! if (*p++ != *eptr++) ! return FALSE; ! } ! return TRUE; ! } ! /************************************************* ! * Match from current position * ! *************************************************/ ! /* On entry ecode points to the first opcode, and eptr to the first character ! in the subject string, while eptrb holds the value of eptr at the start of the ! last bracketed group - used for breaking infinite loops matching zero-length ! strings. ! Arguments: ! eptr pointer in subject ! ecode position in code ! offset_top current top pointer ! md pointer to "static" info for the match ! ims current /i, /m, and /s options ! eptrb pointer to chain of blocks containing eptr at start of ! brackets - for testing for empty matches ! flags can contain ! match_condassert - this is an assertion condition ! match_isgroup - this is the start of a bracketed group ! Returns: TRUE if matched ! */ ! static BOOL ! match(register const uschar * eptr, register const uschar * ecode, ! int offset_top, volatile match_data * md, unsigned long int ims, ! eptrblock * eptrb, int flags) ! { ! unsigned long int original_ims = ims; /* Save for resetting on ')' */ ! eptrblock newptrb; ! /* At the start of a bracketed group, add the current subject pointer to the ! stack of such pointers, to be re-instated at the end of the group when we hit ! the closing ket. When match() is called in other circumstances, we don't add to ! the stack. */ ! #ifdef BACKTRACK_LIMIT ! if (backtrack_count++ > BACKTRACK_LIMIT) ! longjmp(backtrack_jmp, 1); ! #endif ! if ((flags & match_isgroup) != 0) { ! newptrb.prev = eptrb; ! newptrb.saved_eptr = eptr; ! eptrb = &newptrb; ! } ! /* Now start processing the operations. */ ! for (;;) { ! int op = (int) *ecode; ! int min, max, ctype; ! register int i; ! register int c; ! BOOL minimize = FALSE; ! /* Opening capturing bracket. If there is space in the offset vector, save ! the current subject position in the working slot at the top of the vector. We ! mustn't change the current values of the data slot, because they may be set ! from a previous iteration of this group, and be referred to by a reference ! inside the group. ! If the bracket fails to match, we need to restore this value and also the ! values of the final offsets, in case they were set by a previous iteration of ! the same bracket. ! If there isn't enough space in the offset vector, treat this as if it were a ! non-capturing bracket. Don't worry about setting the flag for the error case ! here; that is handled in the code for KET. */ ! if (op > OP_BRA) { ! int offset; ! int number = op - OP_BRA; ! /* For extended extraction brackets (large number), we have to fish out the ! number from a dummy opcode at the start. */ ! if (number > EXTRACT_BASIC_MAX) ! number = (ecode[4] << 8) | ecode[5]; ! offset = number << 1; ! #ifdef DEBUG ! printf("start bracket %d subject=", number); ! pchars(eptr, 16, TRUE, md); ! printf("\n"); ! #endif ! if (offset < md->offset_max) { ! int save_offset1 = md->offset_vector[offset]; ! int save_offset2 = md->offset_vector[offset + 1]; ! int save_offset3 = md->offset_vector[md->offset_end - number]; ! DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, ! save_offset3)); ! md->offset_vector[md->offset_end - number] = eptr - md->start_subject; ! do { ! if (match(eptr, ecode + 3, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! ecode += (ecode[1] << 8) + ecode[2]; ! } ! while (*ecode == OP_ALT); ! DPRINTF(("bracket %d failed\n", number)); ! md->offset_vector[offset] = save_offset1; ! md->offset_vector[offset + 1] = save_offset2; ! md->offset_vector[md->offset_end - number] = save_offset3; ! return FALSE; ! } ! /* Insufficient room for saving captured contents */ ! else ! op = OP_BRA; ! } ! /* Other types of node can be handled by a switch */ ! switch (op) { ! case OP_BRA: /* Non-capturing bracket: optimized */ ! DPRINTF(("start bracket 0\n")); ! do { ! if (match(eptr, ecode + 3, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! ecode += (ecode[1] << 8) + ecode[2]; } - while (*ecode == OP_ALT); - DPRINTF(("bracket 0 failed\n")); - return FALSE; ! /* Conditional group: compilation checked that there are no more than ! two branches. If the condition is false, skipping the first branch takes us ! past the end if there is only one branch, but that's OK because that is ! exactly what going to the ket would do. */ ! case OP_COND: ! if (ecode[3] == OP_CREF) { /* Condition is extraction test */ ! int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */ ! return match(eptr, ! ecode + ! ((offset < offset_top ! && md->offset_vector[offset] >= ! 0) ? 6 : 3 + (ecode[1] << 8) + ecode[2]), offset_top, md, ! ims, eptrb, match_isgroup); } ! /* The condition is an assertion. Call match() to evaluate it - setting ! the final argument TRUE causes it to stop at the end of an assertion. */ ! else { ! if (match(eptr, ecode + 3, offset_top, md, ims, NULL, ! match_condassert | match_isgroup)) { ! ecode += 3 + (ecode[4] << 8) + ecode[5]; ! while (*ecode == OP_ALT) ! ecode += (ecode[1] << 8) + ecode[2]; ! } else ! ecode += (ecode[1] << 8) + ecode[2]; ! return match(eptr, ecode + 3, offset_top, md, ims, eptrb, ! match_isgroup); } - /* Control never reaches here */ ! /* Skip over conditional reference or large extraction number data if ! encountered. */ ! case OP_CREF: ! case OP_BRANUMBER: ! ecode += 3; ! break; ! /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched ! an empty string - recursion will then try other alternatives, if any. */ ! case OP_END: ! if (md->notempty && eptr == md->start_match) ! return FALSE; ! md->end_match_ptr = eptr; /* Record where we ended */ ! md->end_offset_top = offset_top; /* and how many extracts were taken */ ! return TRUE; ! /* Change option settings */ ! case OP_OPT: ! ims = ecode[1]; ! ecode += 2; ! DPRINTF(("ims set to %02lx\n", ims)); ! break; ! /* Assertion brackets. Check the alternative branches in turn - the ! matching won't pass the KET for an assertion. If any one branch matches, ! the assertion is true. Lookbehind assertions have an OP_REVERSE item at the ! start of each branch to move the current point backwards, so the code at ! this level is identical to the lookahead case. */ ! case OP_ASSERT: ! case OP_ASSERTBACK: ! do { ! if (match(eptr, ecode + 3, offset_top, md, ims, NULL, match_isgroup)) ! break; ! ecode += (ecode[1] << 8) + ecode[2]; } - while (*ecode == OP_ALT); - if (*ecode == OP_KET) - return FALSE; - - /* If checking an assertion for a condition, return TRUE. */ - - if ((flags & match_condassert) != 0) - return TRUE; ! /* Continue from after the assertion, updating the offsets high water ! mark, since extracts may have been taken during the assertion. */ ! do ! ecode += (ecode[1] << 8) + ecode[2]; ! while (*ecode == OP_ALT); ! ecode += 3; ! offset_top = md->end_offset_top; ! continue; ! /* Negative assertion: all branches must fail to match */ ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK_NOT: ! do { ! if (match(eptr, ecode + 3, offset_top, md, ims, NULL, match_isgroup)) ! return FALSE; ! ecode += (ecode[1] << 8) + ecode[2]; } - while (*ecode == OP_ALT); ! if ((flags & match_condassert) != 0) ! return TRUE; ! ecode += 3; continue; ! /* Move the subject pointer back. This occurs only at the start of ! each branch of a lookbehind assertion. If we are too close to the start to ! move back, this match function fails. When working with UTF-8 we move ! back a number of characters, not bytes. */ ! ! case OP_REVERSE: ! eptr -= (ecode[1] << 8) + ecode[2]; ! ! if (eptr < md->start_subject) ! return FALSE; ! ecode += 3; ! break; ! /* Recursion matches the current regex, nested. If there are any capturing ! brackets started but not finished, we have to save their starting points ! and reinstate them after the recursion. However, we don't know how many ! such there are (offset_top records the completed total) so we just have ! to save all the potential data. There may be up to 99 such values, which ! is a bit large to put on the stack, but using malloc for small numbers ! seems expensive. As a compromise, the stack is used when there are fewer ! than 16 values to store; otherwise malloc is used. A problem is what to do ! if the malloc fails ... there is no way of returning to the top level with ! an error. Save the top 15 values on the stack, and accept that the rest ! may be wrong. */ ! case OP_RECURSE: ! { ! BOOL rc; ! int *save; ! int stacksave[15]; ! c = md->offset_max; ! if (c < 16) ! save = stacksave; ! else { ! save = (int *) (pcre_malloc) ((c + 1) * sizeof(int)); ! if (save == NULL) { ! save = stacksave; ! c = 15; } } ! for (i = 1; i <= c; i++) ! save[i] = md->offset_vector[md->offset_end - i]; ! rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb, ! match_isgroup); ! for (i = 1; i <= c; i++) ! md->offset_vector[md->offset_end - i] = save[i]; ! if (save != stacksave) ! (pcre_free) (save); ! if (!rc) ! return FALSE; ! /* In case the recursion has set more capturing values, save the final ! number, then move along the subject till after the recursive match, ! and advance one byte in the pattern code. */ ! offset_top = md->end_offset_top; ! eptr = md->end_match_ptr; ! ecode++; ! } ! break; ! /* "Once" brackets are like assertion brackets except that after a match, ! the point in the subject string is not moved back. Thus there can never be ! a move back into the brackets. Check the alternative branches in turn - the ! matching won't pass the KET for this kind of subpattern. If any one branch ! matches, we carry on as at the end of a normal bracket, leaving the subject ! pointer. */ ! case OP_ONCE: ! { ! const uschar *prev = ecode; ! const uschar *saved_eptr = eptr; ! do { ! if (match(eptr, ecode + 3, offset_top, md, ims, eptrb, match_isgroup)) ! break; ! ecode += (ecode[1] << 8) + ecode[2]; ! } ! while (*ecode == OP_ALT); ! /* If hit the end of the group (which could be repeated), fail */ ! if (*ecode != OP_ONCE && *ecode != OP_ALT) ! return FALSE; ! /* Continue as from after the assertion, updating the offsets high water ! mark, since extracts may have been taken. */ ! do ! ecode += (ecode[1] << 8) + ecode[2]; ! while (*ecode == OP_ALT); ! offset_top = md->end_offset_top; ! eptr = md->end_match_ptr; - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ ! if (*ecode == OP_KET || eptr == saved_eptr) { ! ecode += 3; ! break; ! } ! /* The repeating kets try the rest of the pattern or restart from the ! preceding bracket, in the appropriate order. We need to reset any options ! that changed within the bracket before re-running it, so check the next ! opcode. */ ! if (ecode[3] == OP_OPT) { ! ims = (ims & ~PCRE_IMS) | ecode[4]; ! DPRINTF(("ims set to %02lx at group repeat\n", ims)); ! } ! if (*ecode == OP_KETRMIN) { ! if (match(eptr, ecode + 3, offset_top, md, ims, eptrb, 0) || ! match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! } else { /* OP_KETRMAX */ ! if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || ! match(eptr, ecode + 3, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! } ! } ! return FALSE; ! /* An alternation is the end of a branch; scan along to find the end of the ! bracketed group and go to there. */ ! case OP_ALT: ! do ! ecode += (ecode[1] << 8) + ecode[2]; ! while (*ecode == OP_ALT); ! break; ! /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating ! that it may occur zero times. It may repeat infinitely, or not at all - ! i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper ! repeat limits are compiled as a number of copies, with the optional ones ! preceded by BRAZERO or BRAMINZERO. */ ! case OP_BRAZERO: ! { ! const uschar *next = ecode + 1; ! if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! do ! next += (next[1] << 8) + next[2]; ! while (*next == OP_ALT); ! ecode = next + 3; ! } ! break; ! case OP_BRAMINZERO: ! { ! const uschar *next = ecode + 1; ! do ! next += (next[1] << 8) + next[2]; ! while (*next == OP_ALT); ! if (match(eptr, next + 3, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! ecode++; ! } ! break; ! /* End of a group, repeated or non-repeating. If we are at the end of ! an assertion "group", stop matching and return TRUE, but record the ! current high water mark for use by positive assertions. Do this also ! for the "once" (not-backup up) groups. */ ! case OP_KET: ! case OP_KETRMIN: ! case OP_KETRMAX: ! { ! const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; ! const uschar *saved_eptr = eptrb->saved_eptr; ! eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */ ! if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || ! *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || ! *prev == OP_ONCE) { ! md->end_match_ptr = eptr; /* For ONCE */ ! md->end_offset_top = offset_top; ! return TRUE; ! } ! /* In all other cases except a conditional group we have to check the ! group number back at the start and if necessary complete handling an ! extraction by setting the offsets and bumping the high water mark. */ ! if (*prev != OP_COND) { ! int offset; ! int number = *prev - OP_BRA; ! /* For extended extraction brackets (large number), we have to fish out ! the number from a dummy opcode at the start. */ ! if (number > EXTRACT_BASIC_MAX) ! number = (prev[4] << 8) | prev[5]; ! offset = number << 1; ! #ifdef DEBUG ! printf("end bracket %d", number); ! printf("\n"); ! #endif ! if (number > 0) { ! if (offset >= md->offset_max) ! md->offset_overflow = TRUE; ! else { ! md->offset_vector[offset] = ! md->offset_vector[md->offset_end - number]; ! md->offset_vector[offset + 1] = eptr - md->start_subject; ! if (offset_top <= offset) ! offset_top = offset + 2; ! } ! } ! } ! /* Reset the value of the ims flags, in case they got changed during ! the group. */ ! ims = original_ims; ! DPRINTF(("ims reset to %02lx\n", ims)); ! /* For a non-repeating ket, just continue at this level. This also ! happens for a repeating ket if no characters were matched in the group. ! This is the forcible breaking of infinite loops as implemented in Perl ! 5.005. If there is an options reset, it will get obeyed in the normal ! course of events. */ ! if (*ecode == OP_KET || eptr == saved_eptr) { ! ecode += 3; ! break; ! } ! /* The repeating kets try the rest of the pattern or restart from the ! preceding bracket, in the appropriate order. */ ! if (*ecode == OP_KETRMIN) { ! if (match(eptr, ecode + 3, offset_top, md, ims, eptrb, 0) || ! match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) ! return TRUE; ! } else { /* OP_KETRMAX */ - if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || - match(eptr, ecode + 3, offset_top, md, ims, eptrb, 0)) - return TRUE; - } - } - return FALSE; - /* Start of subject unless notbol, or after internal newline if multiline */ ! case OP_CIRC: ! if (md->notbol && eptr == md->start_subject) ! return FALSE; ! if ((ims & PCRE_MULTILINE) != 0) { ! if (eptr != md->start_subject && eptr[-1] != NEWLINE) ! return FALSE; ! ecode++; ! break; ! } ! /* ... else fall through */ ! /* Start of subject assertion */ ! case OP_SOD: ! if (eptr != md->start_subject) ! return FALSE; ! ecode++; ! break; ! /* Assert before internal newline if multiline, or before a terminating ! newline unless endonly is set, else end of subject unless noteol is set. */ ! case OP_DOLL: ! if ((ims & PCRE_MULTILINE) != 0) { ! if (eptr < md->end_subject) { ! if (*eptr != NEWLINE) ! return FALSE; ! } else { ! if (md->noteol) ! return FALSE; ! } ! ecode++; ! break; ! } else { ! if (md->noteol) ! return FALSE; ! if (!md->endonly) { ! if (eptr < md->end_subject - 1 || ! (eptr == md->end_subject - 1 && *eptr != NEWLINE)) ! return FALSE; ! ecode++; ! break; ! } ! } ! /* ... else fall through */ ! /* End of subject assertion (\z) */ ! case OP_EOD: ! if (eptr < md->end_subject) return FALSE; ! ecode++; ! break; ! /* End of subject or ending \n assertion (\Z) */ - case OP_EODN: - if (eptr < md->end_subject - 1 || - (eptr == md->end_subject - 1 && *eptr != NEWLINE)) - return FALSE; - ecode++; - break; ! /* Word boundary assertions */ ! case OP_NOT_WORD_BOUNDARY: ! case OP_WORD_BOUNDARY: ! { ! BOOL prev_is_word = (eptr != md->start_subject) && ! ((md->ctypes[eptr[-1]] & ctype_word) != 0); ! BOOL cur_is_word = (eptr < md->end_subject) && ! ((md->ctypes[*eptr] & ctype_word) != 0); ! if ((*ecode++ == OP_WORD_BOUNDARY) ? ! cur_is_word == prev_is_word : cur_is_word != prev_is_word) ! return FALSE; ! } ! break; ! /* Match a single character type; inline for speed */ ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject ! && *eptr == NEWLINE) ! return FALSE; ! if (eptr++ >= md->end_subject) ! return FALSE; ! ecode++; ! break; ! case OP_NOT_DIGIT: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_digit) != 0) ! return FALSE; ! ecode++; ! break; ! case OP_DIGIT: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_digit) == 0) ! return FALSE; ! ecode++; ! break; ! case OP_NOT_WHITESPACE: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_space) != 0) ! return FALSE; ! ecode++; ! break; ! case OP_WHITESPACE: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_space) == 0) ! return FALSE; ! ecode++; ! break; ! case OP_NOT_WORDCHAR: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_word) != 0) ! return FALSE; ! ecode++; ! break; ! case OP_WORDCHAR: ! if (eptr >= md->end_subject || (md->ctypes[*eptr++] & ctype_word) == 0) ! return FALSE; ! ecode++; ! break; ! /* Match a back reference, possibly repeatedly. Look past the end of the ! item to see if there is repeat information following. The code is similar ! to that for character classes, but repeated for efficiency. Then obey ! similar code to character type repeats - written out again for speed. ! However, if the referenced string is the empty string, always treat ! it as matched, any number of times (otherwise there could be infinite ! loops). */ ! case OP_REF: ! { ! int length; ! int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */ ! ecode += 3; /* Advance past item */ ! /* If the reference is unset, set the length to be longer than the amount ! of subject left; this ensures that every attempt at a match fails. We ! can't just fail here, because of the possibility of quantifiers with zero ! minima. */ ! length = (offset >= offset_top || md->offset_vector[offset] < 0) ? ! md->end_subject - eptr + 1 : ! md->offset_vector[offset + 1] - md->offset_vector[offset]; ! /* Set up for repetition, or handle the non-repeated case */ ! switch (*ecode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRPLUS: ! case OP_CRMINPLUS: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! c = *ecode++ - OP_CRSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! minimize = (*ecode == OP_CRMINRANGE); ! min = (ecode[1] << 8) + ecode[2]; ! max = (ecode[3] << 8) + ecode[4]; ! if (max == 0) ! max = INT_MAX; ! ecode += 5; ! break; ! default: /* No repeat follows */ ! if (!match_ref(offset, eptr, length, md, ims)) ! return FALSE; ! eptr += length; ! continue; /* With the main loop */ } ! /* If the length of the reference is zero, just continue with the ! main loop. */ ! ! if (length == 0) ! continue; ! /* First, ensure the minimum number of matches are present. We get back ! the length of the reference string explicitly rather than passing the ! address of eptr, so that eptr can be a register variable. */ ! for (i = 1; i <= min; i++) { ! if (!match_ref(offset, eptr, length, md, ims)) ! return FALSE; ! eptr += length; ! } ! /* If min = max, continue at the same level without recursion. ! They are not both allowed to be zero. */ ! if (min == max) ! continue; ! /* If minimizing, keep trying and advancing the pointer */ ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || !match_ref(offset, eptr, length, md, ims)) ! return FALSE; ! eptr += length; ! } ! /* Control never gets here */ ! } ! /* If maximizing, find the longest string and work backwards */ ! else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (!match_ref(offset, eptr, length, md, ims)) ! break; ! eptr += length; ! } ! while (eptr >= pp) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! eptr -= length; ! } ! return FALSE; ! } } - /* Control never gets here */ ! /* Match a character class, possibly repeatedly. Look past the end of the ! item to see if there is repeat information following. Then obey similar ! code to character type repeats - written out again for speed. */ ! case OP_CLASS: ! { ! const uschar *data = ecode + 1; /* Save for matching */ ! ecode += 33; /* Advance past the item */ ! switch (*ecode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRPLUS: ! case OP_CRMINPLUS: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! c = *ecode++ - OP_CRSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! minimize = (*ecode == OP_CRMINRANGE); ! min = (ecode[1] << 8) + ecode[2]; ! max = (ecode[3] << 8) + ecode[4]; ! if (max == 0) ! max = INT_MAX; ! ecode += 5; ! break; ! default: /* No repeat follows */ ! min = max = 1; ! break; ! } ! /* First, ensure the minimum number of matches are present. */ ! for (i = 1; i <= min; i++) { ! if (eptr >= md->end_subject) ! return FALSE; ! GETCHARINC(c, eptr) ! /* Get character; increment eptr */ ! if ((data[c / 8] & (1 << (c & 7))) != 0) ! continue; ! return FALSE; ! } ! /* If max == min we can continue with the main loop without the ! need to recurse. */ ! if (min == max) ! continue; ! /* If minimizing, keep testing the rest of the expression and advancing ! the pointer while it matches the class. */ ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject) ! return FALSE; ! GETCHARINC(c, eptr) ! /* Get character; increment eptr */ ! if ((data[c / 8] & (1 << (c & 7))) != 0) ! continue; ! return FALSE; ! } ! /* Control never gets here */ ! } ! /* If maximizing, find the longest possible run, then work backwards. */ ! else { ! const uschar *pp = eptr; ! int len = 1; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject) ! break; ! GETCHARLEN(c, eptr, len) ! /* Get character, set length if UTF-8 */ ! if ((data[c / 8] & (1 << (c & 7))) == 0) ! break; ! eptr += len; ! } ! while (eptr >= pp) { ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! } ! return FALSE; ! } } ! /* Control never gets here */ ! /* Match a run of characters */ ! case OP_CHARS: ! { ! register int length = ecode[1]; ! ecode += 2; ! #ifdef DEBUG /* Sigh. Some compilers never learn. */ ! if (eptr >= md->end_subject) ! printf("matching subject against pattern "); ! else { ! printf("matching subject "); ! pchars(eptr, length, TRUE, md); ! printf(" against pattern "); ! } ! pchars(ecode, length, FALSE, md); ! printf("\n"); ! #endif ! if (length > md->end_subject - eptr) ! return FALSE; ! if ((ims & PCRE_CASELESS) != 0) { ! while (length-- > 0) ! if (md->lcc[*ecode++] != md->lcc[*eptr++]) ! return FALSE; ! } else { ! while (length-- > 0) ! if (*ecode++ != *eptr++) ! return FALSE; ! } } break; ! /* Match a single character repeatedly; different opcodes share code. */ ! case OP_EXACT: ! min = max = (ecode[1] << 8) + ecode[2]; ! ecode += 3; ! goto REPEATCHAR; ! case OP_UPTO: ! case OP_MINUPTO: ! min = 0; ! max = (ecode[1] << 8) + ecode[2]; ! minimize = *ecode == OP_MINUPTO; ! ecode += 3; ! goto REPEATCHAR; ! case OP_STAR: ! case OP_MINSTAR: ! case OP_PLUS: ! case OP_MINPLUS: ! case OP_QUERY: ! case OP_MINQUERY: ! c = *ecode++ - OP_STAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! /* Common code for all repeated single-character matches. We can give ! up quickly if there are fewer than the minimum number of characters left in ! the subject. */ ! REPEATCHAR: ! if (min > md->end_subject - eptr) ! return FALSE; ! c = *ecode++; ! /* The code is duplicated for the caseless and caseful cases, for speed, ! since matching characters is likely to be quite common. First, ensure the ! minimum number of matches are present. If min = max, continue at the same ! level without recursing. Otherwise, if minimizing, keep trying the rest of ! the expression and advancing one matching character if failing, up to the ! maximum. Alternatively, if maximizing, find the maximum number of ! characters and work backwards. */ ! DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, ! max, eptr)); ! if ((ims & PCRE_CASELESS) != 0) { ! c = md->lcc[c]; ! for (i = 1; i <= min; i++) ! if (c != md->lcc[*eptr++]) ! return FALSE; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject || c != md->lcc[*eptr++]) ! return FALSE; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c != md->lcc[*eptr]) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! return FALSE; ! } ! /* Control never gets here */ ! } ! /* Caseful comparisons */ ! else { ! for (i = 1; i <= min; i++) ! if (c != *eptr++) ! return FALSE; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject || c != *eptr++) ! return FALSE; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c != *eptr) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! return FALSE; } - } - /* Control never gets here */ ! /* Match a negated single character */ ! case OP_NOT: ! if (eptr >= md->end_subject) ! return FALSE; ! ecode++; ! if ((ims & PCRE_CASELESS) != 0) { ! if (md->lcc[*ecode++] == md->lcc[*eptr++]) ! return FALSE; ! } else { ! if (*ecode++ == *eptr++) ! return FALSE; } ! break; ! /* Match a negated single character repeatedly. This is almost a repeat of ! the code for a repeated single character, but I haven't found a nice way of ! commoning these up that doesn't require a test of the positive/negative ! option for each character match. Maybe that wouldn't add very much to the ! time taken, but character matching *is* what this is all about... */ ! case OP_NOTEXACT: ! min = max = (ecode[1] << 8) + ecode[2]; ! ecode += 3; ! goto REPEATNOTCHAR; ! case OP_NOTUPTO: ! case OP_NOTMINUPTO: ! min = 0; ! max = (ecode[1] << 8) + ecode[2]; ! minimize = *ecode == OP_NOTMINUPTO; ! ecode += 3; ! goto REPEATNOTCHAR; ! case OP_NOTSTAR: ! case OP_NOTMINSTAR: ! case OP_NOTPLUS: ! case OP_NOTMINPLUS: ! case OP_NOTQUERY: ! case OP_NOTMINQUERY: ! c = *ecode++ - OP_NOTSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! /* Common code for all repeated single-character matches. We can give ! up quickly if there are fewer than the minimum number of characters left in ! the subject. */ ! REPEATNOTCHAR: ! if (min > md->end_subject - eptr) ! return FALSE; ! c = *ecode++; ! /* The code is duplicated for the caseless and caseful cases, for speed, ! since matching characters is likely to be quite common. First, ensure the ! minimum number of matches are present. If min = max, continue at the same ! level without recursing. Otherwise, if minimizing, keep trying the rest of ! the expression and advancing one matching character if failing, up to the ! maximum. Alternatively, if maximizing, find the maximum number of ! characters and work backwards. */ ! DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, ! max, max, eptr)); ! if ((ims & PCRE_CASELESS) != 0) { ! c = md->lcc[c]; ! for (i = 1; i <= min; i++) ! if (c == md->lcc[*eptr++]) ! return FALSE; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject || c == md->lcc[*eptr++]) ! return FALSE; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c == md->lcc[*eptr]) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! return FALSE; } - /* Control never gets here */ - } ! /* Caseful comparisons */ ! else { ! for (i = 1; i <= min; i++) ! if (c == *eptr++) ! return FALSE; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject || c == *eptr++) ! return FALSE; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c == *eptr) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! return FALSE; } } ! /* Control never gets here */ ! /* Match a single character type repeatedly; several different opcodes ! share code. This is very similar to the code for single characters, but we ! repeat it in the interests of efficiency. */ ! case OP_TYPEEXACT: ! min = max = (ecode[1] << 8) + ecode[2]; ! minimize = TRUE; ! ecode += 3; ! goto REPEATTYPE; ! case OP_TYPEUPTO: ! case OP_TYPEMINUPTO: ! min = 0; ! max = (ecode[1] << 8) + ecode[2]; ! minimize = *ecode == OP_TYPEMINUPTO; ! ecode += 3; ! goto REPEATTYPE; ! case OP_TYPESTAR: ! case OP_TYPEMINSTAR: ! case OP_TYPEPLUS: ! case OP_TYPEMINPLUS: ! case OP_TYPEQUERY: ! case OP_TYPEMINQUERY: ! c = *ecode++ - OP_TYPESTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! /* Common code for all repeated single character type matches */ ! REPEATTYPE: ! ctype = *ecode++; /* Code for the character type */ ! /* First, ensure the minimum number of matches are present. Use inline ! code for maximizing the speed, and do the type test once at the start ! (i.e. keep it out of the loop). Also we can test that there are at least ! the minimum number of bytes before we start, except when doing '.' in ! UTF8 mode. Leave the test in in all cases; in the special case we have ! to test after each character. */ ! if (min > md->end_subject - eptr) ! return FALSE; ! if (min > 0) ! switch (ctype) { ! case OP_ANY: ! /* Non-UTF8 can be faster */ ! if ((ims & PCRE_DOTALL) == 0) { ! for (i = 1; i <= min; i++) ! if (*eptr++ == NEWLINE) ! return FALSE; ! } else ! eptr += min; ! break; ! case OP_NOT_DIGIT: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_digit) != 0) ! return FALSE; ! break; ! case OP_DIGIT: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_digit) == 0) ! return FALSE; ! break; ! case OP_NOT_WHITESPACE: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_space) != 0) ! return FALSE; ! break; ! case OP_WHITESPACE: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_space) == 0) ! return FALSE; ! break; ! case OP_NOT_WORDCHAR: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_word) != 0) ! return FALSE; ! break; ! case OP_WORDCHAR: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_word) == 0) ! return FALSE; ! break; } ! /* If min = max, continue at the same level without recursing */ ! if (min == max) ! continue; ! /* If minimizing, we have to test the rest of the pattern before each ! subsequent match. */ ! if (minimize) { ! for (i = min;; i++) { ! if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; ! if (i >= max || eptr >= md->end_subject) ! return FALSE; ! c = *eptr++; ! switch (ctype) { ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) ! return FALSE; ! break; ! case OP_NOT_DIGIT: ! if ((md->ctypes[c] & ctype_digit) != 0) ! return FALSE; ! break; ! case OP_DIGIT: ! if ((md->ctypes[c] & ctype_digit) == 0) ! return FALSE; ! break; ! case OP_NOT_WHITESPACE: ! if ((md->ctypes[c] & ctype_space) != 0) ! return FALSE; ! break; ! case OP_WHITESPACE: ! if ((md->ctypes[c] & ctype_space) == 0) ! return FALSE; ! break; ! case OP_NOT_WORDCHAR: ! if ((md->ctypes[c] & ctype_word) != 0) ! return FALSE; ! break; ! case OP_WORDCHAR: ! if ((md->ctypes[c] & ctype_word) == 0) ! return FALSE; ! break; ! } ! } ! /* Control never gets here */ ! } ! /* If maximizing it is worth using inline code for speed, doing the type ! test once at the start (i.e. keep it out of the loop). */ ! else { ! const uschar *pp = eptr; ! switch (ctype) { ! case OP_ANY: ! /* Special code is required for UTF8, but when the maximum is unlimited ! we don't need it. */ ! /* Non-UTF8 can be faster */ ! if ((ims & PCRE_DOTALL) == 0) { ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || *eptr == NEWLINE) ! break; ! eptr++; ! } ! } else { ! c = max - min; ! if (c > md->end_subject - eptr) ! c = md->end_subject - eptr; ! eptr += c; ! } break; ! case OP_NOT_DIGIT: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_digit) != 0) ! break; ! eptr++; ! } ! break; ! case OP_DIGIT: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_digit) == 0) ! break; ! eptr++; ! } ! break; ! case OP_NOT_WHITESPACE: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_space) != 0) ! break; ! eptr++; ! } ! break; ! case OP_WHITESPACE: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_space) == 0) ! break; ! eptr++; ! } ! break; ! case OP_NOT_WORDCHAR: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_word) != 0) ! break; ! eptr++; ! } ! break; ! case OP_WORDCHAR: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_word) == 0) ! break; ! eptr++; ! } ! break; ! } ! while (eptr >= pp) { ! if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ! return TRUE; } ! return FALSE; } ! /* Control never gets here */ ! /* There's been some horrible disaster. */ ! default: ! DPRINTF(("Unknown opcode %d\n", *ecode)); ! md->errorcode = PCRE_ERROR_UNKNOWN_NODE; ! return FALSE; ! } ! /* Do not stick any code in here without much thought; it is assumed ! that "continue" in the code above comes out to here to repeat the main ! loop. */ ! } /* End of main loop */ ! /* Control never reaches here */ ! } ! /************************************************* ! * Execute a Regular Expression * ! *************************************************/ ! /* This function applies a compiled re to a subject string and picks out ! portions of the string if it matches. Two elements in the vector are set for ! each substring: the offsets to the start and end of the substring. ! Arguments: ! external_re points to the compiled expression ! external_extra points to "hints" from pcre_study() or is NULL ! subject points to the subject string ! length length of subject string (may contain binary zeros) ! start_offset where to start in the subject string ! options option bits ! offsets points to a vector of ints to be filled in with offsets ! offsetcount the number of elements in the vector ! Returns: > 0 => success; value is the number of elements filled in ! = 0 => success, but offsets is not big enough ! -1 => failed to match ! < -1 => some kind of unexpected problem ! */ ! int ! pcre_exec(const pcre * external_re, const pcre_extra * external_extra, ! const char *subject, int length, int start_offset, int options, ! int *offsets, int offsetcount) ! { ! volatile match_data match_block; ! volatile BOOL using_temporary_offsets = FALSE; ! #ifdef BACKTRACK_LIMIT ! backtrack_count = 0; ! if (setjmp(backtrack_jmp)) { ! /* Too much backtracking! */ ! if (using_temporary_offsets) { ! DPRINTF(("Freeing temporary memory\n")); ! free(match_block.offset_vector); ! } ! return PCRE_ERROR_BACKTRACKING; ! } else ! #endif ! { ! int resetcount, ocount; ! int first_char = -1; ! int req_char = -1; ! int req_char2 = -1; ! unsigned long int ims = 0; ! const uschar *start_bits = NULL; ! const uschar *start_match = (const uschar *) subject + start_offset; ! const uschar *end_subject; ! const uschar *req_char_ptr = start_match - 1; ! const real_pcre *re = (const real_pcre *) external_re; ! const real_pcre_extra *extra = (const real_pcre_extra *) external_extra; ! BOOL anchored; ! BOOL startline; ! ! if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) ! return PCRE_ERROR_BADOPTION; ! ! if (re == NULL || subject == NULL || (offsets == NULL && offsetcount > 0)) ! return PCRE_ERROR_NULL; ! if (re->magic_number != MAGIC_NUMBER) ! return PCRE_ERROR_BADMAGIC; ! ! anchored = ((re->options | options) & PCRE_ANCHORED) != 0; ! startline = (re->options & PCRE_STARTLINE) != 0; ! ! match_block.start_pattern = re->code; ! match_block.start_subject = (const uschar *) subject; ! match_block.end_subject = match_block.start_subject + length; ! end_subject = match_block.end_subject; ! ! match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; ! match_block.utf8 = (re->options & PCRE_UTF8) != 0; ! ! match_block.notbol = (options & PCRE_NOTBOL) != 0; ! match_block.noteol = (options & PCRE_NOTEOL) != 0; ! match_block.notempty = (options & PCRE_NOTEMPTY) != 0; ! match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ ! match_block.lcc = re->tables + lcc_offset; ! match_block.ctypes = re->tables + ctypes_offset; ! /* The ims options can vary during the matching as a result of the presence ! of (?ims) items in the pattern. They are kept in a local variable so that ! restoring at the exit of a group is easy. */ ! ims = re->options & (PCRE_CASELESS | PCRE_MULTILINE | PCRE_DOTALL); ! /* If the expression has got more back references than the offsets supplied can ! hold, we get a temporary bit of working store to use during the matching. ! Otherwise, we can use the vector supplied, rounding down its size to a multiple ! of 3. */ ! ocount = offsetcount - (offsetcount % 3); ! if (re->top_backref > 0 && re->top_backref >= ocount / 3) { ! ocount = re->top_backref * 3 + 3; ! match_block.offset_vector = (int *) (pcre_malloc) (ocount * sizeof(int)); ! if (match_block.offset_vector == NULL) ! return PCRE_ERROR_NOMEMORY; ! using_temporary_offsets = TRUE; ! DPRINTF(("Got memory to hold back references\n")); ! } else ! match_block.offset_vector = offsets; ! match_block.offset_end = ocount; ! match_block.offset_max = (2 * ocount) / 3; ! match_block.offset_overflow = FALSE; ! /* Compute the minimum number of offsets that we need to reset each time. Doing ! this makes a huge difference to execution time when there aren't many brackets ! in the pattern. */ ! resetcount = 2 + re->top_bracket * 2; ! if (resetcount > offsetcount) ! resetcount = ocount; ! /* Reset the working variable associated with each extraction. These should ! never be used unless previously set, but they get saved and restored, and so we ! initialize them to avoid reading uninitialized locations. */ ! if (match_block.offset_vector != NULL) { ! register int *iptr = match_block.offset_vector + ocount; ! register int *iend = iptr - resetcount / 2 + 1; ! while (--iptr >= iend) ! *iptr = -1; ! } ! /* Set up the first character to match, if available. The first_char value is ! never set for an anchored regular expression, but the anchoring may be forced ! at run time, so we have to test for anchoring. The first char may be unset for ! an unanchored pattern, of course. If there's no first char and the pattern was ! studied, there may be a bitmap of possible first characters. */ - if (!anchored) { - if ((re->options & PCRE_FIRSTSET) != 0) { - first_char = re->first_char; - if ((ims & PCRE_CASELESS) != 0) - first_char = match_block.lcc[first_char]; - } else - if (!startline && extra != NULL && - (extra->options & PCRE_STUDY_MAPPED) != 0) - start_bits = extra->start_bits; - } - /* For anchored or unanchored matches, there may be a "last known required - character" set. If the PCRE_CASELESS is set, implying that the match starts - caselessly, or if there are any changes of this flag within the regex, set up - both cases of the character. Otherwise set the two values the same, which will - avoid duplicate testing (which takes significant time). This covers the vast - majority of cases. It will be suboptimal when the case flag changes in a regex - and the required character in fact is caseful. */ - - if ((re->options & PCRE_REQCHSET) != 0) { - req_char = re->req_char; - req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0) ? - (re->tables + fcc_offset)[req_char] : req_char; - } ! /* Loop for handling unanchored repeated matching attempts; for anchored regexs ! the loop runs just once. */ ! do { ! int rc; ! register int *iptr = match_block.offset_vector; ! register int *iend = iptr + resetcount; ! ! /* Reset the maximum number of extractions we might see. */ ! ! while (iptr < iend) ! *iptr++ = -1; ! ! /* Advance to a unique first char if possible */ ! ! if (first_char >= 0) { ! if ((ims & PCRE_CASELESS) != 0) ! while (start_match < end_subject && ! match_block.lcc[*start_match] != first_char) ! start_match++; ! else ! while (start_match < end_subject && *start_match != first_char) ! start_match++; ! } ! /* Or to just after \n for a multiline match if possible */ ! else if (startline) { ! if (start_match > match_block.start_subject + start_offset) { ! while (start_match < end_subject && start_match[-1] != NEWLINE) ! start_match++; } - } ! /* Or to a non-unique first char after study */ ! else if (start_bits != NULL) { ! while (start_match < end_subject) { ! register int c = *start_match; ! if ((start_bits[c / 8] & (1 << (c & 7))) == 0) ! start_match++; ! else ! break; } - } - #ifdef DEBUG /* Sigh. Some compilers never learn. */ - printf(">>>> Match against: "); - pchars(start_match, end_subject - start_match, TRUE, &match_block); - printf("\n"); - #endif ! /* If req_char is set, we know that that character must appear in the subject ! for the match to succeed. If the first character is set, req_char must be ! later in the subject; otherwise the test starts at the match point. This ! optimization can save a huge amount of backtracking in patterns with nested ! unlimited repeats that aren't going to match. We don't know what the state of ! case matching may be when this character is hit, so test for it in both its ! cases if necessary. However, the different cased versions will not be set up ! unless PCRE_CASELESS was given or the casing state changes within the regex. ! Writing separate code makes it go faster, as does using an autoincrement and ! backing off on a match. */ ! ! if (req_char >= 0) { ! register const uschar *p = start_match + ((first_char >= 0) ? 1 : 0); ! ! /* We don't need to repeat the search if we haven't yet reached the ! place we found it at last time. */ ! ! if (p > req_char_ptr) { ! /* Do a single test if no case difference is set up */ ! ! if (req_char == req_char2) { ! while (p < end_subject) { ! if (*p++ == req_char) { ! p--; break; ! } } } ! /* Otherwise test for either case */ - else { - while (p < end_subject) { - register int pp = *p++; - if (pp == req_char || pp == req_char2) { - p--; - break; - } - } - } - /* If we can't find the required character, break the matching loop */ ! if (p >= end_subject) ! break; - /* If we have found the required character, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this character yet. */ ! req_char_ptr = p; } } ! /* When a match occurs, substrings will be set for all internal extractions; ! we just need to set up the whole thing as substring 0 before returning. If ! there were too many extractions, set the return code to zero. In the case ! where we had to get some local store to hold offsets for backreferences, copy ! those back references that we can. In this case there need not be overflow ! if certain parts of the pattern were not used. */ ! match_block.start_match = start_match; ! if (!match ! (start_match, re->code, 2, &match_block, ims, NULL, match_isgroup)) ! continue; ! /* Copy the offset information from temporary store if necessary */ ! if (using_temporary_offsets) { ! if (offsetcount >= 4) { ! memcpy(offsets + 2, match_block.offset_vector + 2, ! (offsetcount - 2) * sizeof(int)); ! DPRINTF(("Copied offsets from temporary memory\n")); ! } ! if (match_block.end_offset_top > offsetcount) ! match_block.offset_overflow = TRUE; ! DPRINTF(("Freeing temporary memory\n")); ! (pcre_free) (match_block.offset_vector); ! } ! rc = match_block.offset_overflow ? 0 : match_block.end_offset_top / 2; ! if (offsetcount < 2) ! rc = 0; ! else { ! offsets[0] = start_match - match_block.start_subject; ! offsets[1] = match_block.end_match_ptr - match_block.start_subject; } ! DPRINTF((">>>> returning %d\n", rc)); ! return rc; ! } ! /* This "while" is the end of the "do" above */ ! while (!anchored && ! match_block.errorcode == PCRE_ERROR_NOMATCH && ! start_match++ < end_subject); ! if (using_temporary_offsets) { ! DPRINTF(("Freeing temporary memory\n")); ! (pcre_free) (match_block.offset_vector); ! } ! DPRINTF((">>>> returning %d\n", match_block.errorcode)); ! } ! return match_block.errorcode; ! } ! /* End of pcre.c */ ! /************************************************* ! * Set a bit and maybe its alternate case * ! *************************************************/ ! /* Given a character, set its bit in the table, and also the bit for the other ! version of a letter if we are caseless. ! Arguments: ! start_bits points to the bit map ! c is the character ! caseless the caseless flag ! cd the block with char table pointers ! Returns: nothing ! */ ! static void ! set_bit(uschar * start_bits, int c, BOOL caseless, compile_data * cd) ! { ! start_bits[c / 8] |= (1 << (c & 7)); ! if (caseless && (cd->ctypes[c] & ctype_letter) != 0) ! start_bits[cd->fcc[c] / 8] |= (1 << (cd->fcc[c] & 7)); ! } ! /************************************************* ! * Create bitmap of starting chars * ! *************************************************/ ! /* This function scans a compiled unanchored expression and attempts to build a ! bitmap of the set of initial characters. If it can't, it returns FALSE. As time ! goes by, we may be able to get more clever at doing this. ! Arguments: ! code points to an expression ! start_bits points to a 32-byte table, initialized to 0 ! caseless the current state of the caseless flag ! cd the block with char table pointers ! Returns: TRUE if table built, FALSE otherwise ! */ ! static BOOL ! set_start_bits(const uschar * code, uschar * start_bits, BOOL caseless, ! compile_data * cd) ! { ! register int c; ! /* This next statement and the later reference to dummy are here in order to ! trick the optimizer of the IBM C compiler for OS/2 into generating correct ! code. Apparently IBM isn't going to fix the problem, and we would rather not ! disable optimization (in this module it actually makes a big difference, and ! the pcre module can use all the optimization it can get). */ ! volatile int dummy; ! do { ! const uschar *tcode = code + 3; ! BOOL try_next = TRUE; ! while (try_next) { ! /* If a branch starts with a bracket or a positive lookahead assertion, ! recurse to set bits from within them. That's all for this branch. */ ! if ((int) *tcode >= OP_BRA || *tcode == OP_ASSERT) { ! if (!set_start_bits(tcode, start_bits, caseless, cd)) ! return FALSE; ! try_next = FALSE; } ! else ! switch (*tcode) { ! default: ! return FALSE; ! /* Skip over extended extraction bracket number */ ! case OP_BRANUMBER: ! tcode += 3; ! break; ! /* Skip over lookbehind and negative lookahead assertions */ ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK: ! case OP_ASSERTBACK_NOT: ! do ! tcode += (tcode[1] << 8) + tcode[2]; ! while (*tcode == OP_ALT); ! tcode += 3; ! break; ! /* Skip over an option setting, changing the caseless flag */ ! case OP_OPT: ! caseless = (tcode[1] & PCRE_CASELESS) != 0; ! tcode += 2; ! break; ! /* BRAZERO does the bracket, but carries on. */ ! case OP_BRAZERO: ! case OP_BRAMINZERO: ! if (!set_start_bits(++tcode, start_bits, caseless, cd)) ! return FALSE; ! dummy = 1; ! do ! tcode += (tcode[1] << 8) + tcode[2]; ! while (*tcode == OP_ALT); ! tcode += 3; ! break; ! /* Single-char * or ? sets the bit and tries the next item */ ! case OP_STAR: ! case OP_MINSTAR: ! case OP_QUERY: ! case OP_MINQUERY: ! set_bit(start_bits, tcode[1], caseless, cd); ! tcode += 2; ! break; ! /* Single-char upto sets the bit and tries the next */ ! case OP_UPTO: ! case OP_MINUPTO: ! set_bit(start_bits, tcode[3], caseless, cd); ! tcode += 4; ! break; ! /* At least one single char sets the bit and stops */ ! case OP_EXACT: /* Fall through */ ! tcode++; ! case OP_CHARS: /* Fall through */ ! tcode++; ! case OP_PLUS: ! case OP_MINPLUS: ! set_bit(start_bits, tcode[1], caseless, cd); ! try_next = FALSE; break; ! /* Single character type sets the bits and stops */ case OP_NOT_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_digit]; ! try_next = FALSE; break; case OP_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_digit]; ! try_next = FALSE; break; case OP_NOT_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_space]; ! try_next = FALSE; break; case OP_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_space]; ! try_next = FALSE; break; case OP_NOT_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_word]; ! try_next = FALSE; break; case OP_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_word]; ! try_next = FALSE; break; ! /* One or more character type fudges the pointer and restarts, knowing ! it will hit a single character type and stop there. */ ! case OP_TYPEPLUS: ! case OP_TYPEMINPLUS: ! tcode++; ! break; ! case OP_TYPEEXACT: ! tcode += 3; ! break; ! /* Zero or more repeats of character types set the bits and then ! try again. */ ! case OP_TYPEUPTO: ! case OP_TYPEMINUPTO: ! tcode += 2; /* Fall through */ - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - switch (tcode[1]) { case OP_NOT_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_digit]; break; case OP_DIGIT: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_digit]; break; case OP_NOT_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_space]; break; case OP_WHITESPACE: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_space]; break; case OP_NOT_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= ~cd->cbits[c + cbit_word]; break; case OP_WORDCHAR: ! for (c = 0; c < 32; c++) ! start_bits[c] |= cd->cbits[c + cbit_word]; break; } ! tcode += 2; ! break; ! /* Character class: set the bits and either carry on or not, ! according to the repeat count. */ ! ! case OP_CLASS: ! { ! tcode++; ! for (c = 0; c < 32; c++) ! start_bits[c] |= tcode[c]; ! tcode += 32; ! switch (*tcode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! tcode++; ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! if (((tcode[1] << 8) + tcode[2]) == 0) ! tcode += 5; ! else ! try_next = FALSE; ! break; ! default: ! try_next = FALSE; ! break; ! } ! } ! break; /* End of class handling */ ! } /* End of switch */ ! } /* End of try_next loop */ ! code += (code[1] << 8) + code[2]; /* Advance to next branch */ ! } ! while (*code == OP_ALT); ! return TRUE; } /************************************************* ! * Study a compiled expression * *************************************************/ ! /* This function is handed a compiled expression that it must study to produce ! information that will speed up the matching. It returns a pcre_extra block ! which then gets handed back to pcre_exec(). Arguments: ! re points to the compiled expression ! options contains option bits ! errorptr points to where to place error messages; ! set NULL unless error ! Returns: pointer to a pcre_extra block, ! NULL on error or if no optimization possible */ ! pcre_extra * ! pcre_study(const pcre * external_re, int options, const char **errorptr) { ! uschar start_bits[32]; ! real_pcre_extra *extra; const real_pcre *re = (const real_pcre *) external_re; - compile_data compile_block; ! *errorptr = NULL; ! if (re == NULL || re->magic_number != MAGIC_NUMBER) { ! *errorptr = "argument is not a compiled regular expression"; ! return NULL; } ! if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) { ! *errorptr = "unknown or incorrect option bit(s) set"; ! return NULL; ! } ! /* For an anchored pattern, or an unchored pattern that has a first char, or a ! multiline pattern that matches only at "line starts", no further processing at ! present. */ ! if ((re->options & (PCRE_ANCHORED | PCRE_FIRSTSET | PCRE_STARTLINE)) != 0) ! return NULL; ! /* Set the character tables in the block which is passed around */ ! compile_block.lcc = re->tables + lcc_offset; ! compile_block.fcc = re->tables + fcc_offset; ! compile_block.cbits = re->tables + cbits_offset; ! compile_block.ctypes = re->tables + ctypes_offset; ! /* See if we can find a fixed set of initial characters for the pattern. */ ! memset(start_bits, 0, 32 * sizeof(uschar)); ! if (!set_start_bits(re->code, start_bits, (re->options & PCRE_CASELESS) != 0, ! &compile_block)) ! return NULL; ! /* Get an "extra" block and put the information therein. */ ! extra = (real_pcre_extra *) (pcre_malloc) (sizeof(real_pcre_extra)); ! if (extra == NULL) { ! *errorptr = "failed to get memory"; ! return NULL; } ! extra->options = PCRE_STUDY_MAPPED; ! memcpy(extra->start_bits, start_bits, sizeof(start_bits)); ! return (pcre_extra *) extra; ! } ! /* End of study.c */ ! /************************************************* ! * Create PCRE character tables * ! *************************************************/ ! /* This function builds a set of character tables for use by PCRE and returns ! a pointer to them. They are build using the ctype functions, and consequently ! their contents will depend upon the current locale setting. When compiled as ! part of the library, the store is obtained via pcre_malloc(), but when compiled ! inside dftables, use malloc(). ! Arguments: none ! Returns: pointer to the contiguous block of data ! */ ! const unsigned char * ! pcre_maketables(void) ! { ! unsigned char *yield, *p; ! int i; ! #ifndef DFTABLES ! yield = (unsigned char *) (pcre_malloc) (tables_length); ! #else ! yield = (unsigned char *) malloc(tables_length); ! #endif ! if (yield == NULL) ! return NULL; ! p = yield; ! /* First comes the lower casing table */ ! for (i = 0; i < 256; i++) ! *p++ = tolower(i); ! /* Next the case-flipping table */ ! for (i = 0; i < 256; i++) ! *p++ = islower(i) ? toupper(i) : tolower(i); ! /* Then the character class tables. Don't try to be clever and save effort ! on exclusive ones - in some locales things may be different. */ ! memset(p, 0, cbit_length); ! for (i = 0; i < 256; i++) { ! if (isdigit(i)) { ! p[cbit_digit + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); } ! if (isupper(i)) { ! p[cbit_upper + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); } ! if (islower(i)) { ! p[cbit_lower + i / 8] |= 1 << (i & 7); ! p[cbit_word + i / 8] |= 1 << (i & 7); } - if (i == '_') - p[cbit_word + i / 8] |= 1 << (i & 7); - if (isspace(i)) - p[cbit_space + i / 8] |= 1 << (i & 7); - if (isxdigit(i)) - p[cbit_xdigit + i / 8] |= 1 << (i & 7); - if (isgraph(i)) - p[cbit_graph + i / 8] |= 1 << (i & 7); - if (isprint(i)) - p[cbit_print + i / 8] |= 1 << (i & 7); - if (ispunct(i)) - p[cbit_punct + i / 8] |= 1 << (i & 7); - if (iscntrl(i)) - p[cbit_cntrl + i / 8] |= 1 << (i & 7); - } - p += cbit_length; ! /* Finally, the character type table */ ! for (i = 0; i < 256; i++) { ! int x = 0; ! if (isspace(i)) ! x += ctype_space; ! if (isalpha(i)) ! x += ctype_letter; ! if (isdigit(i)) ! x += ctype_digit; ! if (isxdigit(i)) ! x += ctype_xdigit; ! if (isalnum(i) || i == '_') ! x += ctype_word; ! if (strchr("*+?{^.$|()[", i) != 0) ! x += ctype_meta; ! *p++ = x; ! } ! return yield; ! } ! /* End of maketables.c */ ! /************************************************* ! * Copy captured string to given buffer * ! *************************************************/ ! /* This function copies a single captured substring into a given buffer. ! Note that we use memcpy() rather than strncpy() in case there are binary zeros ! in the string. ! Arguments: ! subject the subject string that was matched ! ovector pointer to the offsets table ! stringcount the number of substrings that were captured ! (i.e. the yield of the pcre_exec call, unless ! that was zero, in which case it should be 1/3 ! of the offset table size) ! stringnumber the number of the required substring ! buffer where to put the substring ! size the size of the buffer ! Returns: if successful: ! the length of the copied string, not including the zero ! that is put on the end; can be zero ! if not successful: ! PCRE_ERROR_NOMEMORY (-6) buffer too small ! PCRE_ERROR_NOSUBSTRING (-7) no such captured substring ! */ ! int ! pcre_copy_substring(const char *subject, int *ovector, int stringcount, ! int stringnumber, char *buffer, int size) ! { ! int yield; ! if (stringnumber < 0 || stringnumber >= stringcount) ! return PCRE_ERROR_NOSUBSTRING; ! stringnumber *= 2; ! yield = ovector[stringnumber + 1] - ovector[stringnumber]; ! if (size < yield + 1) ! return PCRE_ERROR_NOMEMORY; ! memcpy(buffer, subject + ovector[stringnumber], yield); ! buffer[yield] = 0; ! return yield; } ! /* End of get.c */ --- 3598,7145 ---- have to change if any new ones are ever created. */ if (c < 0) { ! if (-c == ESC_Q) { /* Handle start of quoted string */ ! if (ptr[1] == '\\' && ptr[2] == 'E') ! ptr += 2; /* avoid empty string */ ! else ! inescq = TRUE; ! continue; } ! /* For metasequences that actually match a character, we disable the ! setting of a first character if it hasn't already been set. */ ! if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z) ! firstbyte = REQ_NONE; ! /* Set values to reset to if this is followed by a zero repeat. */ + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + /* Back references are handled specially */ + if (-c >= ESC_REF) { + int number = -c - ESC_REF; + previous = code; + *code++ = OP_REF; + PUT2INC(code, 0, number); + } else { + previous = (-c > ESC_b && -c < ESC_Z) ? code : NULL; + *code++ = -c; + } + continue; + } ! /* Data character: reset and fall through */ ! ptr = tempptr; ! c = '\\'; ! /* Handle a run of data characters until a metacharacter is encountered. ! The first character is guaranteed not to be whitespace or # when the ! extended flag is set. */ ! NORMAL_CHAR: ! default: ! previous = code; ! *code = OP_CHARS; ! code += 2; ! length = 0; ! do { ! /* If in \Q...\E, check for the end; if not, we always have a literal */ ! if (inescq) { ! if (c == '\\' && ptr[1] == 'E') { ! inescq = FALSE; ! ptr++; ! } else { ! *code++ = c; ! length++; ! } ! continue; ! } + /* Skip white space and comments for /x patterns */ + if ((options & PCRE_EXTENDED) != 0) { + if ((cd->ctypes[c] & ctype_space) != 0) + continue; + if (c == '#') { + /* The space before the ; is to avoid a warning on a silly compiler + on the Macintosh. */ + while ((c = *(++ptr)) != 0 && c != NEWLINE) ; + if (c == 0) + break; + continue; + } + } ! /* Backslash may introduce a data char or a metacharacter. Escaped items ! are checked for validity in the pre-compiling pass. Stop the string ! before a metaitem. */ ! if (c == '\\') { ! tempptr = ptr; ! c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); ! if (c < 0) { ! ptr = tempptr; ! break; ! } ! /* If a character is > 127 in UTF-8 mode, we have to turn it into ! two or more characters in the UTF-8 encoding. */ ! } + /* Ordinary character or single-char escape */ + *code++ = c; + length++; + } ! /* This "while" is the end of the "do" above. */ ! while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); ! /* Update the first and last requirements. These are always bytes, even in ! UTF-8 mode. However, there is a special case to be considered when there ! are only one or two characters. Because this gets messy in UTF-8 mode, the ! code is kept separate. When we get here "length" contains the number of ! bytes. */ ! /* This is the code for non-UTF-8 operation, either without UTF-8 support, ! or when UTF-8 is not enabled. */ ! { ! /* firstbyte was not previously set; take it from this string */ ! if (firstbyte == REQ_UNSET) { ! if (length == 1) { ! zerofirstbyte = REQ_NONE; ! firstbyte = previous[2] | req_caseopt; ! zeroreqbyte = reqbyte; ! } else { ! zerofirstbyte = firstbyte = previous[2] | req_caseopt; ! zeroreqbyte = (length > 2) ? ! (code[-2] | req_caseopt | cd->req_varyopt) : reqbyte; ! reqbyte = code[-1] | req_caseopt | cd->req_varyopt; ! } ! } ! /* firstbyte was previously set */ ! else { ! zerofirstbyte = firstbyte; ! zeroreqbyte = (length == 1) ? reqbyte : ! code[-2] | req_caseopt | cd->req_varyopt; ! reqbyte = code[-1] | req_caseopt | cd->req_varyopt; ! } ! } ! /* Set the length in the data vector, and advance to the next state. */ ! previous[1] = length; ! if (length < MAXLIT) ! ptr--; break; } + } /* end of big loop */ ! /* Control never reaches here by falling through, only by a goto for all the ! error states. Pass back the position in the pattern so that it can be displayed ! to the user for diagnosing the error. */ + FAILED: + *ptrptr = ptr; + return FALSE; + } /************************************************* ! * Compile sequence of alternatives * *************************************************/ ! /* On entry, ptr is pointing past the bracket character, but on return ! it points to the closing bracket, or vertical bar, or end of string. ! The code variable is pointing at the byte into which the BRA operator has been ! stored. If the ims options are changed at the start (for a (?ims: group) or ! during any branch, we need to insert an OP_OPT item at the start of every ! following branch to ensure they get set correctly at run time, and also pass ! the new options into every subsequent branch compile. ! Argument: ! options option bits, including any changes for this subpattern ! oldims previous settings of ims option bits ! brackets -> int containing the number of extracting brackets used ! codeptr -> the address of the current code pointer ! ptrptr -> the address of the current pattern pointer ! errorptr -> pointer to error message ! lookbehind TRUE if this is a lookbehind assertion ! skipbytes skip this many bytes at start (for OP_COND, OP_BRANUMBER) ! firstbyteptr place to put the first required character, or a negative number ! reqbyteptr place to put the last required character, or a negative number ! bcptr pointer to the chain of currently open branches ! cd points to the data block with tables pointers etc. ! Returns: TRUE on success */ ! static BOOL ! compile_regex(int options, int oldims, int *brackets, uschar ** codeptr, ! const uschar ** ptrptr, const char **errorptr, BOOL lookbehind, ! int skipbytes, int *firstbyteptr, int *reqbyteptr, ! branch_chain * bcptr, compile_data * cd) { ! const uschar *ptr = *ptrptr; ! uschar *code = *codeptr; ! uschar *last_branch = code; ! uschar *start_bracket = code; ! uschar *reverse_count = NULL; ! int firstbyte, reqbyte; ! int branchfirstbyte, branchreqbyte; ! branch_chain bc; ! bc.outer = bcptr; ! bc.current = code; ! firstbyte = reqbyte = REQ_UNSET; ! /* Offset is set zero to mark that this bracket is still open */ ! PUT(code, 1, 0); ! code += 1 + LINK_SIZE + skipbytes; ! /* Loop for each alternative branch */ ! for (;;) { ! /* Handle a change of ims options at the start of the branch */ ! if ((options & PCRE_IMS) != oldims) { ! *code++ = OP_OPT; ! *code++ = options & PCRE_IMS; ! } ! /* Set up dummy OP_REVERSE if lookbehind assertion */ ! if (lookbehind) { ! *code++ = OP_REVERSE; ! reverse_count = code; ! PUTINC(code, 0, 0); ! } ! /* Now compile the branch */ ! if (!compile_branch(&options, brackets, &code, &ptr, errorptr, ! &branchfirstbyte, &branchreqbyte, &bc, cd)) { ! *ptrptr = ptr; ! return FALSE; ! } ! ! /* If this is the first branch, the firstbyte and reqbyte values for the ! branch become the values for the regex. */ ! ! if (*last_branch != OP_ALT) { ! firstbyte = branchfirstbyte; ! reqbyte = branchreqbyte; ! } ! ! /* If this is not the first branch, the first char and reqbyte have to ! match the values from all the previous branches, except that if the previous ! value for reqbyte didn't have REQ_VARY set, it can still match, and we set ! REQ_VARY for the regex. */ ! ! else { ! /* If we previously had a firstbyte, but it doesn't match the new branch, ! we have to abandon the firstbyte for the regex, but if there was previously ! no reqbyte, it takes on the value of the old firstbyte. */ ! ! if (firstbyte >= 0 && firstbyte != branchfirstbyte) { ! if (reqbyte < 0) ! reqbyte = firstbyte; ! firstbyte = REQ_NONE; ! } ! ! /* If we (now or from before) have no firstbyte, a firstbyte from the ! branch becomes a reqbyte if there isn't a branch reqbyte. */ ! ! if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0) ! branchreqbyte = branchfirstbyte; ! /* Now ensure that the reqbytes match */ ! if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY)) ! reqbyte = REQ_NONE; ! else ! reqbyte |= branchreqbyte; /* To "or" REQ_VARY */ ! } ! /* If lookbehind, check that this branch matches a fixed-length string, ! and put the length into the OP_REVERSE item. Temporarily mark the end of ! the branch with OP_END. */ ! if (lookbehind) { ! int length; ! *code = OP_END; ! length = find_fixedlength(last_branch, options); ! DPRINTF(("fixed length = %d\n", length)); ! if (length < 0) { ! *errorptr = (length == -2) ? ERR36 : ERR25; ! *ptrptr = ptr; ! return FALSE; } + PUT(reverse_count, 0, length); } ! /* Reached end of expression, either ')' or end of pattern. Go back through ! the alternative branches and reverse the chain of offsets, with the field in ! the BRA item now becoming an offset to the first alternative. If there are ! no alternatives, it points to the end of the group. The length in the ! terminating ket is always the length of the whole bracketed item. If any of ! the ims options were changed inside the group, compile a resetting op-code ! following, except at the very end of the pattern. Return leaving the pointer ! at the terminating char. */ ! if (*ptr != '|') { ! int length = code - last_branch; ! do { ! int prev_length = GET(last_branch, 1); ! PUT(last_branch, 1, length); ! length = prev_length; ! last_branch -= length; } ! while (length > 0); ! /* Fill in the ket */ ! *code = OP_KET; ! PUT(code, 1, code - start_bracket); ! code += 1 + LINK_SIZE; ! ! /* Resetting option if needed */ ! ! if ((options & PCRE_IMS) != oldims && *ptr == ')') { ! *code++ = OP_OPT; ! *code++ = oldims; } ! /* Set values to pass back */ ! *codeptr = code; ! *ptrptr = ptr; ! *firstbyteptr = firstbyte; ! *reqbyteptr = reqbyte; ! return TRUE; ! } ! /* Another branch follows; insert an "or" node. Its length field points back ! to the previous branch while the bracket remains open. At the end the chain ! is reversed. It's done like this so that the start of the bracket has a ! zero offset until it is closed, making it possible to detect recursion. */ ! *code = OP_ALT; ! PUT(code, 1, code - last_branch); ! bc.current = last_branch = code; ! code += 1 + LINK_SIZE; ! ptr++; ! } ! /* Control never reaches here */ ! } ! /************************************************* ! * Check for anchored expression * ! *************************************************/ ! /* Try to find out if this is an anchored regular expression. Consider each ! alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket ! all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then ! it's anchored. However, if this is a multiline pattern, then only OP_SOD ! counts, since OP_CIRC can match in the middle. ! We can also consider a regex to be anchored if OP_SOM starts all its branches. ! This is the code for \G, which means "match at start of match position, taking ! into account the match offset". ! A branch is also implicitly anchored if it starts with .* and DOTALL is set, ! because that will try the rest of the pattern at all possible matching points, ! so there is no point trying again.... er .... ! .... except when the .* appears inside capturing parentheses, and there is a ! subsequent back reference to those parentheses. We haven't enough information ! to catch that case precisely. ! ! At first, the best we could do was to detect when .* was in capturing brackets ! and the highest back reference was greater than or equal to that level. ! However, by keeping a bitmap of the first 31 back references, we can catch some ! of the more common cases more precisely. ! Arguments: ! code points to start of expression (the bracket) ! options points to the options setting ! bracket_map a bitmap of which brackets we are inside while testing; this ! handles up to substring 31; after that we just have to take ! the less precise approach ! backref_map the back reference bitmap ! Returns: TRUE or FALSE ! */ ! static BOOL ! is_anchored(register const uschar * code, int *options, ! unsigned int bracket_map, unsigned int backref_map) ! { ! do { ! const uschar *scode = ! first_significant_code(code + 1 + LINK_SIZE, options, PCRE_MULTILINE); ! register int op = *scode; ! /* Capturing brackets */ ! if (op > OP_BRA) { ! int new_map; ! op -= OP_BRA; ! if (op > EXTRACT_BASIC_MAX) ! op = GET2(scode, 2 + LINK_SIZE); ! new_map = bracket_map | ((op < 32) ? (1 << op) : 1); ! if (!is_anchored(scode, options, new_map, backref_map)) ! return FALSE; ! } ! /* Other brackets */ ! ! else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) { ! if (!is_anchored(scode, options, bracket_map, backref_map)) ! return FALSE; ! } ! /* .* is not anchored unless DOTALL is set and it isn't in brackets that ! are or may be referenced. */ ! else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) && ! (*options & PCRE_DOTALL) != 0) { ! if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) ! return FALSE; ! } ! /* Check for explicit anchoring */ ! else if (op != OP_SOD && op != OP_SOM && ! ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) ! return FALSE; ! code += GET(code, 1); ! } ! while (*code == OP_ALT); /* Loop for each alternative */ ! return TRUE; ! } ! /************************************************* ! * Check for starting with ^ or .* * ! *************************************************/ ! /* This is called to find out if every branch starts with ^ or .* so that ! "first char" processing can be done to speed things up in multiline ! matching and for non-DOTALL patterns that start with .* (which must start at ! the beginning or after \n). As in the case of is_anchored() (see above), we ! have to take account of back references to capturing brackets that contain .* ! because in that case we can't make the assumption. ! Arguments: ! code points to start of expression (the bracket) ! bracket_map a bitmap of which brackets we are inside while testing; this ! handles up to substring 31; after that we just have to take ! the less precise approach ! backref_map the back reference bitmap ! ! Returns: TRUE or FALSE ! */ ! ! static BOOL ! is_startline(const uschar * code, unsigned int bracket_map, ! unsigned int backref_map) ! { ! do { ! const uschar *scode = first_significant_code(code + 1 + LINK_SIZE, NULL, 0); ! register int op = *scode; ! /* Capturing brackets */ ! if (op > OP_BRA) { ! int new_map; ! op -= OP_BRA; ! if (op > EXTRACT_BASIC_MAX) ! op = GET2(scode, 2 + LINK_SIZE); ! new_map = bracket_map | ((op < 32) ? (1 << op) : 1); ! if (!is_startline(scode, new_map, backref_map)) ! return FALSE; ! } ! /* Other brackets */ ! else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) { ! if (!is_startline(scode, bracket_map, backref_map)) ! return FALSE; ! } ! /* .* is not anchored unless DOTALL is set and it isn't in brackets that ! may be referenced. */ ! else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) { ! if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) ! return FALSE; ! } ! /* Check for explicit circumflex */ ! else if (op != OP_CIRC) ! return FALSE; ! code += GET(code, 1); ! } ! while (*code == OP_ALT); /* Loop for each alternative */ ! return TRUE; ! } ! /************************************************* ! * Check for asserted fixed first char * ! *************************************************/ ! /* During compilation, the "first char" settings from forward assertions are ! discarded, because they can cause conflicts with actual literals that follow. ! However, if we end up without a first char setting for an unanchored pattern, ! it is worth scanning the regex to see if there is an initial asserted first ! char. If all branches start with the same asserted char, or with a bracket all ! of whose alternatives start with the same asserted char (recurse ad lib), then ! we return that char, otherwise -1. ! Arguments: ! code points to start of expression (the bracket) ! options pointer to the options (used to check casing changes) ! inassert TRUE if in an assertion ! Returns: -1 or the fixed first char ! */ ! static int ! find_firstassertedchar(const uschar * code, int *options, BOOL inassert) ! { ! register int c = -1; ! do { ! int d; ! const uschar *scode = ! first_significant_code(code + 1 + LINK_SIZE, options, PCRE_CASELESS); ! register int op = *scode; ! if (op >= OP_BRA) ! op = OP_BRA; ! switch (op) { ! default: ! return -1; ! case OP_BRA: ! case OP_ASSERT: ! case OP_ONCE: ! case OP_COND: ! if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0) ! return -1; ! if (c < 0) ! c = d; ! else if (c != d) ! return -1; ! break; ! case OP_EXACT: /* Fall through */ ! scode++; ! case OP_CHARS: /* Fall through */ ! scode++; ! case OP_PLUS: ! case OP_MINPLUS: ! if (!inassert) ! return -1; ! if (c < 0) { ! c = scode[1]; ! if ((*options & PCRE_CASELESS) != 0) ! c |= REQ_CASELESS; ! } else if (c != scode[1]) ! return -1; ! break; ! } ! code += GET(code, 1); ! } ! while (*code == OP_ALT); ! return c; ! } ! /************************************************* ! * Compile a Regular Expression * ! *************************************************/ ! /* This function takes a string and returns a pointer to a block of store ! holding a compiled version of the expression. ! Arguments: ! pattern the regular expression ! options various option bits ! errorptr pointer to pointer to error text ! erroroffset ptr offset in pattern where error was detected ! tables pointer to character tables or NULL ! Returns: pointer to compiled data block, or NULL on error, ! with errorptr and erroroffset set ! */ ! pcre * ! pcre_compile(const char *pattern, int options, const char **errorptr, ! int *erroroffset, const unsigned char *tables) ! { ! real_pcre *re; ! int length = 1 + LINK_SIZE; /* For initial BRA plus length */ ! int runlength; ! int c, firstbyte, reqbyte; ! int bracount = 0; ! int branch_extra = 0; ! int branch_newextra; ! int item_count = -1; ! int name_count = 0; ! int max_name_size = 0; ! BOOL inescq = FALSE; ! unsigned int brastackptr = 0; ! size_t size; ! uschar *code; ! const uschar *codestart; ! const uschar *ptr; ! compile_data compile_block; ! int brastack[BRASTACK_SIZE]; ! uschar bralenstack[BRASTACK_SIZE]; ! /* We can't pass back an error message if errorptr is NULL; I guess the best we ! can do is just return NULL. */ ! if (errorptr == NULL) ! return NULL; ! *errorptr = NULL; ! /* However, we can give a message for this error */ ! if (erroroffset == NULL) { ! *errorptr = ERR16; ! return NULL; ! } ! *erroroffset = 0; ! /* Can't support UTF8 unless PCRE has been compiled to include the code. */ ! if ((options & PCRE_UTF8) != 0) { ! *errorptr = ERR32; ! return NULL; ! } ! if ((options & ~PUBLIC_OPTIONS) != 0) { ! *errorptr = ERR17; ! return NULL; ! } ! ! /* Set up pointers to the individual character tables */ ! ! if (tables == NULL) ! tables = pcre_default_tables; ! compile_block.lcc = tables + lcc_offset; ! compile_block.fcc = tables + fcc_offset; ! compile_block.cbits = tables + cbits_offset; ! compile_block.ctypes = tables + ctypes_offset; ! /* Maximum back reference and backref bitmap. This is updated for numeric ! references during the first pass, but for named references during the actual ! compile pass. The bitmap records up to 31 back references to help in deciding ! whether (.*) can be treated as anchored or not. */ ! compile_block.top_backref = 0; ! compile_block.backref_map = 0; ! /* Reflect pattern for debugging output */ ! DPRINTF(("------------------------------------------------------------------\n")); ! DPRINTF(("%s\n", pattern)); ! /* The first thing to do is to make a pass over the pattern to compute the ! amount of store required to hold the compiled code. This does not have to be ! perfect as long as errors are overestimates. At the same time we can detect any ! flag settings right at the start, and extract them. Make an attempt to correct ! for any counted white space if an "extended" flag setting appears late in the ! pattern. We can't be so clever for #-comments. */ ! ptr = (const uschar *) (pattern - 1); ! while ((c = *(++ptr)) != 0) { ! int min, max; ! int class_optcount; ! int bracket_length; ! int duplength; ! /* If we are inside a \Q...\E sequence, all chars are literal */ ! if (inescq) ! goto NORMAL_CHAR; ! /* Otherwise, first check for ignored whitespace and comments */ ! if ((options & PCRE_EXTENDED) != 0) { ! if ((compile_block.ctypes[c] & ctype_space) != 0) ! continue; ! if (c == '#') { ! /* The space before the ; is to avoid a warning on a silly compiler ! on the Macintosh. */ ! while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! if (c == 0) ! break; ! continue; ! } ! } ! item_count++; /* Is zero for the first non-comment item */ ! switch (c) { ! /* A backslashed item may be an escaped "normal" character or a ! character type. For a "normal" character, put the pointers and ! character back so that tests for whitespace etc. in the input ! are done correctly. */ ! case '\\': ! { ! const uschar *save_ptr = ptr; ! c = ! check_escape(&ptr, errorptr, bracount, options, FALSE, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if (c >= 0) { ! ptr = save_ptr; ! c = '\\'; ! goto NORMAL_CHAR; ! } ! } ! /* If \Q, enter "literal" mode */ ! if (-c == ESC_Q) { ! inescq = TRUE; ! continue; ! } ! /* Other escapes need one byte, and are of length one for repeats */ ! length++; ! /* A back reference needs an additional 2 bytes, plus either one or 5 ! bytes for a repeat. We also need to keep the value of the highest ! back reference. */ ! if (c <= -ESC_REF) { ! int refnum = -c - ESC_REF; ! compile_block.backref_map |= (refnum < 32) ? (1 << refnum) : 1; ! if (refnum > compile_block.top_backref) ! compile_block.top_backref = refnum; ! length += 2; /* For single back reference */ ! if (ptr[1] == '{' && is_counted_repeat(ptr + 2, &compile_block)) { ! ptr = ! read_repeat_counts(ptr + 2, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) ! length++; ! else ! length += 5; ! if (ptr[1] == '?') ! ptr++; ! } ! } ! continue; ! case '^': /* Single-byte metacharacters */ ! case '.': ! case '$': ! length++; ! continue; ! case '*': /* These repeats won't be after brackets; */ ! case '+': /* those are handled separately */ ! case '?': ! length++; ! goto POSESSIVE; /* A few lines below */ ! /* This covers the cases of braced repeats after a single char, metachar, ! class, or back reference. */ ! case '{': ! if (!is_counted_repeat(ptr + 1, &compile_block)) ! goto NORMAL_CHAR; ! ptr = read_repeat_counts(ptr + 1, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! /* These special cases just insert one extra opcode */ ! if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) ! length++; ! /* These cases might insert additional copies of a preceding character. */ ! else { ! /* Not UTF-8 mode: all characters are one byte */ ! { ! if (min != 1) { ! length--; /* Uncount the original char or metachar */ ! if (min > 0) ! length += 4; ! } ! length += (max > 0) ? 4 : 2; ! } ! } ! if (ptr[1] == '?') ! ptr++; /* Needs no extra length */ ! POSESSIVE: /* Test for possessive quantifier */ ! if (ptr[1] == '+') { ! ptr++; ! length += 2 + 2 * LINK_SIZE; /* Allow for atomic brackets */ ! } ! continue; ! /* An alternation contains an offset to the next branch or ket. If any ims ! options changed in the previous branch(es), and/or if we are in a ! lookbehind assertion, extra space will be needed at the start of the ! branch. This is handled by branch_extra. */ ! case '|': ! length += 1 + LINK_SIZE + branch_extra; ! continue; ! /* A character class uses 33 characters provided that all the character ! values are less than 256. Otherwise, it uses a bit map for low valued ! characters, and individual items for others. Don't worry about character ! types that aren't allowed in classes - they'll get picked up during the ! compile. A character class that contains only one single-byte character ! uses 2 or 3 bytes, depending on whether it is negated or not. Notice this ! where we can. (In UTF-8 mode we can do this only for chars < 128.) */ ! case '[': ! class_optcount = 0; ! if (*(++ptr) == '^') ! ptr++; ! /* Written as a "do" so that an initial ']' is taken as data */ ! if (*ptr != 0) ! do { ! /* Inside \Q...\E everything is literal except \E */ ! if (inescq) { ! if (*ptr != '\\' || ptr[1] != 'E') ! goto NON_SPECIAL_CHARACTER; ! inescq = FALSE; ! ptr += 1; ! continue; ! } ! /* Outside \Q...\E, check for escapes */ ! if (*ptr == '\\') { ! int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! /* \b is backspace inside a class */ ! if (-ch == ESC_b) ! ch = '\b'; ! /* \Q enters quoting mode */ ! if (-ch == ESC_Q) { ! inescq = TRUE; ! continue; } ! /* Handle escapes that turn into characters */ ! if (ch >= 0) { ! class_optcount++; /* for possible optimization */ ! } else ! class_optcount = 10; /* \d, \s etc; make sure > 1 */ ! } ! /* Check the syntax for POSIX stuff. The bits we actually handle are ! checked during the real compile phase. */ ! else if (*ptr == '[' && check_posix_syntax(ptr, &ptr, &compile_block)) { ! ptr++; ! class_optcount = 10; /* Make sure > 1 */ } ! /* Anything else just increments the possible optimization count. If ! there are wide characters, we are going to have to use an XCLASS. */ ! else { ! NON_SPECIAL_CHARACTER: ! class_optcount++; ! } ! } ! while (*(++ptr) != 0 && (inescq || *ptr != ']')); /* Concludes "do" above */ ! if (*ptr == 0) { /* Missing terminating ']' */ ! *errorptr = ERR6; ! goto PCRE_ERROR_RETURN; ! } ! /* We can optimize when there was only one optimizable character. Repeats ! for positive and negated single one-byte chars are handled by the general ! code. Here, we handle repeats for the class opcodes. */ ! if (class_optcount == 1) ! length += 3; ! else { ! length += 33; + /* A repeat needs either 1 or 5 bytes. */ + if (*ptr != 0 && ptr[1] == '{' + && is_counted_repeat(ptr + 2, &compile_block)) { + ptr = + read_repeat_counts(ptr + 2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) + goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || (min == 1 && max == -1)) + length++; + else + length += 5; + if (ptr[1] == '?') + ptr++; + } + } + continue; ! /* Brackets may be genuine groups or special things */ ! case '(': ! branch_newextra = 0; ! bracket_length = 1 + LINK_SIZE; ! /* Handle special forms of bracket, which all start (? */ ! if (ptr[1] == '?') { ! int set, unset; ! int *optset; ! switch (c = ptr[2]) { ! /* Skip over comments entirely */ ! case '#': ! ptr += 3; ! while (*ptr != 0 && *ptr != ')') ! ptr++; ! if (*ptr == 0) { ! *errorptr = ERR18; ! goto PCRE_ERROR_RETURN; ! } ! continue; ! /* Non-referencing groups and lookaheads just move the pointer on, and ! then behave like a non-special bracket, except that they don't increment ! the count of extracting brackets. Ditto for the "once only" bracket, ! which is in Perl from version 5.005. */ ! case ':': ! case '=': ! case '!': ! case '>': ! ptr += 2; ! break; ! /* (?R) specifies a recursive call to the regex, which is an extension ! to provide the facility which can be obtained by (?p{perl-code}) in ! Perl 5.6. In Perl 5.8 this has become (??{perl-code}). ! ! From PCRE 4.00, items such as (?3) specify subroutine-like "calls" to ! the appropriate numbered brackets. This includes both recursive and ! non-recursive calls. (?R) is now synonymous with (?0). */ ! case 'R': ! ptr++; ! case '0': ! case '1': ! case '2': ! case '3': ! case '4': ! case '5': ! case '6': ! case '7': ! case '8': ! case '9': ! ptr += 2; ! if (c != 'R') ! while ((compile_block.ctypes[*(++ptr)] & ctype_digit) != 0) ; ! if (*ptr != ')') { ! *errorptr = ERR29; ! goto PCRE_ERROR_RETURN; ! } ! length += 1 + LINK_SIZE; ! /* If this item is quantified, it will get wrapped inside brackets so ! as to use the code for quantified brackets. We jump down and use the ! code that handles this for real brackets. */ ! ! if (ptr[1] == '+' || ptr[1] == '*' || ptr[1] == '?' || ptr[1] == '{') { ! length += 2 + 2 * LINK_SIZE; /* to make bracketed */ ! duplength = 5 + 3 * LINK_SIZE; ! goto HANDLE_QUANTIFIED_BRACKETS; ! } ! continue; ! /* (?C) is an extension which provides "callout" - to provide a bit of ! the functionality of the Perl (?{...}) feature. An optional number may ! follow (default is zero). */ + case 'C': + ptr += 2; + while ((compile_block.ctypes[*(++ptr)] & ctype_digit) != 0) ; + if (*ptr != ')') { + *errorptr = ERR39; + goto PCRE_ERROR_RETURN; + } + length += 2; + continue; + /* Named subpatterns are an extension copied from Python */ ! case 'P': ! ptr += 3; ! if (*ptr == '<') { ! const uschar *p; /* Don't amalgamate; some compilers */ ! p = ++ptr; /* grumble at autoincrement in declaration */ ! while ((compile_block.ctypes[*ptr] & ctype_word) != 0) ! ptr++; ! if (*ptr != '>') { ! *errorptr = ERR42; ! goto PCRE_ERROR_RETURN; ! } ! name_count++; ! if (ptr - p > max_name_size) ! max_name_size = (ptr - p); ! break; ! } ! if (*ptr == '=' || *ptr == '>') { ! while ((compile_block.ctypes[*(++ptr)] & ctype_word) != 0) ; ! if (*ptr != ')') { ! *errorptr = ERR42; ! goto PCRE_ERROR_RETURN; ! } ! break; ! } ! /* Unknown character after (?P */ ! *errorptr = ERR41; ! goto PCRE_ERROR_RETURN; ! /* Lookbehinds are in Perl from version 5.005 */ ! case '<': ! ptr += 3; ! if (*ptr == '=' || *ptr == '!') { ! branch_newextra = 1 + LINK_SIZE; ! length += 1 + LINK_SIZE; /* For the first branch */ ! break; ! } ! *errorptr = ERR24; ! goto PCRE_ERROR_RETURN; ! /* Conditionals are in Perl from version 5.005. The bracket must either ! be followed by a number (for bracket reference) or by an assertion ! group, or (a PCRE extension) by 'R' for a recursion test. */ ! case '(': ! if (ptr[3] == 'R' && ptr[4] == ')') { ! ptr += 4; ! length += 3; ! } else if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0) { ! ptr += 4; ! length += 3; ! while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) ! ptr++; ! if (*ptr != ')') { ! *errorptr = ERR26; ! goto PCRE_ERROR_RETURN; ! } ! } else { /* An assertion must follow */ ! ptr++; /* Can treat like ':' as far as spacing is concerned */ ! if (ptr[2] != '?' || ! (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<')) { ! ptr += 2; /* To get right offset in message */ ! *errorptr = ERR28; ! goto PCRE_ERROR_RETURN; ! } ! } ! break; ! /* Else loop checking valid options until ) is met. Anything else is an ! error. If we are without any brackets, i.e. at top level, the settings ! act as if specified in the options, so massage the options immediately. ! This is for backward compatibility with Perl 5.004. */ ! default: ! set = unset = 0; ! optset = &set; ! ptr += 2; ! for (;; ptr++) { ! c = *ptr; ! switch (c) { ! case 'i': ! *optset |= PCRE_CASELESS; ! continue; ! case 'm': ! *optset |= PCRE_MULTILINE; ! continue; ! case 's': ! *optset |= PCRE_DOTALL; ! continue; ! case 'x': ! *optset |= PCRE_EXTENDED; ! continue; ! case 'X': ! *optset |= PCRE_EXTRA; ! continue; ! case 'U': ! *optset |= PCRE_UNGREEDY; ! continue; ! case '-': ! optset = &unset; ! continue; ! /* A termination by ')' indicates an options-setting-only item; if ! this is at the very start of the pattern (indicated by item_count ! being zero), we use it to set the global options. This is helpful ! when analyzing the pattern for first characters, etc. Otherwise ! nothing is done here and it is handled during the compiling ! process. ! ! [Historical note: Up to Perl 5.8, options settings at top level ! were always global settings, wherever they appeared in the pattern. ! That is, they were equivalent to an external setting. From 5.8 ! onwards, they apply only to what follows (which is what you might ! expect).] */ ! case ')': ! if (item_count == 0) { ! options = (options | set) & (~unset); ! set = unset = 0; /* To save length */ ! item_count--; /* To allow for several */ ! } ! /* Fall through */ ! /* A termination by ':' indicates the start of a nested group with ! the given options set. This is again handled at compile time, but ! we must allow for compiled space if any of the ims options are ! set. We also have to allow for resetting space at the end of ! the group, which is why 4 is added to the length and not just 2. ! If there are several changes of options within the same group, this ! will lead to an over-estimate on the length, but this shouldn't ! matter very much. We also have to allow for resetting options at ! the start of any alternations, which we do by setting ! branch_newextra to 2. Finally, we record whether the case-dependent ! flag ever changes within the regex. This is used by the "required ! character" code. */ ! case ':': ! if (((set | unset) & PCRE_IMS) != 0) { ! length += 4; ! branch_newextra = 2; ! if (((set | unset) & PCRE_CASELESS) != 0) ! options |= PCRE_ICHANGED; ! } ! goto END_OPTIONS; ! /* Unrecognized option character */ ! default: ! *errorptr = ERR12; ! goto PCRE_ERROR_RETURN; ! } ! } ! /* If we hit a closing bracket, that's it - this is a freestanding ! option-setting. We need to ensure that branch_extra is updated if ! necessary. The only values branch_newextra can have here are 0 or 2. ! If the value is 2, then branch_extra must either be 2 or 5, depending ! on whether this is a lookbehind group or not. */ ! ! END_OPTIONS: ! if (c == ')') { ! if (branch_newextra == 2 && ! (branch_extra == 0 || branch_extra == 1 + LINK_SIZE)) ! branch_extra += branch_newextra; ! continue; ! } ! /* If options were terminated by ':' control comes here. Fall through ! to handle the group below. */ ! } } ! /* Extracting brackets must be counted so we can process escapes in a ! Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to ! need an additional 3 bytes of store per extracting bracket. However, if ! PCRE_NO_AUTO)CAPTURE is set, unadorned brackets become non-capturing, so we ! must leave the count alone (it will aways be zero). */ ! else if ((options & PCRE_NO_AUTO_CAPTURE) == 0) { ! bracount++; ! if (bracount > EXTRACT_BASIC_MAX) ! bracket_length += 3; } ! /* Save length for computing whole length at end if there's a repeat that ! requires duplication of the group. Also save the current value of ! branch_extra, and start the new group with the new value. If non-zero, this ! will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ ! if (brastackptr >= sizeof(brastack) / sizeof(int)) { ! *errorptr = ERR19; ! goto PCRE_ERROR_RETURN; } ! bralenstack[brastackptr] = branch_extra; ! branch_extra = branch_newextra; ! brastack[brastackptr++] = length; ! length += bracket_length; ! continue; ! /* Handle ket. Look for subsequent max/min; for certain sets of values we ! have to replicate this bracket up to that many times. If brastackptr is ! 0 this is an unmatched bracket which will generate an error, but take care ! not to try to access brastack[-1] when computing the length and restoring ! the branch_extra value. */ ! case ')': ! length += 1 + LINK_SIZE; ! if (brastackptr > 0) { ! duplength = length - brastack[--brastackptr]; ! branch_extra = bralenstack[brastackptr]; ! } else ! duplength = 0; ! /* The following code is also used when a recursion such as (?3) is ! followed by a quantifier, because in that case, it has to be wrapped inside ! brackets so that the quantifier works. The value of duplength must be ! set before arrival. */ ! HANDLE_QUANTIFIED_BRACKETS: ! /* Leave ptr at the final char; for read_repeat_counts this happens ! automatically; for the others we need an increment. */ ! if ((c = ptr[1]) == '{' && is_counted_repeat(ptr + 2, &compile_block)) { ! ptr = read_repeat_counts(ptr + 2, &min, &max, errorptr, &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! } else if (c == '*') { ! min = 0; ! max = -1; ! ptr++; ! } else if (c == '+') { ! min = 1; ! max = -1; ! ptr++; ! } else if (c == '?') { ! min = 0; ! max = 1; ! ptr++; ! } else { ! min = 1; ! max = 1; } ! /* If the minimum is zero, we have to allow for an OP_BRAZERO before the ! group, and if the maximum is greater than zero, we have to replicate ! maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting ! bracket set. */ ! if (min == 0) { ! length++; ! if (max > 0) ! length += (max - 1) * (duplength + 3 + 2 * LINK_SIZE); ! } ! /* When the minimum is greater than zero, we have to replicate up to ! minval-1 times, with no additions required in the copies. Then, if there ! is a limited maximum we have to replicate up to maxval-1 times allowing ! for a BRAZERO item before each optional copy and nesting brackets for all ! but one of the optional copies. */ ! else { ! length += (min - 1) * duplength; ! if (max > min) /* Need this test as max=-1 means no limit */ ! length += (max - min) * (duplength + 3 + 2 * LINK_SIZE) ! - (2 + 2 * LINK_SIZE); } ! /* Allow space for once brackets for "possessive quantifier" */ ! if (ptr[1] == '+') { ! ptr++; ! length += 2 + 2 * LINK_SIZE; ! } continue; ! /* Non-special character. For a run of such characters the length required ! is the number of characters + 2, except that the maximum run length is ! MAXLIT. We won't get a skipped space or a non-data escape or the start of a ! # comment as the first character, so the length can't be zero. */ ! NORMAL_CHAR: ! default: ! length += 2; ! runlength = 0; ! do { ! /* If in a \Q...\E sequence, check for end; otherwise it's a literal */ ! if (inescq) { ! if (c == '\\' && ptr[1] == 'E') { ! inescq = FALSE; ! ptr++; ! } else ! runlength++; ! continue; ! } ! /* Skip whitespace and comments for /x */ ! if ((options & PCRE_EXTENDED) != 0) { ! if ((compile_block.ctypes[c] & ctype_space) != 0) ! continue; ! if (c == '#') { ! /* The space before the ; is to avoid a warning on a silly compiler ! on the Macintosh. */ ! while ((c = *(++ptr)) != 0 && c != NEWLINE) ; ! continue; } } ! /* Backslash may introduce a data char or a metacharacter; stop the ! string before the latter. */ ! if (c == '\\') { ! const uschar *saveptr = ptr; ! c = check_escape(&ptr, errorptr, bracount, options, FALSE, ! &compile_block); ! if (*errorptr != NULL) ! goto PCRE_ERROR_RETURN; ! if (c < 0) { ! ptr = saveptr; ! break; ! } ! /* In UTF-8 mode, add on the number of additional bytes needed to ! encode this character, and save the total length in case this is a ! final char that is repeated. */ ! } ! /* Ordinary character or single-char escape */ ! runlength++; ! } ! /* This "while" is the end of the "do" above. */ ! while (runlength < MAXLIT && ! (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); ! /* If we hit a meta-character, back off to point to it */ ! if (runlength < MAXLIT) ! ptr--; ! /* If the last char in the string is a UTF-8 multibyte character, we must ! set lastcharlength correctly. If it was specified as an escape, this will ! already have been done above. However, we also have to support in-line ! UTF-8 characters, so check backwards from where we are. */ ! length += runlength; ! continue; ! } ! } ! length += 2 + LINK_SIZE; /* For final KET and END */ ! if (length > MAX_PATTERN_SIZE) { ! *errorptr = ERR20; ! return NULL; ! } ! /* Compute the size of data block needed and get it, either from malloc or ! externally provided function. */ ! size = length + sizeof(real_pcre) + name_count * (max_name_size + 3); ! re = (real_pcre *) (malloc) (size); ! if (re == NULL) { ! *errorptr = ERR21; ! return NULL; ! } ! /* Put in the magic number, and save the size, options, and table pointer */ ! re->magic_number = MAGIC_NUMBER; ! re->size = size; ! re->options = options; ! re->tables = tables; ! re->name_entry_size = max_name_size + 3; ! re->name_count = name_count; ! /* The starting points of the name/number translation table and of the code are ! passed around in the compile data block. */ ! compile_block.names_found = 0; ! compile_block.name_entry_size = max_name_size + 3; ! compile_block.name_table = (uschar *) re + sizeof(real_pcre); ! codestart = compile_block.name_table + re->name_entry_size * re->name_count; ! compile_block.start_code = codestart; ! compile_block.req_varyopt = 0; ! /* Set up a starting, non-extracting bracket, then compile the expression. On ! error, *errorptr will be set non-NULL, so we don't need to look at the result ! of the function here. */ ! ptr = (const uschar *) pattern; ! code = (uschar *) codestart; ! *code = OP_BRA; ! bracount = 0; ! (void) compile_regex(options, options & PCRE_IMS, &bracount, &code, &ptr, ! errorptr, FALSE, 0, &firstbyte, &reqbyte, NULL, ! &compile_block); ! re->top_bracket = bracount; ! re->top_backref = compile_block.top_backref; ! /* If not reached end of pattern on success, there's an excess bracket. */ ! if (*errorptr == NULL && *ptr != 0) ! *errorptr = ERR22; ! /* Fill in the terminating state and check for disastrous overflow, but ! if debugging, leave the test till after things are printed out. */ ! *code++ = OP_END; ! if (code - codestart > length) ! *errorptr = ERR23; ! /* Give an error if there's back reference to a non-existent capturing ! subpattern. */ ! if (re->top_backref > re->top_bracket) ! *errorptr = ERR15; ! /* Failed to compile, or error while post-processing */ ! if (*errorptr != NULL) { ! (free) (re); ! PCRE_ERROR_RETURN: ! *erroroffset = ptr - (const uschar *) pattern; ! return NULL; ! } ! /* If the anchored option was not passed, set the flag if we can determine that ! the pattern is anchored by virtue of ^ characters or \A or anything else (such ! as starting with .* when DOTALL is set). ! Otherwise, if we know what the first character has to be, save it, because that ! speeds up unanchored matches no end. If not, see if we can set the ! PCRE_STARTLINE flag. This is helpful for multiline matches when all branches ! start with ^. and also when all branches start with .* for non-DOTALL matches. ! */ ! if ((options & PCRE_ANCHORED) == 0) { ! int temp_options = options; ! if (is_anchored(codestart, &temp_options, 0, compile_block.backref_map)) ! re->options |= PCRE_ANCHORED; ! else { ! if (firstbyte < 0) ! firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE); ! if (firstbyte >= 0) { /* Remove caseless flag for non-caseable chars */ ! int ch = firstbyte & 255; ! re->first_byte = ((firstbyte & REQ_CASELESS) != 0 && ! compile_block.fcc[ch] == ch) ? ch : firstbyte; ! re->options |= PCRE_FIRSTSET; ! } else if (is_startline(codestart, 0, compile_block.backref_map)) ! re->options |= PCRE_STARTLINE; ! } ! } ! /* For an anchored pattern, we use the "required byte" only if it follows a ! variable length item in the regex. Remove the caseless flag for non-caseable ! chars. */ ! ! if (reqbyte >= 0 && ! ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0)) { ! int ch = reqbyte & 255; ! re->req_byte = ((reqbyte & REQ_CASELESS) != 0 && ! compile_block.fcc[ch] == ! ch) ? (reqbyte & ~REQ_CASELESS) : reqbyte; ! re->options |= PCRE_REQCHSET; ! } ! return (pcre *) re; ! } ! /************************************************* ! * Match a back-reference * ! *************************************************/ ! /* If a back reference hasn't been set, the length that is passed is greater ! than the number of characters left in the string, so the match fails. ! Arguments: ! offset index into the offset vector ! eptr points into the subject ! length length to be matched ! md points to match data block ! ims the ims flags ! Returns: TRUE if matched ! */ ! static BOOL ! match_ref(int offset, register const uschar * eptr, int length, match_data * md, ! unsigned long int ims) ! { ! const uschar *p = md->start_subject + md->offset_vector[offset]; ! /* Always fail if not enough characters left */ ! if (length > md->end_subject - eptr) ! return FALSE; ! /* Separate the caselesss case for speed */ ! ! if ((ims & PCRE_CASELESS) != 0) { ! while (length-- > 0) ! if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; ! } else { ! while (length-- > 0) ! if (*p++ != *eptr++) ! return FALSE; ! } ! return TRUE; ! } ! /************************************************* ! * Match from current position * ! *************************************************/ ! /* On entry ecode points to the first opcode, and eptr to the first character ! in the subject string, while eptrb holds the value of eptr at the start of the ! last bracketed group - used for breaking infinite loops matching zero-length ! strings. This function is called recursively in many circumstances. Whenever it ! returns a negative (error) response, the outer incarnation must also return the ! same response. ! ! Performance note: It might be tempting to extract commonly used fields from the ! md structure (e.g. utf8, end_subject) into individual variables to improve ! performance. Tests using gcc on a SPARC disproved this; in the first case, it ! made performance worse. ! Arguments: ! eptr pointer in subject ! ecode position in code ! offset_top current top pointer ! md pointer to "static" info for the match ! ims current /i, /m, and /s options ! eptrb pointer to chain of blocks containing eptr at start of ! brackets - for testing for empty matches ! flags can contain ! match_condassert - this is an assertion condition ! match_isgroup - this is the start of a bracketed group ! Returns: MATCH_MATCH if matched ) these values are >= 0 ! MATCH_NOMATCH if failed to match ) ! a negative PCRE_ERROR_xxx value if aborted by an error condition ! (e.g. stopped by recursion limit) ! */ ! static int ! match(register const uschar * eptr, register const uschar * ecode, ! int offset_top, match_data * md, unsigned long int ims, eptrblock * eptrb, ! int flags) ! { ! unsigned long int original_ims = ims; /* Save for resetting on ')' */ ! register int rrc; ! eptrblock newptrb; ! if (md->match_call_count++ >= md->match_limit) ! return PCRE_ERROR_MATCHLIMIT; ! /* At the start of a bracketed group, add the current subject pointer to the ! stack of such pointers, to be re-instated at the end of the group when we hit ! the closing ket. When match() is called in other circumstances, we don't add to ! the stack. */ ! if ((flags & match_isgroup) != 0) { ! newptrb.prev = eptrb; ! newptrb.saved_eptr = eptr; ! eptrb = &newptrb; ! } ! /* Now start processing the operations. */ ! for (;;) { ! int op = (int) *ecode; ! int min, max, ctype; ! register int i; ! register int c; ! BOOL minimize = FALSE; ! ! /* Opening capturing bracket. If there is space in the offset vector, save ! the current subject position in the working slot at the top of the vector. We ! mustn't change the current values of the data slot, because they may be set ! from a previous iteration of this group, and be referred to by a reference ! inside the group. ! If the bracket fails to match, we need to restore this value and also the ! values of the final offsets, in case they were set by a previous iteration of ! the same bracket. ! If there isn't enough space in the offset vector, treat this as if it were a ! non-capturing bracket. Don't worry about setting the flag for the error case ! here; that is handled in the code for KET. */ ! if (op > OP_BRA) { ! int offset; ! int number = op - OP_BRA; ! /* For extended extraction brackets (large number), we have to fish out the ! number from a dummy opcode at the start. */ ! if (number > EXTRACT_BASIC_MAX) ! number = GET2(ecode, 2 + LINK_SIZE); ! offset = number << 1; ! if (offset < md->offset_max) { ! int save_offset1 = md->offset_vector[offset]; ! int save_offset2 = md->offset_vector[offset + 1]; ! int save_offset3 = md->offset_vector[md->offset_end - number]; ! int save_capture_last = md->capture_last; ! DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, ! save_offset3)); ! md->offset_vector[md->offset_end - number] = eptr - md->start_subject; ! do { ! if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, ! eptrb, match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! md->capture_last = save_capture_last; ! ecode += GET(ecode, 1); } + while (*ecode == OP_ALT); ! DPRINTF(("bracket %d failed\n", number)); ! md->offset_vector[offset] = save_offset1; ! md->offset_vector[offset + 1] = save_offset2; ! md->offset_vector[md->offset_end - number] = save_offset3; ! return MATCH_NOMATCH; ! } ! /* Insufficient room for saving captured contents */ ! else ! op = OP_BRA; ! } ! /* Other types of node can be handled by a switch */ ! switch (op) { ! case OP_BRA: /* Non-capturing bracket: optimized */ ! DPRINTF(("start bracket 0\n")); ! do { ! if ((rrc = ! match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! ecode += GET(ecode, 1); ! } ! while (*ecode == OP_ALT); ! DPRINTF(("bracket 0 failed\n")); ! return MATCH_NOMATCH; ! /* Conditional group: compilation checked that there are no more than ! two branches. If the condition is false, skipping the first branch takes us ! past the end if there is only one branch, but that's OK because that is ! exactly what going to the ket would do. */ ! case OP_COND: ! if (ecode[LINK_SIZE + 1] == OP_CREF) { /* Condition extract or recurse test */ ! int offset = GET2(ecode, LINK_SIZE + 2) << 1; /* Doubled ref number */ ! BOOL condition = (offset == CREF_RECURSE * 2) ? ! (md->recursive != NULL) : ! (offset < offset_top && md->offset_vector[offset] >= 0); ! return match(eptr, ecode + (condition ? ! (LINK_SIZE + 4) : (LINK_SIZE + 1 + ! GET(ecode, 1))), ! offset_top, md, ims, eptrb, match_isgroup); } + /* The condition is an assertion. Call match() to evaluate it - setting + the final argument TRUE causes it to stop at the end of an assertion. */ + else { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, + match_condassert | match_isgroup)) == MATCH_MATCH) { + ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2); + while (*ecode == OP_ALT) + ecode += GET(ecode, 1); + } else if (rrc != MATCH_NOMATCH) + return rrc; + else + ecode += GET(ecode, 1); + return match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, + match_isgroup); + } + /* Control never reaches here */ ! /* Skip over conditional reference or large extraction number data if ! encountered. */ ! case OP_CREF: ! case OP_BRANUMBER: ! ecode += 3; ! break; ! /* End of the pattern. If we are in a recursion, we should restore the ! offsets appropriately and continue from after the call. */ ! case OP_END: ! if (md->recursive != NULL && md->recursive->group_num == 0) { ! recursion_info *rec = md->recursive; ! DPRINTF(("Hit the end in a (?0) recursion\n")); ! md->recursive = rec->prev; ! memmove(md->offset_vector, rec->offset_save, ! rec->saved_max * sizeof(int)); ! md->start_match = rec->save_start; ! ims = original_ims; ! ecode = rec->after_call; ! break; ! } ! /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty ! string - backtracking will then try other alternatives, if any. */ ! if (md->notempty && eptr == md->start_match) ! return MATCH_NOMATCH; ! md->end_match_ptr = eptr; /* Record where we ended */ ! md->end_offset_top = offset_top; /* and how many extracts were taken */ ! return MATCH_MATCH; ! /* Change option settings */ ! case OP_OPT: ! ims = ecode[1]; ! ecode += 2; ! DPRINTF(("ims set to %02lx\n", ims)); ! break; ! /* Assertion brackets. Check the alternative branches in turn - the ! matching won't pass the KET for an assertion. If any one branch matches, ! the assertion is true. Lookbehind assertions have an OP_REVERSE item at the ! start of each branch to move the current point backwards, so the code at ! this level is identical to the lookahead case. */ ! case OP_ASSERT: ! case OP_ASSERTBACK: ! do { ! if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, ! match_isgroup)) == MATCH_MATCH) ! break; ! if (rrc != MATCH_NOMATCH) ! return rrc; ! ecode += GET(ecode, 1); ! } ! while (*ecode == OP_ALT); ! if (*ecode == OP_KET) ! return MATCH_NOMATCH; ! /* If checking an assertion for a condition, return MATCH_MATCH. */ ! if ((flags & match_condassert) != 0) ! return MATCH_MATCH; ! /* Continue from after the assertion, updating the offsets high water ! mark, since extracts may have been taken during the assertion. */ ! do ! ecode += GET(ecode, 1); ! while (*ecode == OP_ALT); ! ecode += 1 + LINK_SIZE; ! offset_top = md->end_offset_top; ! continue; ! /* Negative assertion: all branches must fail to match */ ! case OP_ASSERT_NOT: ! case OP_ASSERTBACK_NOT: ! do { ! if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, ! match_isgroup)) == MATCH_MATCH) ! return MATCH_NOMATCH; ! if (rrc != MATCH_NOMATCH) ! return rrc; ! ecode += GET(ecode, 1); } ! while (*ecode == OP_ALT); ! if ((flags & match_condassert) != 0) ! return MATCH_MATCH; ! ! ecode += 1 + LINK_SIZE; ! continue; ! ! /* Move the subject pointer back. This occurs only at the start of ! each branch of a lookbehind assertion. If we are too close to the start to ! move back, this match function fails. When working with UTF-8 we move ! back a number of characters, not bytes. */ ! case OP_REVERSE: ! eptr -= GET(ecode, 1); ! if (eptr < md->start_subject) ! return MATCH_NOMATCH; ! ecode += 1 + LINK_SIZE; ! break; ! /* The callout item calls an external function, if one is provided, passing ! details of the match so far. This is mainly for debugging, though the ! function is able to force a failure. */ ! ! case OP_CALLOUT: ! if (pcre_callout != NULL) { ! pcre_callout_block cb; ! cb.version = 0; /* Version 0 of the callout block */ ! cb.callout_number = ecode[1]; ! cb.offset_vector = md->offset_vector; ! cb.subject = (const char *) md->start_subject; ! cb.subject_length = md->end_subject - md->start_subject; ! cb.start_match = md->start_match - md->start_subject; ! cb.current_position = eptr - md->start_subject; ! cb.capture_top = offset_top / 2; ! cb.capture_last = md->capture_last; ! cb.callout_data = md->callout_data; ! if ((rrc = (*pcre_callout) (&cb)) > 0) ! return MATCH_NOMATCH; ! if (rrc < 0) ! return rrc; } + ecode += 2; break; ! /* Recursion either matches the current regex, or some subexpression. The ! offset data is the offset to the starting bracket from the start of the ! whole pattern. However, it is possible that a BRAZERO was inserted before ! this bracket after we took the offset - we just skip it if encountered. ! ! If there are any capturing brackets started but not finished, we have to ! save their starting points and reinstate them after the recursion. However, ! we don't know how many such there are (offset_top records the completed ! total) so we just have to save all the potential data. There may be up to ! 65535 such values, which is too large to put on the stack, but using malloc ! for small numbers seems expensive. As a compromise, the stack is used when ! there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc ! is used. A problem is what to do if the malloc fails ... there is no way of ! returning to the top level with an error. Save the top REC_STACK_SAVE_MAX ! values on the stack, and accept that the rest may be wrong. ! ! There are also other values that have to be saved. We use a chained ! sequence of blocks that actually live on the stack. Thanks to Robin Houston ! for the original version of this logic. */ ! case OP_RECURSE: ! { ! int stacksave[REC_STACK_SAVE_MAX]; ! recursion_info new_recursive; ! const uschar *callpat = md->start_code + GET(ecode, 1); ! if (*callpat == OP_BRAZERO) ! callpat++; ! new_recursive.group_num = *callpat - OP_BRA; ! /* For extended extraction brackets (large number), we have to fish out ! the number from a dummy opcode at the start. */ ! if (new_recursive.group_num > EXTRACT_BASIC_MAX) ! new_recursive.group_num = GET2(callpat, 2 + LINK_SIZE); ! /* Add to "recursing stack" */ ! new_recursive.prev = md->recursive; ! md->recursive = &new_recursive; ! /* Find where to continue from afterwards */ ! ecode += 1 + LINK_SIZE; ! new_recursive.after_call = ecode; ! /* Now save the offset data. */ ! ! new_recursive.saved_max = md->offset_end; ! if (new_recursive.saved_max <= REC_STACK_SAVE_MAX) ! new_recursive.offset_save = stacksave; ! else { ! new_recursive.offset_save = ! (int *) (malloc) (new_recursive.saved_max * sizeof(int)); ! if (new_recursive.offset_save == NULL) ! return PCRE_ERROR_NOMEMORY; } ! memcpy(new_recursive.offset_save, md->offset_vector, ! new_recursive.saved_max * sizeof(int)); ! new_recursive.save_start = md->start_match; ! md->start_match = eptr; ! /* OK, now we can do the recursion. For each top-level alternative we ! restore the offset and recursion data. */ ! ! DPRINTF(("Recursing into group %d\n", new_recursive.group_num)); ! do { ! if ((rrc = match(eptr, callpat + 1 + LINK_SIZE, offset_top, md, ims, ! eptrb, match_isgroup)) == MATCH_MATCH) { ! md->recursive = new_recursive.prev; ! if (new_recursive.offset_save != stacksave) ! (free) (new_recursive.offset_save); ! return MATCH_MATCH; ! } else if (rrc != MATCH_NOMATCH) ! return rrc; ! ! md->recursive = &new_recursive; ! memcpy(md->offset_vector, new_recursive.offset_save, ! new_recursive.saved_max * sizeof(int)); ! callpat += GET(callpat, 1); ! } ! while (*callpat == OP_ALT); ! ! DPRINTF(("Recursion didn't match\n")); ! md->recursive = new_recursive.prev; ! if (new_recursive.offset_save != stacksave) ! (free) (new_recursive.offset_save); ! return MATCH_NOMATCH; } ! /* Control never reaches here */ ! /* "Once" brackets are like assertion brackets except that after a match, ! the point in the subject string is not moved back. Thus there can never be ! a move back into the brackets. Friedl calls these "atomic" subpatterns. ! Check the alternative branches in turn - the matching won't pass the KET ! for this kind of subpattern. If any one branch matches, we carry on as at ! the end of a normal bracket, leaving the subject pointer. */ ! case OP_ONCE: ! { ! const uschar *prev = ecode; ! const uschar *saved_eptr = eptr; ! do { ! if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, ! eptrb, match_isgroup)) == MATCH_MATCH) ! break; ! if (rrc != MATCH_NOMATCH) ! return rrc; ! ecode += GET(ecode, 1); ! } ! while (*ecode == OP_ALT); ! /* If hit the end of the group (which could be repeated), fail */ ! if (*ecode != OP_ONCE && *ecode != OP_ALT) ! return MATCH_NOMATCH; ! /* Continue as from after the assertion, updating the offsets high water ! mark, since extracts may have been taken. */ ! do ! ecode += GET(ecode, 1); ! while (*ecode == OP_ALT); ! offset_top = md->end_offset_top; ! eptr = md->end_match_ptr; ! /* For a non-repeating ket, just continue at this level. This also ! happens for a repeating ket if no characters were matched in the group. ! This is the forcible breaking of infinite loops as implemented in Perl ! 5.005. If there is an options reset, it will get obeyed in the normal ! course of events. */ ! ! if (*ecode == OP_KET || eptr == saved_eptr) { ! ecode += 1 + LINK_SIZE; ! break; } ! /* The repeating kets try the rest of the pattern or restart from the ! preceding bracket, in the appropriate order. We need to reset any options ! that changed within the bracket before re-running it, so check the next ! opcode. */ ! if (ecode[1 + LINK_SIZE] == OP_OPT) { ! ims = (ims & ~PCRE_IMS) | ecode[4]; ! DPRINTF(("ims set to %02lx at group repeat\n", ims)); ! } ! ! if (*ecode == OP_KETRMIN) { ! if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, ! eptrb, 0)) != MATCH_NOMATCH) ! return rrc; ! if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! } else { /* OP_KETRMAX */ ! ! if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! if ((rrc = ! match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, ! 0)) != MATCH_NOMATCH) ! return rrc; } } ! return MATCH_NOMATCH; ! /* An alternation is the end of a branch; scan along to find the end of the ! bracketed group and go to there. */ ! ! case OP_ALT: ! do ! ecode += GET(ecode, 1); ! while (*ecode == OP_ALT); ! break; ! ! /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating ! that it may occur zero times. It may repeat infinitely, or not at all - ! i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper ! repeat limits are compiled as a number of copies, with the optional ones ! preceded by BRAZERO or BRAMINZERO. */ ! ! case OP_BRAZERO: ! { ! const uschar *next = ecode + 1; ! if ((rrc = match(eptr, next, offset_top, md, ims, eptrb, match_isgroup)) ! != MATCH_NOMATCH) ! return rrc; ! do ! next += GET(next, 1); ! while (*next == OP_ALT); ! ecode = next + 1 + LINK_SIZE; ! } ! break; ! case OP_BRAMINZERO: ! { ! const uschar *next = ecode + 1; ! do ! next += GET(next, 1); ! while (*next == OP_ALT); ! if ((rrc = match(eptr, next + 1 + LINK_SIZE, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! ecode++; ! } ! break; ! /* End of a group, repeated or non-repeating. If we are at the end of ! an assertion "group", stop matching and return MATCH_MATCH, but record the ! current high water mark for use by positive assertions. Do this also ! for the "once" (not-backup up) groups. */ ! case OP_KET: ! case OP_KETRMIN: ! case OP_KETRMAX: ! { ! const uschar *prev = ecode - GET(ecode, 1); ! const uschar *saved_eptr = eptrb->saved_eptr; ! eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */ ! if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || ! *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || ! *prev == OP_ONCE) { ! md->end_match_ptr = eptr; /* For ONCE */ ! md->end_offset_top = offset_top; ! return MATCH_MATCH; ! } ! /* In all other cases except a conditional group we have to check the ! group number back at the start and if necessary complete handling an ! extraction by setting the offsets and bumping the high water mark. */ ! if (*prev != OP_COND) { ! int offset; ! int number = *prev - OP_BRA; ! /* For extended extraction brackets (large number), we have to fish out ! the number from a dummy opcode at the start. */ ! if (number > EXTRACT_BASIC_MAX) ! number = GET2(prev, 2 + LINK_SIZE); ! offset = number << 1; ! /* Test for a numbered group. This includes groups called as a result ! of recursion. Note that whole-pattern recursion is coded as a recurse ! into group 0, so it won't be picked up here. Instead, we catch it when ! the OP_END is reached. */ ! if (number > 0) { ! md->capture_last = number; ! if (offset >= md->offset_max) ! md->offset_overflow = TRUE; ! else { ! md->offset_vector[offset] = ! md->offset_vector[md->offset_end - number]; ! md->offset_vector[offset + 1] = eptr - md->start_subject; ! if (offset_top <= offset) ! offset_top = offset + 2; ! } ! /* Handle a recursively called group. Restore the offsets ! appropriately and continue from after the call. */ ! if (md->recursive != NULL && md->recursive->group_num == number) { ! recursion_info *rec = md->recursive; ! DPRINTF(("Recursion (%d) succeeded - continuing\n", number)); ! md->recursive = rec->prev; ! md->start_match = rec->save_start; ! memcpy(md->offset_vector, rec->offset_save, ! rec->saved_max * sizeof(int)); ! ecode = rec->after_call; ! ims = original_ims; ! break; ! } ! } } ! /* Reset the value of the ims flags, in case they got changed during ! the group. */ ! ims = original_ims; ! DPRINTF(("ims reset to %02lx\n", ims)); ! /* For a non-repeating ket, just continue at this level. This also ! happens for a repeating ket if no characters were matched in the group. ! This is the forcible breaking of infinite loops as implemented in Perl ! 5.005. If there is an options reset, it will get obeyed in the normal ! course of events. */ ! if (*ecode == OP_KET || eptr == saved_eptr) { ! ecode += 1 + LINK_SIZE; ! break; ! } ! /* The repeating kets try the rest of the pattern or restart from the ! preceding bracket, in the appropriate order. */ ! if (*ecode == OP_KETRMIN) { ! if ((rrc = ! match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, ! 0)) != MATCH_NOMATCH) ! return rrc; ! if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! } else { /* OP_KETRMAX */ ! if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, ! match_isgroup)) != MATCH_NOMATCH) ! return rrc; ! if ((rrc = ! match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, ! 0)) != MATCH_NOMATCH) ! return rrc; ! } ! } ! return MATCH_NOMATCH; ! /* Start of subject unless notbol, or after internal newline if multiline */ ! case OP_CIRC: ! if (md->notbol && eptr == md->start_subject) ! return MATCH_NOMATCH; ! if ((ims & PCRE_MULTILINE) != 0) { ! if (eptr != md->start_subject && eptr[-1] != NEWLINE) ! return MATCH_NOMATCH; ! ecode++; ! break; ! } ! /* ... else fall through */ ! /* Start of subject assertion */ ! case OP_SOD: ! if (eptr != md->start_subject) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* Start of match assertion */ ! case OP_SOM: ! if (eptr != md->start_subject + md->start_offset) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* Assert before internal newline if multiline, or before a terminating ! newline unless endonly is set, else end of subject unless noteol is set. */ ! case OP_DOLL: ! if ((ims & PCRE_MULTILINE) != 0) { ! if (eptr < md->end_subject) { ! if (*eptr != NEWLINE) ! return MATCH_NOMATCH; ! } else { ! if (md->noteol) ! return MATCH_NOMATCH; ! } ! ecode++; ! break; ! } else { ! if (md->noteol) ! return MATCH_NOMATCH; ! if (!md->endonly) { ! if (eptr < md->end_subject - 1 || ! (eptr == md->end_subject - 1 && *eptr != NEWLINE)) ! return MATCH_NOMATCH; ! ecode++; break; + } + } + /* ... else fall through */ ! /* End of subject assertion (\z) */ ! case OP_EOD: ! if (eptr < md->end_subject) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* End of subject or ending \n assertion (\Z) */ ! case OP_EODN: ! if (eptr < md->end_subject - 1 || ! (eptr == md->end_subject - 1 && *eptr != NEWLINE)) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* Word boundary assertions */ ! case OP_NOT_WORD_BOUNDARY: ! case OP_WORD_BOUNDARY: ! { ! BOOL prev_is_word, cur_is_word; ! /* Find out if the previous and current characters are "word" characters. ! It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to ! be "non-word" characters. */ ! ! ! /* More streamlined when not in UTF-8 mode */ ! ! { ! prev_is_word = (eptr != md->start_subject) && ! ((md->ctypes[eptr[-1]] & ctype_word) != 0); ! cur_is_word = (eptr < md->end_subject) && ! ((md->ctypes[*eptr] & ctype_word) != 0); } ! ! /* Now see if the situation is what we want */ ! ! if ((*ecode++ == OP_WORD_BOUNDARY) ? ! cur_is_word == prev_is_word : cur_is_word != prev_is_word) ! return MATCH_NOMATCH; } ! break; ! /* Match a single character type; inline for speed */ ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject ! && *eptr == NEWLINE) ! return MATCH_NOMATCH; ! if (eptr++ >= md->end_subject) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* Match a single byte, even in UTF-8 mode. This opcode really does match ! any byte, even newline, independent of the setting of PCRE_DOTALL. */ ! case OP_ANYBYTE: ! if (eptr++ >= md->end_subject) ! return MATCH_NOMATCH; ! ecode++; ! break; + case OP_NOT_DIGIT: + if (eptr >= md->end_subject) + return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ((md->ctypes[c] & ctype_digit) != 0) + return MATCH_NOMATCH; + ecode++; + break; + case OP_DIGIT: + if (eptr >= md->end_subject) + return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ((md->ctypes[c] & ctype_digit) == 0) + return MATCH_NOMATCH; + ecode++; + break; + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject) + return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ((md->ctypes[c] & ctype_space) != 0) + return MATCH_NOMATCH; + ecode++; + break; ! case OP_WHITESPACE: ! if (eptr >= md->end_subject) ! return MATCH_NOMATCH; ! GETCHARINCTEST(c, eptr); ! if ((md->ctypes[c] & ctype_space) == 0) ! return MATCH_NOMATCH; ! ecode++; ! break; ! case OP_NOT_WORDCHAR: ! if (eptr >= md->end_subject) ! return MATCH_NOMATCH; ! GETCHARINCTEST(c, eptr); ! if ((md->ctypes[c] & ctype_word) != 0) ! return MATCH_NOMATCH; ! ecode++; ! break; ! case OP_WORDCHAR: ! if (eptr >= md->end_subject) ! return MATCH_NOMATCH; ! GETCHARINCTEST(c, eptr); ! if ((md->ctypes[c] & ctype_word) == 0) ! return MATCH_NOMATCH; ! ecode++; ! break; ! /* Match a back reference, possibly repeatedly. Look past the end of the ! item to see if there is repeat information following. The code is similar ! to that for character classes, but repeated for efficiency. Then obey ! similar code to character type repeats - written out again for speed. ! However, if the referenced string is the empty string, always treat ! it as matched, any number of times (otherwise there could be infinite ! loops). */ ! case OP_REF: ! { ! int length; ! int offset = GET2(ecode, 1) << 1; /* Doubled ref number */ ! ecode += 3; /* Advance past item */ + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ ! length = (offset >= offset_top || md->offset_vector[offset] < 0) ? ! md->end_subject - eptr + 1 : ! md->offset_vector[offset + 1] - md->offset_vector[offset]; ! ! /* Set up for repetition, or handle the non-repeated case */ ! switch (*ecode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRPLUS: ! case OP_CRMINPLUS: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! c = *ecode++ - OP_CRSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! minimize = (*ecode == OP_CRMINRANGE); ! min = GET2(ecode, 1); ! max = GET2(ecode, 3); ! if (max == 0) ! max = INT_MAX; ! ecode += 5; ! break; ! default: /* No repeat follows */ ! if (!match_ref(offset, eptr, length, md, ims)) ! return MATCH_NOMATCH; ! eptr += length; ! continue; /* With the main loop */ ! } ! /* If the length of the reference is zero, just continue with the ! main loop. */ ! if (length == 0) ! continue; ! /* First, ensure the minimum number of matches are present. We get back ! the length of the reference string explicitly rather than passing the ! address of eptr, so that eptr can be a register variable. */ ! for (i = 1; i <= min; i++) { ! if (!match_ref(offset, eptr, length, md, ims)) ! return MATCH_NOMATCH; ! eptr += length; ! } ! /* If min = max, continue at the same level without recursion. ! They are not both allowed to be zero. */ ! if (min == max) ! continue; ! /* If minimizing, keep trying and advancing the pointer */ ! if (minimize) { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || !match_ref(offset, eptr, length, md, ims)) ! return MATCH_NOMATCH; ! eptr += length; ! } ! /* Control never gets here */ ! } ! /* If maximizing, find the longest string and work backwards */ ! else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (!match_ref(offset, eptr, length, md, ims)) ! break; ! eptr += length; ! } ! while (eptr >= pp) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! eptr -= length; ! } ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! /* Match a bit-mapped character class, possibly repeatedly. This op code is ! used when all the characters in the class have values in the range 0-255. ! The only difference between OP_CLASS and OP_NCLASS occurs when a data ! character outside the range is encountered. ! ! First, look past the end of the item to see if there is repeat information ! following. Then obey similar code to character type repeats - written out ! again for speed. */ ! case OP_NCLASS: ! case OP_CLASS: ! { ! const uschar *data = ecode + 1; /* Save for matching */ ! ecode += 33; /* Advance past the item */ ! ! switch (*ecode) { ! case OP_CRSTAR: ! case OP_CRMINSTAR: ! case OP_CRPLUS: ! case OP_CRMINPLUS: ! case OP_CRQUERY: ! case OP_CRMINQUERY: ! c = *ecode++ - OP_CRSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! break; ! case OP_CRRANGE: ! case OP_CRMINRANGE: ! minimize = (*ecode == OP_CRMINRANGE); ! min = GET2(ecode, 1); ! max = GET2(ecode, 3); ! if (max == 0) ! max = INT_MAX; ! ecode += 5; ! break; ! default: /* No repeat follows */ ! min = max = 1; ! break; } ! /* First, ensure the minimum number of matches are present. */ ! ! /* Not UTF-8 mode */ ! { ! for (i = 1; i <= min; i++) { ! if (eptr >= md->end_subject) ! return MATCH_NOMATCH; ! c = *eptr++; ! if ((data[c / 8] & (1 << (c & 7))) == 0) ! return MATCH_NOMATCH; ! } ! } ! /* If max == min we can continue with the main loop without the ! need to recurse. */ ! ! if (min == max) ! continue; ! ! /* If minimizing, keep testing the rest of the expression and advancing ! the pointer while it matches the class. */ ! ! if (minimize) { ! /* Not UTF-8 mode */ ! { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject) ! return MATCH_NOMATCH; ! c = *eptr++; ! if ((data[c / 8] & (1 << (c & 7))) == 0) ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ } ! /* If maximizing, find the longest possible run, then work backwards. */ ! ! else { ! const uschar *pp = eptr; ! ! /* Not UTF-8 mode */ ! { ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject) ! break; ! c = *eptr; ! if ((data[c / 8] & (1 << (c & 7))) == 0) break; ! eptr++; ! } ! while (eptr >= pp) { ! if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; } } ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! /* Match a run of characters */ ! ! case OP_CHARS: ! { ! register int length = ecode[1]; ! ecode += 2; ! if (length > md->end_subject - eptr) ! return MATCH_NOMATCH; ! if ((ims & PCRE_CASELESS) != 0) { ! while (length-- > 0) ! if (md->lcc[*ecode++] != md->lcc[*eptr++]) ! return MATCH_NOMATCH; ! } else { ! while (length-- > 0) ! if (*ecode++ != *eptr++) ! return MATCH_NOMATCH; } } + break; ! /* Match a single character repeatedly; different opcodes share code. */ ! case OP_EXACT: ! min = max = GET2(ecode, 1); ! ecode += 3; ! goto REPEATCHAR; ! case OP_UPTO: ! case OP_MINUPTO: ! min = 0; ! max = GET2(ecode, 1); ! minimize = *ecode == OP_MINUPTO; ! ecode += 3; ! goto REPEATCHAR; ! case OP_STAR: ! case OP_MINSTAR: ! case OP_PLUS: ! case OP_MINPLUS: ! case OP_QUERY: ! case OP_MINQUERY: ! c = *ecode++ - OP_STAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! /* Common code for all repeated single-character matches. We can give ! up quickly if there are fewer than the minimum number of characters left in ! the subject. */ ! REPEATCHAR: ! /* When not in UTF-8 mode, load a single-byte character. */ ! { ! if (min > md->end_subject - eptr) ! return MATCH_NOMATCH; ! c = *ecode++; } ! /* The value of c at this point is always less than 256, though we may or ! may not be in UTF-8 mode. The code is duplicated for the caseless and ! caseful cases, for speed, since matching characters is likely to be quite ! common. First, ensure the minimum number of matches are present. If min = ! max, continue at the same level without recursing. Otherwise, if ! minimizing, keep trying the rest of the expression and advancing one ! matching character if failing, up to the maximum. Alternatively, if ! maximizing, find the maximum number of characters and work backwards. */ ! DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, ! max, eptr)); ! if ((ims & PCRE_CASELESS) != 0) { ! c = md->lcc[c]; ! for (i = 1; i <= min; i++) ! if (c != md->lcc[*eptr++]) ! return MATCH_NOMATCH; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject || c != md->lcc[*eptr++]) ! return MATCH_NOMATCH; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c != md->lcc[*eptr]) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! return MATCH_NOMATCH; ! } ! /* Control never gets here */ ! } ! /* Caseful comparisons (includes all multi-byte characters) */ ! else { ! for (i = 1; i <= min; i++) ! if (c != *eptr++) ! return MATCH_NOMATCH; ! if (min == max) ! continue; ! if (minimize) { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject || c != *eptr++) ! return MATCH_NOMATCH; ! } ! /* Control never gets here */ ! } else { ! const uschar *pp = eptr; ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c != *eptr) ! break; ! eptr++; ! } ! while (eptr >= pp) ! if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! /* Match a negated single one-byte character. The character we are ! checking can be multibyte. */ ! case OP_NOT: ! if (eptr >= md->end_subject) ! return MATCH_NOMATCH; ! ecode++; ! GETCHARINCTEST(c, eptr); ! if ((ims & PCRE_CASELESS) != 0) { ! c = md->lcc[c]; ! if (md->lcc[*ecode++] == c) ! return MATCH_NOMATCH; ! } else { ! if (*ecode++ == c) ! return MATCH_NOMATCH; ! } ! break; ! /* Match a negated single one-byte character repeatedly. This is almost a ! repeat of the code for a repeated single character, but I haven't found a ! nice way of commoning these up that doesn't require a test of the ! positive/negative option for each character match. Maybe that wouldn't add ! very much to the time taken, but character matching *is* what this is all ! about... */ ! case OP_NOTEXACT: ! min = max = GET2(ecode, 1); ! ecode += 3; ! goto REPEATNOTCHAR; ! case OP_NOTUPTO: ! case OP_NOTMINUPTO: ! min = 0; ! max = GET2(ecode, 1); ! minimize = *ecode == OP_NOTMINUPTO; ! ecode += 3; ! goto REPEATNOTCHAR; ! case OP_NOTSTAR: ! case OP_NOTMINSTAR: ! case OP_NOTPLUS: ! case OP_NOTMINPLUS: ! case OP_NOTQUERY: ! case OP_NOTMINQUERY: ! c = *ecode++ - OP_NOTSTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; + /* Common code for all repeated single-character (less than 255) matches. + We can give up quickly if there are fewer than the minimum number of + characters left in the subject. */ + REPEATNOTCHAR: + if (min > md->end_subject - eptr) + return MATCH_NOMATCH; + c = *ecode++; ! /* The code is duplicated for the caseless and caseful cases, for speed, ! since matching characters is likely to be quite common. First, ensure the ! minimum number of matches are present. If min = max, continue at the same ! level without recursing. Otherwise, if minimizing, keep trying the rest of ! the expression and advancing one matching character if failing, up to the ! maximum. Alternatively, if maximizing, find the maximum number of ! characters and work backwards. */ ! DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, ! max, max, eptr)); ! if ((ims & PCRE_CASELESS) != 0) { ! c = md->lcc[c]; ! /* Not UTF-8 mode */ ! { ! for (i = 1; i <= min; i++) ! if (c == md->lcc[*eptr++]) ! return MATCH_NOMATCH; ! } ! if (min == max) ! continue; ! if (minimize) { ! /* Not UTF-8 mode */ ! { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject || c == md->lcc[*eptr++]) ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! } ! /* Maximize case */ ! else { ! const uschar *pp = eptr; ! /* Not UTF-8 mode */ ! { ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c == md->lcc[*eptr]) ! break; ! eptr++; ! } ! while (eptr >= pp) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! eptr--; ! } ! } ! return MATCH_NOMATCH; ! } ! /* Control never gets here */ } ! /* Caseful comparisons */ ! else { ! /* Not UTF-8 mode */ ! { ! for (i = 1; i <= min; i++) ! if (c == *eptr++) ! return MATCH_NOMATCH; ! } ! if (min == max) ! continue; ! if (minimize) { ! /* Not UTF-8 mode */ ! { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject || c == *eptr++) ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! } ! /* Maximize case */ ! else { ! const uschar *pp = eptr; ! /* Not UTF-8 mode */ ! { ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || c == *eptr) ! break; ! eptr++; ! } ! while (eptr >= pp) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! eptr--; ! } ! } ! return MATCH_NOMATCH; ! } ! } ! /* Control never gets here */ ! /* Match a single character type repeatedly; several different opcodes ! share code. This is very similar to the code for single characters, but we ! repeat it in the interests of efficiency. */ ! case OP_TYPEEXACT: ! min = max = GET2(ecode, 1); ! minimize = TRUE; ! ecode += 3; ! goto REPEATTYPE; ! case OP_TYPEUPTO: ! case OP_TYPEMINUPTO: ! min = 0; ! max = GET2(ecode, 1); ! minimize = *ecode == OP_TYPEMINUPTO; ! ecode += 3; ! goto REPEATTYPE; ! case OP_TYPESTAR: ! case OP_TYPEMINSTAR: ! case OP_TYPEPLUS: ! case OP_TYPEMINPLUS: ! case OP_TYPEQUERY: ! case OP_TYPEMINQUERY: ! c = *ecode++ - OP_TYPESTAR; ! minimize = (c & 1) != 0; ! min = rep_min[c]; /* Pick up values from tables; */ ! max = rep_max[c]; /* zero for max => infinity */ ! if (max == 0) ! max = INT_MAX; ! /* Common code for all repeated single character type matches. Note that ! in UTF-8 mode, '.' matches a character of any length, but for the other ! character types, the valid characters are all one-byte long. */ ! REPEATTYPE: ! ctype = *ecode++; /* Code for the character type */ ! /* First, ensure the minimum number of matches are present. Use inline ! code for maximizing the speed, and do the type test once at the start ! (i.e. keep it out of the loop). Also we can test that there are at least ! the minimum number of bytes before we start. This isn't as effective in ! UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that ! is tidier. */ ! if (min > md->end_subject - eptr) ! return MATCH_NOMATCH; ! if (min > 0) { ! /* Code for the non-UTF-8 case for minimum matching */ ! ! switch (ctype) { ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0) { ! for (i = 1; i <= min; i++) ! if (*eptr++ == NEWLINE) ! return MATCH_NOMATCH; ! } else ! eptr += min; break; ! case OP_ANYBYTE: ! eptr += min; ! break; case OP_NOT_DIGIT: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_digit) != 0) ! return MATCH_NOMATCH; break; case OP_DIGIT: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_digit) == 0) ! return MATCH_NOMATCH; break; case OP_NOT_WHITESPACE: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_space) != 0) ! return MATCH_NOMATCH; break; case OP_WHITESPACE: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_space) == 0) ! return MATCH_NOMATCH; break; case OP_NOT_WORDCHAR: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_word) != 0) ! return MATCH_NOMATCH; break; case OP_WORDCHAR: ! for (i = 1; i <= min; i++) ! if ((md->ctypes[*eptr++] & ctype_word) == 0) ! return MATCH_NOMATCH; break; + } + } ! /* If min = max, continue at the same level without recursing */ ! if (min == max) ! continue; ! /* If minimizing, we have to test the rest of the pattern before each ! subsequent match. Again, separate the UTF-8 case for speed. */ ! if (minimize) { ! /* Not UTF-8 mode */ ! { ! for (i = min;; i++) { ! if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! if (i >= max || eptr >= md->end_subject) ! return MATCH_NOMATCH; ! c = *eptr++; ! switch (ctype) { ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) ! return MATCH_NOMATCH; ! break; ! case OP_ANYBYTE: ! break; ! ! case OP_NOT_DIGIT: ! if ((md->ctypes[c] & ctype_digit) != 0) ! return MATCH_NOMATCH; ! break; ! ! case OP_DIGIT: ! if ((md->ctypes[c] & ctype_digit) == 0) ! return MATCH_NOMATCH; ! break; ! ! case OP_NOT_WHITESPACE: ! if ((md->ctypes[c] & ctype_space) != 0) ! return MATCH_NOMATCH; ! break; ! ! case OP_WHITESPACE: ! if ((md->ctypes[c] & ctype_space) == 0) ! return MATCH_NOMATCH; ! break; ! ! case OP_NOT_WORDCHAR: ! if ((md->ctypes[c] & ctype_word) != 0) ! return MATCH_NOMATCH; ! break; ! ! case OP_WORDCHAR: ! if ((md->ctypes[c] & ctype_word) == 0) ! return MATCH_NOMATCH; ! break; ! } ! } ! } ! /* Control never gets here */ ! } ! ! /* If maximizing it is worth using inline code for speed, doing the type ! test once at the start (i.e. keep it out of the loop). Again, keep the ! UTF-8 stuff separate. */ ! ! else { ! const uschar *pp = eptr; ! ! ! /* Not UTF-8 mode */ ! { ! switch (ctype) { ! case OP_ANY: ! if ((ims & PCRE_DOTALL) == 0) { ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject || *eptr == NEWLINE) ! break; ! eptr++; ! } ! break; ! } ! /* For DOTALL case, fall through and treat as \C */ ! ! case OP_ANYBYTE: ! c = max - min; ! if (c > md->end_subject - eptr) ! c = md->end_subject - eptr; ! eptr += c; ! break; case OP_NOT_DIGIT: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_digit) != 0) ! break; ! eptr++; ! } break; case OP_DIGIT: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_digit) == 0) ! break; ! eptr++; ! } break; case OP_NOT_WHITESPACE: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_space) != 0) ! break; ! eptr++; ! } break; case OP_WHITESPACE: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_space) == 0) ! break; ! eptr++; ! } break; case OP_NOT_WORDCHAR: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_word) != 0) ! break; ! eptr++; ! } break; case OP_WORDCHAR: ! for (i = min; i < max; i++) { ! if (eptr >= md->end_subject ! || (md->ctypes[*eptr] & ctype_word) == 0) ! break; ! eptr++; ! } break; } ! /* eptr is now past the end of the maximum run */ ! while (eptr >= pp) { ! if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != ! MATCH_NOMATCH) ! return rrc; ! } ! } ! /* Get here if we can't make it match with any permitted repetitions */ ! return MATCH_NOMATCH; ! } ! /* Control never gets here */ ! /* There's been some horrible disaster. Since all codes > OP_BRA are ! for capturing brackets, and there shouldn't be any gaps between 0 and ! OP_BRA, arrival here can only mean there is something seriously wrong ! in the code above or the OP_xxx definitions. */ ! default: ! DPRINTF(("Unknown opcode %d\n", *ecode)); ! return PCRE_ERROR_UNKNOWN_NODE; ! } ! ! /* Do not stick any code in here without much thought; it is assumed ! that "continue" in the code above comes out to here to repeat the main ! loop. */ ! ! } /* End of main loop */ ! /* Control never reaches here */ } + /************************************************* ! * Execute a Regular Expression * *************************************************/ ! /* This function applies a compiled re to a subject string and picks out ! portions of the string if it matches. Two elements in the vector are set for ! each substring: the offsets to the start and end of the substring. Arguments: ! external_re points to the compiled expression ! extra_data points to extra data or is NULL ! subject points to the subject string ! length length of subject string (may contain binary zeros) ! start_offset where to start in the subject string ! options option bits ! offsets points to a vector of ints to be filled in with offsets ! offsetcount the number of elements in the vector ! Returns: > 0 => success; value is the number of elements filled in ! = 0 => success, but offsets is not big enough ! -1 => failed to match ! < -1 => some kind of unexpected problem */ ! int ! pcre_exec(const pcre * external_re, const pcre_extra * extra_data, ! const char *subject, int length, int start_offset, int options, ! int *offsets, int offsetcount) { ! int rc, resetcount, ocount; ! int first_byte = -1; ! int req_byte = -1; ! int req_byte2 = -1; ! unsigned long int ims = 0; ! BOOL using_temporary_offsets = FALSE; ! BOOL anchored; ! BOOL startline; ! BOOL first_byte_caseless = FALSE; ! BOOL req_byte_caseless = FALSE; ! match_data match_block; ! const uschar *start_bits = NULL; ! const uschar *start_match = (const uschar *) subject + start_offset; ! const uschar *end_subject; ! const uschar *req_byte_ptr = start_match - 1; ! const pcre_study_data *study; const real_pcre *re = (const real_pcre *) external_re; ! /* Plausibility checks */ ! if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) ! return PCRE_ERROR_BADOPTION; ! if (re == NULL || subject == NULL || (offsets == NULL && offsetcount > 0)) ! return PCRE_ERROR_NULL; ! ! /* Fish out the optional data from the extra_data structure, first setting ! the default values. */ ! ! study = NULL; ! match_block.match_limit = MATCH_LIMIT; ! match_block.callout_data = NULL; ! ! if (extra_data != NULL) { ! register unsigned int flags = extra_data->flags; ! if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) ! study = extra_data->study_data; ! if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) ! match_block.match_limit = extra_data->match_limit; ! if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) ! match_block.callout_data = extra_data->callout_data; } ! /* Now we have re supposedly pointing to the regex */ ! if (re->magic_number != MAGIC_NUMBER) ! return PCRE_ERROR_BADMAGIC; ! anchored = ((re->options | options) & PCRE_ANCHORED) != 0; ! startline = (re->options & PCRE_STARTLINE) != 0; ! match_block.start_code = ! (const uschar *) re + sizeof(real_pcre) + ! re->name_count * re->name_entry_size; ! match_block.start_subject = (const uschar *) subject; ! match_block.start_offset = start_offset; ! match_block.end_subject = match_block.start_subject + length; ! end_subject = match_block.end_subject; ! match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; ! match_block.utf8 = (re->options & PCRE_UTF8) != 0; ! match_block.notbol = (options & PCRE_NOTBOL) != 0; ! match_block.noteol = (options & PCRE_NOTEOL) != 0; ! match_block.notempty = (options & PCRE_NOTEMPTY) != 0; ! match_block.recursive = NULL; /* No recursion at top level */ ! match_block.lcc = re->tables + lcc_offset; ! match_block.ctypes = re->tables + ctypes_offset; ! /* The ims options can vary during the matching as a result of the presence ! of (?ims) items in the pattern. They are kept in a local variable so that ! restoring at the exit of a group is easy. */ ! ims = re->options & (PCRE_CASELESS | PCRE_MULTILINE | PCRE_DOTALL); ! ! /* If the expression has got more back references than the offsets supplied can ! hold, we get a temporary bit of working store to use during the matching. ! Otherwise, we can use the vector supplied, rounding down its size to a multiple ! of 3. */ ! ! ocount = offsetcount - (offsetcount % 3); ! ! if (re->top_backref > 0 && re->top_backref >= ocount / 3) { ! ocount = re->top_backref * 3 + 3; ! match_block.offset_vector = (int *) (malloc) (ocount * sizeof(int)); ! if (match_block.offset_vector == NULL) ! return PCRE_ERROR_NOMEMORY; ! using_temporary_offsets = TRUE; ! DPRINTF(("Got memory to hold back references\n")); ! } else ! match_block.offset_vector = offsets; ! ! match_block.offset_end = ocount; ! match_block.offset_max = (2 * ocount) / 3; ! match_block.offset_overflow = FALSE; ! match_block.capture_last = -1; ! ! /* Compute the minimum number of offsets that we need to reset each time. Doing ! this makes a huge difference to execution time when there aren't many brackets ! in the pattern. */ ! ! resetcount = 2 + re->top_bracket * 2; ! if (resetcount > offsetcount) ! resetcount = ocount; ! ! /* Reset the working variable associated with each extraction. These should ! never be used unless previously set, but they get saved and restored, and so we ! initialize them to avoid reading uninitialized locations. */ ! ! if (match_block.offset_vector != NULL) { ! register int *iptr = match_block.offset_vector + ocount; ! register int *iend = iptr - resetcount / 2 + 1; ! while (--iptr >= iend) ! *iptr = -1; } ! /* Set up the first character to match, if available. The first_byte value is ! never set for an anchored regular expression, but the anchoring may be forced ! at run time, so we have to test for anchoring. The first char may be unset for ! an unanchored pattern, of course. If there's no first char and the pattern was ! studied, there may be a bitmap of possible first characters. */ ! if (!anchored) { ! if ((re->options & PCRE_FIRSTSET) != 0) { ! first_byte = re->first_byte & 255; ! if ((first_byte_caseless = ! ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) ! first_byte = match_block.lcc[first_byte]; ! } else ! if (!startline && study != NULL && ! (study->options & PCRE_STUDY_MAPPED) != 0) ! start_bits = study->start_bits; ! } ! /* For anchored or unanchored matches, there may be a "last known required ! character" set. */ + if ((re->options & PCRE_REQCHSET) != 0) { + req_byte = re->req_byte & 255; + req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; + req_byte2 = (re->tables + fcc_offset)[req_byte]; /* case flipped */ + } ! /* Loop for handling unanchored repeated matching attempts; for anchored regexs ! the loop runs just once. */ ! do { ! register int *iptr = match_block.offset_vector; ! register int *iend = iptr + resetcount; ! /* Reset the maximum number of extractions we might see. */ ! while (iptr < iend) ! *iptr++ = -1; ! /* Advance to a unique first char if possible */ ! if (first_byte >= 0) { ! if (first_byte_caseless) ! while (start_match < end_subject && ! match_block.lcc[*start_match] != first_byte) ! start_match++; ! else ! while (start_match < end_subject && *start_match != first_byte) ! start_match++; ! } ! /* Or to just after \n for a multiline match if possible */ ! else if (startline) { ! if (start_match > match_block.start_subject + start_offset) { ! while (start_match < end_subject && start_match[-1] != NEWLINE) ! start_match++; ! } ! } ! /* Or to a non-unique first char after study */ ! else if (start_bits != NULL) { ! while (start_match < end_subject) { ! register int c = *start_match; ! if ((start_bits[c / 8] & (1 << (c & 7))) == 0) ! start_match++; ! else ! break; ! } ! } ! /* If req_byte is set, we know that that character must appear in the subject ! for the match to succeed. If the first character is set, req_byte must be ! later in the subject; otherwise the test starts at the match point. This ! optimization can save a huge amount of backtracking in patterns with nested ! unlimited repeats that aren't going to match. Writing separate code for ! cased/caseless versions makes it go faster, as does using an autoincrement ! and backing off on a match. ! ! HOWEVER: when the subject string is very, very long, searching to its end can ! take a long time, and give bad performance on quite ordinary patterns. This ! showed up when somebody was matching /^C/ on a 32-megabyte string... so we ! don't do this when the string is sufficiently long. */ ! ! if (req_byte >= 0 && end_subject - start_match < REQ_BYTE_MAX) { ! register const uschar *p = start_match + ((first_byte >= 0) ? 1 : 0); ! ! /* We don't need to repeat the search if we haven't yet reached the ! place we found it at last time. */ ! ! if (p > req_byte_ptr) { ! if (req_byte_caseless) { ! while (p < end_subject) { ! register int pp = *p++; ! if (pp == req_byte || pp == req_byte2) { ! p--; ! break; ! } ! } ! } else { ! while (p < end_subject) { ! if (*p++ == req_byte) { ! p--; ! break; ! } ! } ! } ! /* If we can't find the required character, break the matching loop */ ! ! if (p >= end_subject) ! break; ! ! /* If we have found the required character, save the point where we ! found it, so that we don't search again next time round the loop if ! the start hasn't passed this character yet. */ ! ! req_byte_ptr = p; ! } } ! ! /* When a match occurs, substrings will be set for all internal extractions; ! we just need to set up the whole thing as substring 0 before returning. If ! there were too many extractions, set the return code to zero. In the case ! where we had to get some local store to hold offsets for backreferences, copy ! those back references that we can. In this case there need not be overflow ! if certain parts of the pattern were not used. */ ! ! match_block.start_match = start_match; ! match_block.match_call_count = 0; ! ! rc = match(start_match, match_block.start_code, 2, &match_block, ims, NULL, ! match_isgroup); ! ! if (rc == MATCH_NOMATCH) { ! start_match++; ! continue; } ! ! if (rc != MATCH_MATCH) { ! DPRINTF((">>>> error: returning %d\n", rc)); ! return rc; } ! /* We have a match! Copy the offset information from temporary store if ! necessary */ ! if (using_temporary_offsets) { ! if (offsetcount >= 4) { ! memcpy(offsets + 2, match_block.offset_vector + 2, ! (offsetcount - 2) * sizeof(int)); ! DPRINTF(("Copied offsets from temporary memory\n")); ! } ! if (match_block.end_offset_top > offsetcount) ! match_block.offset_overflow = TRUE; ! DPRINTF(("Freeing temporary memory\n")); ! (free) (match_block.offset_vector); ! } ! rc = match_block.offset_overflow ? 0 : match_block.end_offset_top / 2; ! if (offsetcount < 2) ! rc = 0; ! else { ! offsets[0] = start_match - match_block.start_subject; ! offsets[1] = match_block.end_match_ptr - match_block.start_subject; ! } ! DPRINTF((">>>> returning %d\n", rc)); ! return rc; ! } ! /* This "while" is the end of the "do" above */ ! while (!anchored && start_match <= end_subject); ! if (using_temporary_offsets) { ! DPRINTF(("Freeing temporary memory\n")); ! (free) (match_block.offset_vector); ! } ! ! DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); ! ! return PCRE_ERROR_NOMATCH; } ! /* End of pcre.c */ *** 1_7_7.366/hdrs/pcre.h Thu, 05 Sep 2002 09:29:20 -0500 dunemush (pennmush/d/38_pcre.h 1.3.1.2.1.1.1.7 660) --- 1_7_7.446(w)/hdrs/pcre.h Thu, 29 May 2003 10:01:48 -0500 dunemush (pennmush/d/38_pcre.h 1.3.1.2.1.1.1.8 660) *************** *** 1,83 **** ! /* This is a library of functions to support regular expressions whose syntax ! and semantics are as close as possible to those of the Perl 5 language. See ! the file Tech.Notes for some information on the internals. ! ! Written by: Philip Hazel ! ! Copyright (c) 1997-2000 University of Cambridge ! ! ----------------------------------------------------------------------------- ! Permission is granted to anyone to use this software for any purpose on any ! computer system, and to redistribute it freely, subject to the following ! restrictions: ! ! 1. This software is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! ! 2. The origin of this software must not be misrepresented, either by ! explicit claim or by omission. ! ! 3. Altered versions must be plainly marked as such, and must not be ! misrepresented as being the original software. ! ! 4. If PCRE is embedded in any software that is released under the GNU ! General Purpose Licence (GPL), then the terms of that licence shall ! supersede any condition above with which it is incompatible. ! ----------------------------------------------------------------------------- ! */ ! ! /***** ALTERED VERSION ******/ ! /* This file combines pcre.h and internal.h into a single header for ! * use in PennMUSH, and removes some things we handle ourselves or never ! * used to save space.. ! * Also tries to avoid infinitly-long backtracking. ! * Modified by Javelin and Raevnos ! * ! */ ! /* This header contains definitions that are shared between the different ! modules, but which are not relevant to the outside. */ #ifndef _PCRE_H #define _PCRE_H ! /* The file pcre.h is build by "configure". Do not edit it; instead ! make changes to pcre.in. */ ! #define PCRE_MAJOR 3 ! #define PCRE_MINOR 7 ! #define PCRE_DATE 29-Oct-2001 ! ! /* Win32 uses DLL by default */ ! ! #ifdef _WIN32 ! # ifdef STATIC ! # define PCRE_DL_IMPORT ! # else ! # define PCRE_DL_IMPORT __declspec(dllimport) ! # endif ! #else ! # define PCRE_DL_IMPORT #endif /* Options */ ! #define PCRE_CASELESS 0x0001 ! #define PCRE_MULTILINE 0x0002 ! #define PCRE_DOTALL 0x0004 ! #define PCRE_EXTENDED 0x0008 ! #define PCRE_ANCHORED 0x0010 ! #define PCRE_DOLLAR_ENDONLY 0x0020 ! #define PCRE_EXTRA 0x0040 ! #define PCRE_NOTBOL 0x0080 ! #define PCRE_NOTEOL 0x0100 ! #define PCRE_UNGREEDY 0x0200 ! #define PCRE_NOTEMPTY 0x0400 ! #define PCRE_UTF8 0x0800 ! /* Exec-time and get-time error codes */ #define PCRE_ERROR_NOMATCH (-1) #define PCRE_ERROR_NULL (-2) --- 1,51 ---- ! /************************************************* ! * Perl-Compatible Regular Expressions * ! *************************************************/ ! /* Copyright (c) 1997-2003 University of Cambridge */ + /* Modified a bit by Shawn Wagner for inclusion in PennMUSH. See + pcre.c for details. */ #ifndef _PCRE_H #define _PCRE_H ! #define PCRE_MAJOR 4 ! #define PCRE_MINOR 2 ! #define PCRE_DATE 14-Apr-2003 ! #ifndef PCRE_DATA_SCOPE ! # define PCRE_DATA_SCOPE extern ! #endif ! ! /* Have to include stdlib.h in order to ensure that size_t is defined; ! it is needed here for malloc. */ ! ! #include ! ! /* Allow for C++ users */ ! ! #ifdef __cplusplus ! extern "C" { #endif /* Options */ ! #define PCRE_CASELESS 0x0001 ! #define PCRE_MULTILINE 0x0002 ! #define PCRE_DOTALL 0x0004 ! #define PCRE_EXTENDED 0x0008 ! #define PCRE_ANCHORED 0x0010 ! #define PCRE_DOLLAR_ENDONLY 0x0020 ! #define PCRE_EXTRA 0x0040 ! #define PCRE_NOTBOL 0x0080 ! #define PCRE_NOTEOL 0x0100 ! #define PCRE_UNGREEDY 0x0200 ! #define PCRE_NOTEMPTY 0x0400 ! #define PCRE_UTF8 0x0800 ! #define PCRE_NO_AUTO_CAPTURE 0x1000 ! /* Exec-time and get/set-time error codes */ #define PCRE_ERROR_NOMATCH (-1) #define PCRE_ERROR_NULL (-2) *************** *** 86,121 **** #define PCRE_ERROR_UNKNOWN_NODE (-5) #define PCRE_ERROR_NOMEMORY (-6) #define PCRE_ERROR_NOSUBSTRING (-7) ! #define PCRE_ERROR_BACKTRACKING (-8) /* Request types for pcre_fullinfo() */ ! #define PCRE_INFO_OPTIONS 0 ! #define PCRE_INFO_SIZE 1 ! #define PCRE_INFO_CAPTURECOUNT 2 ! #define PCRE_INFO_BACKREFMAX 3 ! #define PCRE_INFO_FIRSTCHAR 4 ! #define PCRE_INFO_FIRSTTABLE 5 ! #define PCRE_INFO_LASTLITERAL 6 /* Types */ ! struct real_pcre; /* declaration; the definition is private */ ! struct real_pcre_extra; /* declaration; the definition is private */ ! ! typedef struct real_pcre pcre; ! typedef struct real_pcre_extra pcre_extra; ! #undef PCRE_DL_IMPORT ! /* Functions */ ! ! extern pcre *pcre_compile(const char *, int, const char **, int *, ! const unsigned char *); ! extern int pcre_copy_substring(const char *, int *, int, int, char *, int); ! extern int pcre_exec(const pcre *, const pcre_extra *, const char *, ! int, int, int, int *, int); ! extern const unsigned char *pcre_maketables(void); ! extern pcre_extra *pcre_study(const pcre *, int, const char **); #endif /* End of pcre.h */ --- 54,138 ---- #define PCRE_ERROR_UNKNOWN_NODE (-5) #define PCRE_ERROR_NOMEMORY (-6) #define PCRE_ERROR_NOSUBSTRING (-7) ! #define PCRE_ERROR_MATCHLIMIT (-8) ! #define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ /* Request types for pcre_fullinfo() */ ! #define PCRE_INFO_OPTIONS 0 ! #define PCRE_INFO_SIZE 1 ! #define PCRE_INFO_CAPTURECOUNT 2 ! #define PCRE_INFO_BACKREFMAX 3 ! #define PCRE_INFO_FIRSTBYTE 4 ! #define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ ! #define PCRE_INFO_FIRSTTABLE 5 ! #define PCRE_INFO_LASTLITERAL 6 ! #define PCRE_INFO_NAMEENTRYSIZE 7 ! #define PCRE_INFO_NAMECOUNT 8 ! #define PCRE_INFO_NAMETABLE 9 ! #define PCRE_INFO_STUDYSIZE 10 ! ! /* Request types for pcre_config() */ ! ! #define PCRE_CONFIG_UTF8 0 ! #define PCRE_CONFIG_NEWLINE 1 ! #define PCRE_CONFIG_LINK_SIZE 2 ! #define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 ! #define PCRE_CONFIG_MATCH_LIMIT 4 ! ! /* Bit flags for the pcre_extra structure */ ! ! #define PCRE_EXTRA_STUDY_DATA 0x0001 ! #define PCRE_EXTRA_MATCH_LIMIT 0x0002 ! #define PCRE_EXTRA_CALLOUT_DATA 0x0004 /* Types */ ! struct real_pcre; /* declaration; the definition is private */ ! typedef struct real_pcre pcre; ! /* The structure for passing additional data to pcre_exec(). This is defined in ! such as way as to be extensible. */ ! typedef struct pcre_extra { ! unsigned long int flags; /* Bits for which fields are set */ ! void *study_data; /* Opaque data from pcre_study() */ ! unsigned long int match_limit; /* Maximum number of calls to match() */ ! void *callout_data; /* Data passed back in callouts */ ! } pcre_extra; ! ! /* The structure for passing out data via the pcre_callout_function. We use a ! structure so that new fields can be added on the end in future versions, ! without changing the API of the function, thereby allowing old clients to work ! without modification. */ ! ! typedef struct pcre_callout_block { ! int version; /* Identifies version of block */ ! /* ------------------------ Version 0 ------------------------------- */ ! int callout_number; /* Number compiled into pattern */ ! int *offset_vector; /* The offset vector */ ! const char *subject; /* The subject being matched */ ! int subject_length; /* The length of the subject */ ! int start_match; /* Offset to start of this match attempt */ ! int current_position; /* Where we currently are */ ! int capture_top; /* Max current capture */ ! int capture_last; /* Most recently closed capture */ ! void *callout_data; /* Data passed in with the call */ ! /* ------------------------------------------------------------------ */ ! } pcre_callout_block; ! ! ! /* Exported PCRE functions */ ! ! extern pcre *pcre_compile(const char *, int, const char **, ! int *, const unsigned char *); ! extern int pcre_copy_substring(const char *, int *, int, int, char *, int); ! extern int pcre_exec(const pcre *, const pcre_extra *, ! const char *, int, int, int, int *, int); ! extern const unsigned char *pcre_maketables(void); ! extern pcre_extra *pcre_study(const pcre *, int, const char **); + #ifdef __cplusplus + } /* extern "C" */ + #endif #endif /* End of pcre.h */ *** 1_7_7.366/hdrs/mysocket.h Wed, 26 Mar 2003 11:35:29 -0600 dunemush (pennmush/e/51_mysocket.h 1.17 660) --- 1_7_7.446(w)/hdrs/mysocket.h Thu, 29 May 2003 10:01:48 -0500 dunemush (pennmush/e/51_mysocket.h 1.18 660) *************** *** 49,57 **** const char *port; /**< Host's source port */ }; union sockaddr_u { ! struct sockaddr addr; ! char data[MAXSOCKADDR]; }; /* What htons expects. Is this even used anymore? */ --- 49,58 ---- const char *port; /**< Host's source port */ }; + /** A union for sockaddr manipulation */ union sockaddr_u { ! struct sockaddr addr; /**< A sockaddr structure */ ! char data[MAXSOCKADDR]; /**< A byte array representation */ }; /* What htons expects. Is this even used anymore? */ *** 1_7_7.366/src/mysocket.c Thu, 27 Mar 2003 11:18:02 -0600 dunemush (pennmush/f/0_mysocket.c 1.16.1.1.1.1.1.1.1.4.1.4.1.8 660) --- 1_7_7.446(w)/src/mysocket.c Thu, 29 May 2003 10:01:33 -0500 dunemush (pennmush/f/0_mysocket.c 1.16.1.1.1.1.1.1.1.4.1.4.1.9 660) *************** *** 236,242 **** /** Start listening to TCP on a given port. Basically tcp_listen * from UNPv1 * \param port port to listen on. ! * \param addr pointer to sockaddr to copy address data from. * \param len length of addr. * \param host hostname or address to listen on, as a string. * \return file descriptor of listening socket, or -1 for failure. --- 236,242 ---- /** Start listening to TCP on a given port. Basically tcp_listen * from UNPv1 * \param port port to listen on. ! * \param addr pointer to sockaddr to copy address data to. * \param len length of addr. * \param host hostname or address to listen on, as a string. * \return file descriptor of listening socket, or -1 for failure. *** 1_7_7.366/win32/funs.h Wed, 26 Mar 2003 14:31:41 -0600 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3 660) --- 1_7_7.446(w)/win32/funs.h Thu, 22 May 2003 11:38:35 -0500 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.7 660) *************** *** 94,99 **** --- 94,100 ---- FUNCTION_PROTO(fun_followers); FUNCTION_PROTO(fun_following); FUNCTION_PROTO(fun_foreach); + FUNCTION_PROTO(fun_fraction); FUNCTION_PROTO(fun_fullname); FUNCTION_PROTO(fun_functions); FUNCTION_PROTO(fun_get); *************** *** 238,243 **** --- 239,245 ---- FUNCTION_PROTO(fun_rloc); FUNCTION_PROTO(fun_rnum); FUNCTION_PROTO(fun_room); + FUNCTION_PROTO(fun_root); FUNCTION_PROTO(fun_round); FUNCTION_PROTO(fun_s); FUNCTION_PROTO(fun_scan); *************** *** 273,278 **** --- 275,281 ---- FUNCTION_PROTO(fun_stripansi); FUNCTION_PROTO(fun_strlen); FUNCTION_PROTO(fun_strmatch); + FUNCTION_PROTO(fun_strreplace); FUNCTION_PROTO(fun_sub); FUNCTION_PROTO(fun_subj); FUNCTION_PROTO(fun_switch); *** 1_7_7.366/hdrs/mushtype.h Sat, 29 Mar 2003 09:46:22 -0600 dunemush (pennmush/f/20_mushtype.h 1.2.1.1.1.12 660) --- 1_7_7.446(w)/hdrs/mushtype.h Thu, 29 May 2003 10:01:47 -0500 dunemush (pennmush/f/20_mushtype.h 1.2.1.1.1.13 660) *************** *** 26,31 **** --- 26,32 ---- #define INTERACT_SEE 0x1 #define INTERACT_HEAR 0x2 #define INTERACT_MATCH 0x4 + #define INTERACT_PRESENCE 0x8 typedef unsigned char *object_flag_type; *** 1_7_7.366/game/txt/hlp/pennconf.hlp Thu, 27 Mar 2003 21:05:56 -0600 dunemush (pennmush/f/43_pennconf.h 1.9 600) --- 1_7_7.446(w)/game/txt/hlp/pennconf.hlp Tue, 13 May 2003 12:22:57 -0500 dunemush (pennmush/f/43_pennconf.h 1.12 600) *************** *** 10,16 **** The categories and groups are the same as those used by @config/list. Values must be of the listed type for each option. They include: ! , , (Yes/No), or . & @config attribs These options control wether or not some built in attributes are checked. --- 10,16 ---- The categories and groups are the same as those used by @config/list. Values must be of the listed type for each option. They include: ! , , (Yes/No),