2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // common.c -- misc functions used in client and server
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
33 char com_token[MAX_INPUTLINE];
35 const char **com_argv;
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
47 ============================================================================
51 ============================================================================
54 short ShortSwap (short l)
66 unsigned char b1,b2,b3,b4;
73 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
76 float FloatSwap (float f)
86 dat2.b[0] = dat1.b[3];
87 dat2.b[1] = dat1.b[2];
88 dat2.b[2] = dat1.b[1];
89 dat2.b[3] = dat1.b[0];
94 // Extract integers from buffers
96 unsigned int BuffBigLong (const unsigned char *buffer)
98 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
101 unsigned short BuffBigShort (const unsigned char *buffer)
103 return (buffer[0] << 8) | buffer[1];
106 unsigned int BuffLittleLong (const unsigned char *buffer)
108 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
111 unsigned short BuffLittleShort (const unsigned char *buffer)
113 return (buffer[1] << 8) | buffer[0];
118 ============================================================================
122 ============================================================================
125 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
126 // and the initial and final xor values shown below... in other words, the
127 // CCITT standard CRC used by XMODEM
129 #define CRC_INIT_VALUE 0xffff
130 #define CRC_XOR_VALUE 0x0000
132 static unsigned short crctable[256] =
134 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
135 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
136 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
137 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
138 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
139 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
140 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
141 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
142 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
143 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
144 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
145 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
146 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
147 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
148 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
149 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
150 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
151 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
152 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
153 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
154 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
155 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
156 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
157 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
158 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
159 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
160 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
161 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
162 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
163 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
164 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
165 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
168 unsigned short CRC_Block(const unsigned char *data, size_t size)
170 unsigned short crc = CRC_INIT_VALUE;
172 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
173 return crc ^ CRC_XOR_VALUE;
177 static unsigned char chktbl[1024 + 4] =
179 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
180 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
181 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
182 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
183 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
184 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
185 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
186 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
187 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
188 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
189 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
190 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
191 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
192 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
193 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
194 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
195 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
196 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
197 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
198 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
199 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
200 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
201 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
202 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
203 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
204 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
205 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
206 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
207 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
208 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
209 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
210 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
212 // map checksum goes here
217 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
220 unsigned char chkb[60 + 4];
222 p = chktbl + (sequence % (sizeof(chktbl) - 8));
226 memcpy(chkb, base, length);
228 chkb[length] = (sequence & 0xff) ^ p[0];
229 chkb[length+1] = p[1];
230 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
231 chkb[length+3] = p[3];
233 return CRC_Block(chkb, length + 4) & 0xff;
237 ==============================================================================
241 Handles byte ordering and avoids alignment errors
242 ==============================================================================
249 void MSG_WriteChar (sizebuf_t *sb, int c)
253 buf = SZ_GetSpace (sb, 1);
257 void MSG_WriteByte (sizebuf_t *sb, int c)
261 buf = SZ_GetSpace (sb, 1);
265 void MSG_WriteShort (sizebuf_t *sb, int c)
269 buf = SZ_GetSpace (sb, 2);
274 void MSG_WriteLong (sizebuf_t *sb, int c)
278 buf = SZ_GetSpace (sb, 4);
280 buf[1] = (c>>8)&0xff;
281 buf[2] = (c>>16)&0xff;
285 void MSG_WriteFloat (sizebuf_t *sb, float f)
295 dat.l = LittleLong (dat.l);
297 SZ_Write (sb, (unsigned char *)&dat.l, 4);
300 void MSG_WriteString (sizebuf_t *sb, const char *s)
303 MSG_WriteChar (sb, 0);
305 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
308 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
311 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
314 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
317 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
319 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
322 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
325 MSG_WriteShort (sb, (int)(f + 0.5));
327 MSG_WriteShort (sb, (int)(f - 0.5));
330 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
332 MSG_WriteFloat (sb, f);
335 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
337 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
338 MSG_WriteCoord13i (sb, f);
339 else if (protocol == PROTOCOL_DARKPLACES1)
340 MSG_WriteCoord32f (sb, f);
341 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
342 MSG_WriteCoord16i (sb, f);
344 MSG_WriteCoord32f (sb, f);
347 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
349 MSG_WriteCoord (sb, v[0], protocol);
350 MSG_WriteCoord (sb, v[1], protocol);
351 MSG_WriteCoord (sb, v[2], protocol);
354 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
355 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
358 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
360 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
363 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
366 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
368 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
371 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
373 MSG_WriteFloat (sb, f);
376 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
378 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
379 MSG_WriteAngle8i (sb, f);
381 MSG_WriteAngle16i (sb, f);
388 qboolean msg_badread;
390 void MSG_BeginReading (void)
396 int MSG_ReadLittleShort (void)
398 if (msg_readcount+2 > net_message.cursize)
404 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
407 int MSG_ReadBigShort (void)
409 if (msg_readcount+2 > net_message.cursize)
415 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
418 int MSG_ReadLittleLong (void)
420 if (msg_readcount+4 > net_message.cursize)
426 return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
429 int MSG_ReadBigLong (void)
431 if (msg_readcount+4 > net_message.cursize)
437 return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
440 float MSG_ReadLittleFloat (void)
447 if (msg_readcount+4 > net_message.cursize)
453 dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
457 float MSG_ReadBigFloat (void)
464 if (msg_readcount+4 > net_message.cursize)
470 dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
474 char *MSG_ReadString (void)
476 static char string[MAX_INPUTLINE];
478 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
484 int MSG_ReadBytes (int numbytes, unsigned char *out)
487 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
492 float MSG_ReadCoord13i (void)
494 return MSG_ReadLittleShort() * (1.0/8.0);
497 float MSG_ReadCoord16i (void)
499 return (signed short) MSG_ReadLittleShort();
502 float MSG_ReadCoord32f (void)
504 return MSG_ReadLittleFloat();
507 float MSG_ReadCoord (protocolversion_t protocol)
509 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
510 return MSG_ReadCoord13i();
511 else if (protocol == PROTOCOL_DARKPLACES1)
512 return MSG_ReadCoord32f();
513 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
514 return MSG_ReadCoord16i();
516 return MSG_ReadCoord32f();
519 void MSG_ReadVector (float *v, protocolversion_t protocol)
521 v[0] = MSG_ReadCoord(protocol);
522 v[1] = MSG_ReadCoord(protocol);
523 v[2] = MSG_ReadCoord(protocol);
526 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
527 float MSG_ReadAngle8i (void)
529 return (signed char) MSG_ReadByte () * (360.0/256.0);
532 float MSG_ReadAngle16i (void)
534 return (signed short)MSG_ReadShort () * (360.0/65536.0);
537 float MSG_ReadAngle32f (void)
539 return MSG_ReadFloat ();
542 float MSG_ReadAngle (protocolversion_t protocol)
544 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
545 return MSG_ReadAngle8i ();
547 return MSG_ReadAngle16i ();
551 //===========================================================================
553 void SZ_Clear (sizebuf_t *buf)
558 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
562 if (buf->cursize + length > buf->maxsize)
564 if (!buf->allowoverflow)
565 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
567 if (length > buf->maxsize)
568 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
570 buf->overflowed = true;
571 Con_Print("SZ_GetSpace: overflow\n");
575 data = buf->data + buf->cursize;
576 buf->cursize += length;
581 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
583 memcpy (SZ_GetSpace(buf,length),data,length);
586 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
587 // attention, it has been eradicated from here, its only (former) use in
588 // all of darkplaces.
590 static char *hexchar = "0123456789ABCDEF";
591 void Com_HexDumpToConsole(const unsigned char *data, int size)
595 char *cur, *flushpointer;
596 const unsigned char *d;
598 flushpointer = text + 512;
599 for (i = 0;i < size;)
606 *cur++ = hexchar[(i >> 12) & 15];
607 *cur++ = hexchar[(i >> 8) & 15];
608 *cur++ = hexchar[(i >> 4) & 15];
609 *cur++ = hexchar[(i >> 0) & 15];
612 for (j = 0;j < 16;j++)
616 *cur++ = hexchar[(d[j] >> 4) & 15];
617 *cur++ = hexchar[(d[j] >> 0) & 15];
628 for (j = 0;j < 16;j++)
632 // color change prefix character has to be treated specially
633 if (d[j] == STRING_COLOR_TAG)
635 *cur++ = STRING_COLOR_TAG;
636 *cur++ = STRING_COLOR_TAG;
638 else if (d[j] >= ' ')
648 if (cur >= flushpointer || i >= size)
657 void SZ_HexDumpToConsole(const sizebuf_t *buf)
659 Com_HexDumpToConsole(buf->data, buf->cursize);
663 //============================================================================
668 COM_ParseToken_Simple
670 Parse a token out of a string
673 int COM_ParseToken_Simple(const char **datapointer, int returnnewline)
677 const char *data = *datapointer;
694 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
704 // handle Windows line ending
705 if (data[0] == '\r' && data[1] == '\n')
708 if (data[0] == '/' && data[1] == '/')
711 while (*data && *data != '\n' && *data != '\r')
715 else if (data[0] == '/' && data[1] == '*')
719 while (*data && (data[0] != '*' || data[1] != '/'))
727 else if (*data == '\"')
730 for (data++;*data != '\"';data++)
732 if (!*data || len >= (int)sizeof(com_token) - 1)
748 com_token[len++] = c;
751 *datapointer = data+1;
754 else if (*data == '\r')
756 // translate Mac line ending to UNIX
757 com_token[len++] = '\n';data++;
762 else if (*data == '\n')
765 com_token[len++] = *data++;
773 for (;*data > ' ';data++)
775 if (len >= (int)sizeof(com_token) - 1)
781 com_token[len++] = *data;
791 COM_ParseToken_QuakeC
793 Parse a token out of a string
796 int COM_ParseToken_QuakeC(const char **datapointer, int returnnewline)
800 const char *data = *datapointer;
817 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
827 // handle Windows line ending
828 if (data[0] == '\r' && data[1] == '\n')
831 if (data[0] == '/' && data[1] == '/')
834 while (*data && *data != '\n' && *data != '\r')
838 else if (data[0] == '/' && data[1] == '*')
842 while (*data && (data[0] != '*' || data[1] != '/'))
850 else if (*data == '\"' || *data == '\'')
854 for (data++;*data != quote;data++)
856 if (!*data || len >= (int)sizeof(com_token) - 1)
872 com_token[len++] = c;
875 *datapointer = data+1;
878 else if (*data == '\r')
880 // translate Mac line ending to UNIX
881 com_token[len++] = '\n';data++;
886 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
889 com_token[len++] = *data++;
897 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
899 if (len >= (int)sizeof(com_token) - 1)
905 com_token[len++] = *data;
915 COM_ParseToken_VM_Tokenize
917 Parse a token out of a string
920 int COM_ParseToken_VM_Tokenize(const char **datapointer, int returnnewline)
924 const char *data = *datapointer;
941 for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
951 // handle Windows line ending
952 if (data[0] == '\r' && data[1] == '\n')
955 if (data[0] == '/' && data[1] == '/')
958 while (*data && *data != '\n' && *data != '\r')
962 else if (data[0] == '/' && data[1] == '*')
966 while (*data && (data[0] != '*' || data[1] != '/'))
974 else if (*data == '\"' || *data == '\'')
978 for (data++;*data != quote;data++)
980 if (!*data || len >= (int)sizeof(com_token) - 1)
996 com_token[len++] = c;
999 *datapointer = data+1;
1002 else if (*data == '\r')
1004 // translate Mac line ending to UNIX
1005 com_token[len++] = '\n';data++;
1007 *datapointer = data;
1010 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1013 com_token[len++] = *data++;
1015 *datapointer = data;
1021 for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1023 if (len >= (int)sizeof(com_token) - 1)
1026 *datapointer = NULL;
1029 com_token[len++] = *data;
1032 *datapointer = data;
1039 COM_ParseToken_Console
1041 Parse a token out of a string, behaving like the qwcl console
1044 int COM_ParseToken_Console(const char **datapointer)
1047 const char *data = *datapointer;
1054 *datapointer = NULL;
1060 for (;*data <= ' ';data++)
1065 *datapointer = NULL;
1070 if (*data == '/' && data[1] == '/')
1073 while (*data && *data != '\n' && *data != '\r')
1077 else if (*data == '\"')
1080 for (data++;*data != '\"';data++)
1082 if (!*data || len >= (int)sizeof(com_token) - 1)
1085 *datapointer = NULL;
1088 // allow escaped " and \ case
1089 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1091 com_token[len++] = *data;
1094 *datapointer = data+1;
1099 for (;*data > ' ';data++)
1101 if (len >= (int)sizeof(com_token) - 1)
1104 *datapointer = NULL;
1107 com_token[len++] = *data;
1110 *datapointer = data;
1121 Returns the position (1 to argc-1) in the program's argument list
1122 where the given parameter apears, or 0 if not present
1125 int COM_CheckParm (const char *parm)
1129 for (i=1 ; i<com_argc ; i++)
1132 continue; // NEXTSTEP sometimes clears appkit vars.
1133 if (!strcmp (parm,com_argv[i]))
1140 //===========================================================================
1144 typedef struct gamemode_info_s
1146 const char* prog_name;
1147 const char* cmdline;
1148 const char* gamename;
1149 const char* gamedirname1;
1150 const char* gamedirname2;
1151 const char* gamescreenshotname;
1152 const char* gameuserdirname;
1155 static const gamemode_info_t gamemode_info [] =
1156 {// prog_name cmdline gamename gamedirname gamescreenshotname
1159 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1160 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1162 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1163 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1165 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1166 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1168 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1169 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1171 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1172 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1174 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1175 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1177 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1178 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1180 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1181 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1183 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1184 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1186 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1187 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1189 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1190 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1192 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1193 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1195 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1196 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1198 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1199 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1201 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1202 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1204 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1205 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1207 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master
1208 { "netherworld", "-netherworld", "Netherworld: Dark Master", "id1", "netherworld", "nw", "darkplaces" },
1210 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1211 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1212 // GAME_DEFEATINDETAIL2
1213 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1214 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1216 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1217 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1218 // GAME_CONTAGIONTHEORY
1219 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1220 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1223 void COM_InitGameType (void)
1225 char name [MAX_OSPATH];
1228 FS_StripExtension (com_argv[0], name, sizeof (name));
1229 COM_ToLowerString (name, name, sizeof (name));
1231 // Check the binary name; default to GAME_NORMAL (0)
1232 gamemode = GAME_NORMAL;
1233 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1234 if (strstr (name, gamemode_info[i].prog_name))
1236 gamemode = (gamemode_t)i;
1240 // Look for a command-line option
1241 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1242 if (COM_CheckParm (gamemode_info[i].cmdline))
1244 gamemode = (gamemode_t)i;
1248 gamename = gamemode_info[gamemode].gamename;
1249 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1250 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1251 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1252 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1261 void COM_Init_Commands (void)
1264 char com_cmdline[MAX_INPUTLINE];
1266 Cvar_RegisterVariable (®istered);
1267 Cvar_RegisterVariable (&cmdline);
1269 // reconstitute the command line for the cmdline externally visible cvar
1271 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1274 if (strstr(com_argv[j], " "))
1276 // arg contains whitespace, store quotes around it
1277 com_cmdline[n++] = '\"';
1278 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1279 com_cmdline[n++] = com_argv[j][i++];
1280 com_cmdline[n++] = '\"';
1284 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1285 com_cmdline[n++] = com_argv[j][i++];
1287 if (n < ((int)sizeof(com_cmdline) - 1))
1288 com_cmdline[n++] = ' ';
1293 Cvar_Set ("cmdline", com_cmdline);
1300 does a varargs printf into a temp buffer, so I don't need to have
1301 varargs versions of all text functions.
1302 FIXME: make this buffer size safe someday
1305 char *va(const char *format, ...)
1308 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1309 static char string[8][1024], *s;
1310 static int stringindex = 0;
1312 s = string[stringindex];
1313 stringindex = (stringindex + 1) & 7;
1314 va_start (argptr, format);
1315 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1322 //======================================
1324 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1330 # define snprintf _snprintf
1331 # define vsnprintf _vsnprintf
1335 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1340 va_start (args, format);
1341 result = dpvsnprintf (buffer, buffersize, format, args);
1348 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1352 result = vsnprintf (buffer, buffersize, format, args);
1353 if (result < 0 || (size_t)result >= buffersize)
1355 buffer[buffersize - 1] = '\0';
1363 //======================================
1365 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1370 while (*in && size_out > 1)
1372 if (*in >= 'A' && *in <= 'Z')
1373 *out++ = *in++ + 'a' - 'A';
1381 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1386 while (*in && size_out > 1)
1388 if (*in >= 'a' && *in <= 'z')
1389 *out++ = *in++ + 'A' - 'a';
1397 int COM_StringBeginsWith(const char *s, const char *match)
1399 for (;*s && *match;s++, match++)
1405 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1407 int argc, commentprefixlength;
1411 tokenbufend = tokenbuf + tokenbufsize;
1413 commentprefixlength = 0;
1415 commentprefixlength = (int)strlen(commentprefix);
1416 while (*l && *l != '\n' && *l != '\r')
1420 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1422 while (*l && *l != '\n' && *l != '\r')
1426 if (argc >= maxargc)
1428 argv[argc++] = tokenbuf;
1432 while (*l && *l != '"')
1434 if (tokenbuf >= tokenbufend)
1445 if (tokenbuf >= tokenbufend)
1450 if (tokenbuf >= tokenbufend)
1471 COM_StringLengthNoColors
1473 calculates the visible width of a color coded string.
1475 *valid is filled with TRUE if the string is a valid colored string (that is, if
1476 it does not end with an unfinished color code). If it gets filled with FALSE, a
1477 fix would be adding a STRING_COLOR_TAG at the end of the string.
1479 valid can be set to NULL if the caller doesn't care.
1481 For size_s, specify the maximum number of characters from s to use, or 0 to use
1482 all characters until the zero terminator.
1486 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1488 const char *end = size_s ? (s + size_s) : NULL;
1492 switch((s == end) ? 0 : *s)
1498 case STRING_COLOR_TAG:
1500 switch((s == end) ? 0 : *s)
1502 case 0: // ends with unfinished color code!
1507 case STRING_COLOR_TAG: // escaped ^
1510 case '0': case '1': case '2': case '3': case '4':
1511 case '5': case '6': case '7': case '8': case '9': // color code
1513 default: // not a color code
1514 ++len; // STRING_COLOR_TAG
1515 ++len; // the character
1530 COM_StringDecolorize
1532 removes color codes from a string.
1534 If escape_carets is true, the resulting string will be safe for printing. If
1535 escape_carets is false, the function will just strip color codes (for logging
1538 If the output buffer size did not suffice for converting, the function returns
1539 FALSE. Generally, if escape_carets is false, the output buffer needs
1540 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1541 bytes. In any case, the function makes sure that the resulting string is
1544 For size_in, specify the maximum number of characters from in to use, or 0 to use
1545 all characters until the zero terminator.
1549 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1551 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1552 const char *end = size_in ? (in + size_in) : NULL;
1557 switch((in == end) ? 0 : *in)
1562 case STRING_COLOR_TAG:
1564 switch((in == end) ? 0 : *in)
1566 case 0: // ends with unfinished color code!
1567 APPEND(STRING_COLOR_TAG);
1568 // finish the code by appending another caret when escaping
1570 APPEND(STRING_COLOR_TAG);
1573 case STRING_COLOR_TAG: // escaped ^
1574 APPEND(STRING_COLOR_TAG);
1575 // append a ^ twice when escaping
1577 APPEND(STRING_COLOR_TAG);
1579 case '0': case '1': case '2': case '3': case '4':
1580 case '5': case '6': case '7': case '8': case '9': // color code
1582 default: // not a color code
1583 APPEND(STRING_COLOR_TAG);
1598 // written by Elric, thanks Elric!
1599 char *SearchInfostring(const char *infostring, const char *key)
1601 static char value [MAX_INPUTLINE];
1602 char crt_key [MAX_INPUTLINE];
1603 size_t value_ind, key_ind;
1606 if (*infostring++ != '\\')
1621 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1623 crt_key[key_ind] = '\0';
1627 crt_key[key_ind++] = c;
1630 // If it's the key we are looking for, save it in "value"
1631 if (!strcmp(crt_key, key))
1637 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1639 value[value_ind] = '\0';
1643 value[value_ind++] = c;
1647 // Else, skip the value
1660 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1668 keylength = strlen(key);
1669 if (valuelength < 1 || !value)
1671 Con_Printf("InfoString_GetValue: no room in value\n");
1675 if (strchr(key, '\\'))
1677 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1680 if (strchr(key, '\"'))
1682 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1687 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1690 while (buffer[pos] == '\\')
1692 if (!memcmp(buffer + pos+1, key, keylength))
1694 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1696 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1697 value[j] = buffer[pos+j];
1701 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1702 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1704 // if we reach this point the key was not found
1707 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1715 keylength = strlen(key);
1716 if (strchr(key, '\\') || strchr(value, '\\'))
1718 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1721 if (strchr(key, '\"') || strchr(value, '\"'))
1723 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1728 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1731 while (buffer[pos] == '\\')
1733 if (!memcmp(buffer + pos+1, key, keylength))
1735 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1736 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1738 // if we found the key, find the end of it because we will be replacing it
1740 if (buffer[pos] == '\\')
1742 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1743 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1745 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1747 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1750 if (value && value[0])
1752 // set the key/value and append the remaining text
1753 char tempbuffer[4096];
1754 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1755 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
1759 // just remove the key from the text
1760 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1764 void InfoString_Print(char *buffer)
1771 if (*buffer != '\\')
1773 Con_Printf("InfoString_Print: corrupt string\n");
1776 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1777 if (i < (int)sizeof(key)-1)
1780 if (*buffer != '\\')
1782 Con_Printf("InfoString_Print: corrupt string\n");
1785 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1786 if (i < (int)sizeof(value)-1)
1787 value[i++] = *buffer;
1789 // empty value is an error case
1790 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1794 //========================================================
1795 // strlcat and strlcpy, from OpenBSD
1798 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1800 * Permission to use, copy, modify, and distribute this software for any
1801 * purpose with or without fee is hereby granted, provided that the above
1802 * copyright notice and this permission notice appear in all copies.
1804 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1805 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1806 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1807 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1808 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1809 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1810 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1813 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
1814 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
1817 #ifndef HAVE_STRLCAT
1819 strlcat(char *dst, const char *src, size_t siz)
1821 register char *d = dst;
1822 register const char *s = src;
1823 register size_t n = siz;
1826 /* Find the end of dst and adjust bytes left but don't go past end */
1827 while (n-- != 0 && *d != '\0')
1833 return(dlen + strlen(s));
1834 while (*s != '\0') {
1843 return(dlen + (s - src)); /* count does not include NUL */
1845 #endif // #ifndef HAVE_STRLCAT
1848 #ifndef HAVE_STRLCPY
1850 strlcpy(char *dst, const char *src, size_t siz)
1852 register char *d = dst;
1853 register const char *s = src;
1854 register size_t n = siz;
1856 /* Copy as many bytes as will fit */
1857 if (n != 0 && --n != 0) {
1859 if ((*d++ = *s++) == 0)
1864 /* Not enough room in dst, add NUL and traverse rest of src */
1867 *d = '\0'; /* NUL-terminate dst */
1872 return(s - src - 1); /* count does not include NUL */
1875 #endif // #ifndef HAVE_STRLCPY