NCBI C++ ToolKit
rpc.c
Go to the documentation of this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 1998, 1999, 2000, 2001 Brian Bruns
3  * Copyright (C) 2002, 2003, 2004, 2005 James K. Lowden
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 #include <config.h>
22 
23 #include <stdarg.h>
24 #include <stdio.h>
25 
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif /* HAVE_UNISTD_H */
29 
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif /* HAVE_STDLIB_H */
33 
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif /* HAVE_STRING_H */
37 
38 #if HAVE_ERRNO_H
39 # include <errno.h>
40 #endif /* HAVE_ERRNO_H */
41 
42 #include <assert.h>
43 
44 #include <freetds/tds.h>
45 #include <freetds/convert.h>
46 #include <freetds/string.h>
47 #include <replacements.h>
48 #include <sybfront.h>
49 #include <sybdb.h>
50 #include <dblib.h>
51 
52 static void rpc_clear(DBREMOTE_PROC * rpc);
53 static void param_clear(DBREMOTE_PROC_PARAM * pparam);
54 
56 
57 /**
58  * \ingroup dblib_rpc
59  * \brief Initialize a remote procedure call.
60  *
61  * \param dbproc contains all information needed by db-lib to manage communications with the server.
62  * \param rpcname name of the stored procedure to be run.
63  * \param options Only supported option would be DBRPCRECOMPILE,
64  * which causes the stored procedure to be recompiled before executing.
65  * \remark The RPC functions are the only way to get back OUTPUT parameter data with db-lib
66  * from modern Microsoft servers.
67  * \retval SUCCEED normal.
68  * \retval FAIL on error
69  * \sa dbrpcparam(), dbrpcsend()
70  */
71 RETCODE
72 dbrpcinit(DBPROCESS * dbproc, const char rpcname[], DBSMALLINT options)
73 {
74  DBREMOTE_PROC **rpc;
75 
76  tdsdump_log(TDS_DBG_FUNC, "dbrpcinit(%p, %s, %d)\n", dbproc, rpcname, options);
78  CHECK_NULP(rpcname, "dbrpcinit", 2, FAIL);
79 
80  /*
81  * TODO: adhere to docs. Only Microsoft supports DBRPCRESET. They say:
82  * "Cancels a single stored procedure or a batch of stored procedures.
83  * If rpcname is specified, that new stored procedure is initialized after the cancellation is complete."
84  */
85  if (options & DBRPCRESET) {
87  dbproc->rpc = NULL;
88  return SUCCEED;
89  }
90 
91  /* any bits we want from the options argument */
92  /* dbrpcrecompile = options & DBRPCRECOMPILE; */
93  options &= ~DBRPCRECOMPILE; /* turn that one off, now that we've extracted it */
94 
95  /* all other options except DBRPCRECOMPILE are invalid */
96  DBPERROR_RETURN3(options, SYBEIPV, (int) options, "options", "dbrpcinit");
97 
98  /* find a free node */
99  for (rpc = &dbproc->rpc; *rpc != NULL; rpc = &(*rpc)->next) {
100  /* check existing nodes for name match (there shouldn't be one) */
101  if ((*rpc)->name == NULL || strcmp((*rpc)->name, rpcname) == 0) {
102  tdsdump_log(TDS_DBG_INFO1, "error: dbrpcinit called twice for procedure \"%s\"\n", rpcname);
103  return FAIL;
104  }
105  }
106 
107  /* rpc now contains the address of the dbproc's first empty (null) DBREMOTE_PROC* */
108 
109  /* allocate */
110  if ((*rpc = tds_new0(DBREMOTE_PROC, 1)) == NULL) {
111  dbperror(dbproc, SYBEMEM, errno);
112  return FAIL;
113  }
114 
115  if (((*rpc)->name = strdup(rpcname)) == NULL) {
116  free(*rpc);
117  *rpc = NULL;
118  dbperror(dbproc, SYBEMEM, errno);
119  return FAIL;
120  }
121 
122  /* store */
123  (*rpc)->options = options & DBRPCRECOMPILE;
124  (*rpc)->param_list = NULL;
125 
126  /* completed */
127  tdsdump_log(TDS_DBG_INFO1, "dbrpcinit() added rpcname \"%s\"\n", rpcname);
128 
129  return SUCCEED;
130 }
131 
132 /**
133  * \ingroup dblib_rpc
134  * \brief Add a parameter to a remote procedure call.
135  *
136  * Call between dbrpcinit() and dbrpcsend()
137  * \param dbproc contains all information needed by db-lib to manage communications with the server.
138  * \param paramname literal name of the parameter, according to the stored procedure (starts with '@'). Optional.
139  * If not used, parameters will be passed in order instead of by name.
140  * \param status must be DBRPCRETURN, if this parameter is a return parameter, else 0.
141  * \param type datatype of the value parameter e.g., SYBINT4, SYBCHAR.
142  * \param maxlen Maximum output size of the parameter's value to be returned by the stored procedure,
143  * usually the size of your host variable.
144  * Fixed-length datatypes take -1 (NULL or not).
145  * Non-OUTPUT parameters also use -1.
146  * Use 0 to send a NULL value for a variable length datatype.
147  * \param datalen For variable-length datatypes, the byte size of the data to be sent, exclusive of any null terminator.
148  * For fixed-length datatypes use -1. To send a NULL value, use 0.
149  * \param value Address of your host variable.
150  * \retval SUCCEED normal.
151  * \retval FAIL on error
152  * \sa dbrpcinit(), dbrpcsend()
153  */
154 RETCODE
155 dbrpcparam(DBPROCESS * dbproc, const char paramname[], BYTE status, int db_type, DBINT maxlen, DBINT datalen, BYTE * value)
156 {
157  char *name = NULL;
158  DBREMOTE_PROC *rpc;
159  DBREMOTE_PROC_PARAM **pparam;
160  DBREMOTE_PROC_PARAM *param;
162 
163  tdsdump_log(TDS_DBG_FUNC, "dbrpcparam(%p, %s, 0x%x, %d, %d, %d, %p)\n",
164  dbproc, paramname, status, db_type, maxlen, datalen, value);
165  CHECK_CONN(FAIL);
167 
169  type = (TDS_SERVER_TYPE) db_type;
170 
171  /* validate datalen parameter */
172 
173  if (is_fixed_type(type)) {
174  if (datalen != 0)
175  datalen = -1;
176  } else { /* Sybooks: "Passing datalen as -1 for any of these [non-fixed] datatypes results
177  * in the DBPROCESS referenced by dbproc being marked as "dead," or unusable."
178  */
179  DBPERROR_RETURN(datalen < 0, SYBERPIL);
180  }
181 
182  /* "value parameter for dbprcparam() can be NULL, only if the datalen parameter is 0." */
183  DBPERROR_RETURN(value == NULL && datalen != 0, SYBERPNULL);
184 
185  /* nullable types must provide a data length */
186  DBPERROR_RETURN(is_nullable_type(type) && datalen < 0, SYBERPUL);
187 
188  /* validate maxlen parameter */
189 
190  if (status & DBRPCRETURN) {
191  if (is_fixed_type(type)) {
192  maxlen = -1;
193  } else {
194  if (maxlen == -1)
195  maxlen = 255;
196  }
197  } else {
198  /*
199  * Well, maxlen should be used only for output parameter however it seems
200  * that ms implementation wrongly require this 0 for NULL variable
201  * input parameters, so fix it
202  */
203  DBPERROR_RETURN3(maxlen != -1 && maxlen != 0, SYBEIPV, (int) maxlen, "maxlen", "dbrpcparam");
204  maxlen = -1;
205  }
206 
207  /* end validation */
208 
209  /* This trick is to allow for client using utf8 to insert any character into a NVARCHAR parameter
210  * The 4000 check is to allow varchar with more then 4000 characters (varchar is limited to 8000
211  * characters) which can't be converted to nvarchar (which is limited to 4000 character)
212  */
214  && maxlen <= 4000 && datalen <= 4000)
215  type = XSYBNVARCHAR;
216 
217  /* allocate */
218  param = tds_new(DBREMOTE_PROC_PARAM, 1);
219  if (param == NULL) {
220  dbperror(dbproc, SYBEMEM, 0);
221  return FAIL;
222  }
223 
224  if (paramname) {
225  name = strdup(paramname);
226  if (name == NULL) {
227  free(param);
228  dbperror(dbproc, SYBEMEM, 0);
229  return FAIL;
230  }
231  }
232 
233  /* initialize */
234  param->next = NULL; /* NULL signifies end of linked list */
235  param->name = name;
236  param->status = status;
237  param->type = type;
238  param->maxlen = maxlen;
239  param->datalen = datalen;
240 
241  /*
242  * If datalen = 0, value parameter is ignored.
243  * This is one way to specify a NULL input parameter.
244  */
245  if (datalen == 0)
246  param->value = NULL;
247  else
248  param->value = value;
249 
250  /*
251  * Add a parameter to the current rpc.
252  *
253  * Traverse the dbproc's procedure list to find the current rpc,
254  * then traverse the parameter linked list until its end,
255  * then tack on our parameter's address.
256  */
257  for (rpc = dbproc->rpc; rpc->next != NULL; rpc = rpc->next) /* find "current" procedure */
258  continue;
259  for (pparam = &rpc->param_list; *pparam != NULL; pparam = &(*pparam)->next)
260  continue;
261 
262  /* pparam now contains the address of the end of the rpc's parameter list */
263 
264  *pparam = param; /* add to the end of the list */
265 
266  tdsdump_log(TDS_DBG_INFO1, "dbrpcparam() added parameter \"%s\"\n", (paramname) ? paramname : "");
267 
268  return SUCCEED;
269 }
270 
271 /**
272  * \ingroup dblib_rpc
273  * \brief Execute the procedure and free associated memory
274  *
275  * \param dbproc contains all information needed by db-lib to manage communications with the server.
276  * \retval SUCCEED normal.
277  * \retval FAIL on error
278  * \sa dbrpcinit(), dbrpcparam()
279  */
280 RETCODE
282 {
283  DBREMOTE_PROC *rpc;
284 
285  tdsdump_log(TDS_DBG_FUNC, "dbrpcsend(%p)\n", dbproc);
286  CHECK_CONN(FAIL);
287  CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL); /* dbrpcinit should allocate pointer */
288 
289  /* sanity */
290  if (dbproc->rpc->name == NULL) { /* can't be ready without a name */
291  tdsdump_log(TDS_DBG_INFO1, "returning FAIL: name is NULL\n");
292  return FAIL;
293  }
294 
296 
297  for (rpc = dbproc->rpc; rpc != NULL; rpc = rpc->next) {
298  TDSRET erc;
299  TDSPARAMINFO *pparam_info = NULL;
300 
301  /*
302  * liam@inodes.org: allow stored procedures to have no paramaters
303  */
304  if (rpc->param_list != NULL) {
305  pparam_info = param_info_alloc(dbproc->tds_socket, rpc);
306  if (!pparam_info)
307  return FAIL;
308  }
309  erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info, NULL);
310  tds_free_param_results(pparam_info);
311  if (TDS_FAILED(erc)) {
312  tdsdump_log(TDS_DBG_INFO1, "returning FAIL: tds_submit_rpc() failed\n");
313  return FAIL;
314  }
315  }
316 
317  /* free up the memory */
318  rpc_clear(dbproc->rpc);
319  dbproc->rpc = NULL;
320 
321  tdsdump_log(TDS_DBG_FUNC, "dbrpcsend() returning SUCCEED\n");
322 
323  return SUCCEED;
324 }
325 
326 /**
327  * Tell the TDSPARAMINFO structure where the data go. This is a kind of "bind" operation.
328  */
329 static const unsigned char *
330 param_row_alloc(TDSPARAMINFO * params, TDSCOLUMN * curcol, int param_num, void *value, int size)
331 {
332  const void *row = tds_alloc_param_data(curcol);
333  tdsdump_log(TDS_DBG_INFO1, "parameter size = %d, data = %p, row_size = %d\n",
334  size, curcol->column_data, params->row_size);
335  if (!row)
336  return NULL;
337  if (size > 0 && value) {
338  tdsdump_log(TDS_DBG_FUNC, "copying %d bytes of data to parameter #%d\n", size, param_num);
339  if (!is_blob_col(curcol)) {
340  if (is_numeric_type(curcol->column_type))
341  memset(curcol->column_data, 0, sizeof(TDS_NUMERIC));
342  memcpy(curcol->column_data, value, size);
343  } else {
344  TDSBLOB *blob = (TDSBLOB *) curcol->column_data;
345  blob->textvalue = tds_new(TDS_CHAR, size);
346  tdsdump_log(TDS_DBG_FUNC, "blob parameter supported, size %d textvalue pointer is %p\n",
347  size, blob->textvalue);
348  if (!blob->textvalue)
349  return NULL;
350  memcpy(blob->textvalue, value, size);
351  }
352  }
353  else {
354  tdsdump_log(TDS_DBG_FUNC, "setting parameter #%d to NULL\n", param_num);
355  curcol->column_cur_size = -1;
356  }
357 
358  return (const unsigned char*) row;
359 }
360 
361 /**
362  * Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
363  */
364 static TDSPARAMINFO *
366 {
367  int i;
369  TDSCOLUMN *pcol;
370  TDSPARAMINFO *params = NULL, *new_params;
371  BYTE *temp_value;
372  int temp_datalen;
373  TDS_SERVER_TYPE temp_type;
374  int param_is_null;
375 
376  /* sanity */
377  if (rpc == NULL)
378  return NULL;
379 
380  /* see v 1.10 2002/11/23 for first broken attempt */
381 
382  for (i = 0, p = rpc->param_list; p != NULL; p = p->next, i++) {
383  const unsigned char *prow;
384 
385  if (!(new_params = tds_alloc_param_result(params))) {
386  tds_free_param_results(params);
387  tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
388  return NULL;
389  }
390  params = new_params;
391 
392  /*
393  * Determine whether an input parameter is NULL or not.
394  */
395  param_is_null = 0;
396  temp_type = p->type;
397  temp_value = p->value;
398  temp_datalen = p->datalen;
399 
400  if (p->datalen == 0)
401  param_is_null = 1;
402 
403  tdsdump_log(TDS_DBG_INFO1, "parm_info_alloc(): parameter null-ness = %d\n", param_is_null);
404 
405  pcol = params->columns[i];
406 
407  if (temp_value && is_numeric_type(temp_type)) {
408  DBDECIMAL *dec = (DBDECIMAL*) temp_value;
409  pcol->column_prec = dec->precision;
410  pcol->column_scale = dec->scale;
411  if (dec->precision > 0 && dec->precision <= MAXPRECISION)
412  temp_datalen = tds_numeric_bytes_per_prec[dec->precision] + 2;
413  }
414  if (param_is_null || (p->status & DBRPCRETURN)) {
415  if (param_is_null) {
416  temp_datalen = 0;
417  temp_value = NULL;
418  } else if (is_fixed_type(temp_type)) {
419  temp_datalen = tds_get_size_by_type(temp_type);
420  }
421  temp_type = tds_get_null_type(temp_type);
422  } else if (is_fixed_type(temp_type)) {
423  temp_datalen = tds_get_size_by_type(temp_type);
424  }
425 
426  /* meta data */
427  if (p->name)
428  if (!tds_dstr_copy(&pcol->column_name, p->name)) {
429  tds_free_param_results(params);
430  tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
431  return NULL;
432  }
433 
434  tds_set_param_type(tds->conn, pcol, temp_type);
435 
436  if (p->maxlen > 0)
437  pcol->column_size = p->maxlen;
438  else {
439  if (is_fixed_type(p->type)) {
441  } else {
442  pcol->column_size = p->datalen;
443  }
444  }
445  if (p->type == XSYBNVARCHAR)
446  pcol->column_size *= 2;
447  pcol->on_server.column_size = pcol->column_size;
448 
449  pcol->column_output = p->status;
450  pcol->column_cur_size = temp_datalen;
451 
452  prow = param_row_alloc(params, pcol, i, temp_value, temp_datalen);
453 
454  if (!prow) {
455  tds_free_param_results(params);
456  tdsdump_log(TDS_DBG_ERROR, "out of memory for rpc row!");
457  return NULL;
458  }
459 
460  }
461 
462  return params;
463 
464 }
465 
466 /**
467  * erase the procedure list
468  */
469 static void
471 {
473 
474  while (rpc) {
475  next = rpc->next;
476  param_clear(rpc->param_list);
477  free(rpc->name);
478  free(rpc);
479  rpc = next;
480  }
481 }
482 
483 /**
484  * erase the parameter list
485  */
486 static void
488 {
490 
491  while (pparam) {
492  next = pparam->next;
493  free(pparam->name);
494  /* free self */
495  free(pparam);
496  pparam = next;
497  }
498 }
static TDSSOCKET * tds
Definition: collations.c:37
char value[7]
Definition: config.c:431
static void param_clear(DBREMOTE_PROC_PARAM *pparam)
erase the parameter list
Definition: rpc.c:487
static TDSPARAMINFO * param_info_alloc(TDSSOCKET *tds, DBREMOTE_PROC *rpc)
Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
Definition: rpc.c:365
static const unsigned char * param_row_alloc(TDSPARAMINFO *params, TDSCOLUMN *curcol, int param_num, void *value, int size)
Tell the TDSPARAMINFO structure where the data go.
Definition: rpc.c:330
static void rpc_clear(DBREMOTE_PROC *rpc)
erase the procedure list
Definition: rpc.c:470
#define DBPERROR_RETURN(x, msg)
Definition: dblib.h:196
#define CHECK_NULP(x, func, param_num, ret)
Definition: dblib.h:194
@ _DB_RES_INIT
Definition: dblib.h:34
#define CHECK_PARAMETER(x, msg, ret)
Definition: dblib.h:193
#define CHECK_CONN(ret)
Definition: dblib.h:198
#define DBPERROR_RETURN3(x, msg, a, b, c)
Definition: dblib.h:197
static DLIST_TYPE *DLIST_NAME() next(DLIST_LIST_TYPE *list, DLIST_TYPE *item)
Definition: dlist.tmpl.h:56
static int type
Definition: getdata.c:31
#define NULL
Definition: ncbistd.hpp:225
RETCODE dbrpcinit(DBPROCESS *dbproc, const char rpcname[], DBSMALLINT options)
Initialize a remote procedure call.
Definition: rpc.c:72
RETCODE dbrpcparam(DBPROCESS *dbproc, const char paramname[], BYTE status, int db_type, DBINT maxlen, DBINT datalen, BYTE *value)
Add a parameter to a remote procedure call.
Definition: rpc.c:155
RETCODE dbrpcsend(DBPROCESS *dbproc)
Execute the procedure and free associated memory.
Definition: rpc.c:281
DSTR * tds_dstr_copy(DSTR *s, const char *src) TDS_WUR
copy a string from another
Definition: tdsstring.c:123
int i
const struct ncbi::grid::netcache::search::fields::SIZE size
int strcmp(const char *str1, const char *str2)
Definition: odbc_utils.hpp:160
#define strdup
Definition: ncbi_ansi_ext.h:70
TDS_SERVER_TYPE
Definition: proto.h:161
@ XSYBNVARCHAR
Definition: proto.h:195
#define dbperror
#define tds_set_param_type
#define tds_free_param_results
#define tds_get_size_by_type
#define tds_alloc_param_result
#define tds_submit_rpc
#define tds_numeric_bytes_per_prec
#define tds_get_null_type
#define tds_alloc_param_data
unsigned char scale
Definition: sybdb.h:279
unsigned char precision
Definition: sybdb.h:278
TDS_SERVER_TYPE type
Definition: dblib.h:92
struct _DBREMOTE_PROC_PARAM * next
Definition: dblib.h:88
struct _DBREMOTE_PROC * next
Definition: dblib.h:100
char * name
Definition: dblib.h:102
DBREMOTE_PROC_PARAM * param_list
Definition: dblib.h:104
Information about blobs (e.g.
Definition: tds.h:658
TDS_CHAR * textvalue
Definition: tds.h:659
Metadata about columns in regular and compute rows.
Definition: tds.h:761
TDS_INT column_size
maximun size of data.
Definition: tds.h:766
DSTR column_name
Definition: tds.h:787
unsigned char * column_data
Definition: tds.h:793
TDS_TINYINT column_prec
precision for decimal/numeric
Definition: tds.h:775
TDS_SERVER_TYPE column_type
This type can be different from wire type because conversion (e.g.
Definition: tds.h:768
TDS_TINYINT column_scale
scale for decimal/numeric
Definition: tds.h:776
unsigned int column_output
Definition: tds.h:800
struct tds_column::@124 on_server
TDS_INT column_cur_size
size written in variable (ie: char, text, binary).
Definition: tds.h:811
TDSSOCKET * tds_socket
Definition: dblib.h:122
DBREMOTE_PROC * rpc
Definition: dblib.h:143
DB_RESULT_STATE dbresults_state
Definition: dblib.h:129
Hold information for any results.
Definition: tds.h:842
TDSCOLUMN ** columns
Definition: tds.h:844
TDS_INT row_size
Definition: tds.h:851
Information for a server connection.
Definition: tds.h:1211
TDSCONNECTION conn[1]
Definition: tds.h:1215
Definition: type.c:6
#define DBRPCRESET
Definition: sybdb.h:598
#define SYBEMEM
Definition: sybdb.h:938
#define SYBEUDTY
Definition: sybdb.h:988
#define SYBVARCHAR
Definition: sybdb.h:162
int RETCODE
Definition: sybdb.h:121
#define DBRPCRECOMPILE
Definition: sybdb.h:597
#define SYBEIPV
Definition: sybdb.h:1122
unsigned char BYTE
Definition: sybdb.h:334
#define SYBERPUL
Definition: sybdb.h:1042
Int2 DBSMALLINT
Definition: sybdb.h:254
#define SYBERPIL
Definition: sybdb.h:1041
#define SUCCEED
Definition: sybdb.h:585
#define SYBERPCS
Definition: sybdb.h:1114
#define FAIL
Definition: sybdb.h:586
#define DBRPCRETURN
Definition: sybdb.h:577
#define SYBERPNULL
Definition: sybdb.h:1136
Int4 DBINT
Definition: sybdb.h:255
DBPROCESS * dbproc
Definition: t0013.c:18
Main include file for libtds.
#define tds_new(type, n)
Definition: tds.h:1392
#define is_numeric_type(x)
Definition: tds.h:454
#define TDS_FAILED(rc)
Definition: tds.h:206
#define is_fixed_type(x)
Definition: tds.h:438
#define tdsdump_log
Definition: tds.h:1561
#define TDS_DBG_INFO1
Definition: tds.h:900
static bool is_tds_type_valid(int type)
Definition: tds.h:463
#define is_blob_col(x)
Definition: tds.h:445
#define IS_TDS7_PLUS(x)
Definition: tds.h:1708
#define tds_new0(type, n)
Definition: tds.h:1393
char TDS_CHAR
Definition: tds.h:144
#define is_nullable_type(x)
Definition: tds.h:439
int TDSRET
Definition: tds.h:201
#define TDS_DBG_ERROR
Definition: tds.h:903
#define MAXPRECISION
Definition: tds.h:470
#define TDS_DBG_FUNC
Definition: tds.h:898
void free(voidpf ptr)
Modified on Tue Apr 09 07:58:40 2024 by modify_doxy.py rev. 669887
Modified on Wed Apr 10 07:34:00 2024 by modify_doxy.py rev. 669887
Modified on Thu Apr 11 15:10:59 2024 by modify_doxy.py rev. 669887
Modified on Fri Apr 12 17:20:05 2024 by modify_doxy.py rev. 669887
Modified on Sat Apr 13 11:47:43 2024 by modify_doxy.py rev. 669887
Modified on Sun Apr 14 05:27:39 2024 by modify_doxy.py rev. 669887
Modified on Tue Apr 16 20:11:57 2024 by modify_doxy.py rev. 669887
Modified on Wed Apr 17 13:09:53 2024 by modify_doxy.py rev. 669887