]>
git.zerfleddert.de Git - micropolis/blob - src/tclx/src/tclxbsrc.c
4 * Extended Tcl binary file search command.
5 *-----------------------------------------------------------------------------
6 * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies. Karl Lehenbauer and
11 * Mark Diekhans make no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
14 *-----------------------------------------------------------------------------
15 * $Id: tclXbsearch.c,v 2.0 1992/10/16 04:50:24 markd Rel $
16 *-----------------------------------------------------------------------------
22 * Control block used to pass data used by the binary search routines.
24 typedef struct binSearchCB_t
{
25 Tcl_Interp
*interp
; /* Pointer to the interpreter. */
26 char *fileHandle
; /* Handle of file. */
27 char *key
; /* The key to search for. */
29 FILE *fileCBPtr
; /* Open file structure. */
30 dynamicBuf_t dynBuf
; /* Dynamic buffer to hold a line of file. */
31 long lastRecOffset
; /* Offset of last record read. */
32 int cmpResult
; /* -1, 0 or 1 result of string compare. */
33 char *tclProc
; /* Name of Tcl comparsion proc, or NULL. */
37 * Prototypes of internal functions.
40 StandardKeyCompare
_ANSI_ARGS_((char *key
,
44 TclProcKeyCompare
_ANSI_ARGS_((binSearchCB_t
*searchCBPtr
));
47 ReadAndCompare
_ANSI_ARGS_((long fileOffset
,
48 binSearchCB_t
*searchCBPtr
));
51 BinSearch
_ANSI_ARGS_((binSearchCB_t
*searchCBPtr
));
54 *-----------------------------------------------------------------------------
56 * StandardKeyCompare --
57 * Standard comparison routine for BinSearch, compares the key to the
58 * first white-space seperated field in the line.
61 * o key (I) - The key to search for.
62 * o line (I) - The line to compare the key to.
65 * o < 0 if key < line-key
66 * o = 0 if key == line-key
67 * o > 0 if key > line-key.
68 *-----------------------------------------------------------------------------
71 StandardKeyCompare (key
, line
)
75 int cmpResult
, fieldLen
;
78 fieldLen
= strcspn (line
, " \t\r\n\v\f");
80 saveChar
= line
[fieldLen
];
82 cmpResult
= strcmp (key
, line
);
83 line
[fieldLen
] = saveChar
;
89 *-----------------------------------------------------------------------------
91 * TclProcKeyCompare --
92 * Comparison routine for BinSearch that runs a Tcl procedure to,
93 * compare the key to a line from the file.
96 * o searchCBPtr (I/O) - The search control block, the line should be in
97 * dynBuf, the comparsion result is returned in cmpResult.
100 * TCL_OK or TCL_ERROR.
101 *-----------------------------------------------------------------------------
104 TclProcKeyCompare (searchCBPtr
)
105 binSearchCB_t
*searchCBPtr
;
111 cmdArgv
[0] = searchCBPtr
->tclProc
;
112 cmdArgv
[1] = searchCBPtr
->key
;
113 cmdArgv
[2] = searchCBPtr
->dynBuf
.ptr
;
114 command
= Tcl_Merge (3, cmdArgv
);
116 result
= Tcl_Eval (searchCBPtr
->interp
, command
, 0, (char **) NULL
);
119 if (result
== TCL_ERROR
)
122 if (!Tcl_StrToInt (searchCBPtr
->interp
->result
, 0,
123 &searchCBPtr
->cmpResult
)) {
124 char *oldResult
= ckalloc (strlen (searchCBPtr
->interp
->result
+ 1));
126 strcpy (oldResult
, searchCBPtr
->interp
->result
);
127 Tcl_ResetResult (searchCBPtr
->interp
);
128 Tcl_AppendResult (searchCBPtr
->interp
, "invalid integer \"", oldResult
,
129 "\" returned from compare proc \"",
130 searchCBPtr
->tclProc
, "\"", (char *) NULL
);
134 Tcl_ResetResult (searchCBPtr
->interp
);
139 *-----------------------------------------------------------------------------
142 * Search for the next line in the file starting at the specified
143 * offset. Read the line into the dynamic buffer and compare it to
144 * the key using the specified comparison method. The start of the
145 * last line read is saved in the control block, and if the start of
146 * the same line is found in the search, then it will not be recompared.
147 * This is needed since the search algorithm has to hit the same line
148 * a couple of times before failing, due to the fact that the records are
152 * o fileOffset (I) - The offset of the next byte of the search, not
153 * necessarly the start of a record.
154 * o searchCBPtr (I/O) - The search control block, the comparsion result
155 * is returned in cmpResult. If the EOF is hit, a less-than result is
159 * TCL_OK or TCL_ERROR.
160 *-----------------------------------------------------------------------------
163 ReadAndCompare (fileOffset
, searchCBPtr
)
165 binSearchCB_t
*searchCBPtr
;
169 if (fseek (searchCBPtr
->fileCBPtr
, fileOffset
, SEEK_SET
) != 0)
173 * Go to beginning of next line.
176 if (fileOffset
!= 0) {
177 while (((recChar
= getc (searchCBPtr
->fileCBPtr
)) != EOF
) &&
180 if ((recChar
== EOF
) && ferror (searchCBPtr
->fileCBPtr
))
184 * If this is the same line as before, then just leave the comparison
187 if (fileOffset
== searchCBPtr
->lastRecOffset
)
190 searchCBPtr
->lastRecOffset
= fileOffset
;
192 status
= Tcl_DynamicFgets (&searchCBPtr
->dynBuf
, searchCBPtr
->fileCBPtr
,
198 * Only compare if EOF was not hit, otherwise, treat as if we went
199 * above the key we are looking for.
202 searchCBPtr
->cmpResult
= -1;
206 if (searchCBPtr
->tclProc
== NULL
) {
207 searchCBPtr
->cmpResult
= StandardKeyCompare (searchCBPtr
->key
,
208 searchCBPtr
->dynBuf
.ptr
);
210 if (TclProcKeyCompare (searchCBPtr
) != TCL_OK
)
217 Tcl_AppendResult (searchCBPtr
->interp
, searchCBPtr
->fileHandle
, ": ",
218 Tcl_UnixError (searchCBPtr
->interp
), (char *) NULL
);
223 *-----------------------------------------------------------------------------
226 * Binary search a sorted ASCII file.
229 * o searchCBPtr (I/O) - The search control block, if the line is found,
230 * it is returned in dynBuf.
232 * TCL_OK - If the key was found.
233 * TCL_BREAK - If it was not found.
234 * TCL_ERROR - If there was an error.
236 * based on getpath.c from smail 2.5 (9/15/87)
238 *-----------------------------------------------------------------------------
241 BinSearch (searchCBPtr
)
242 binSearchCB_t
*searchCBPtr
;
245 long middle
, high
, low
;
248 if (TclGetOpenFile (searchCBPtr
->interp
, searchCBPtr
->fileHandle
,
252 searchCBPtr
->fileCBPtr
= filePtr
->f
;
253 searchCBPtr
->lastRecOffset
= -1;
255 if (fstat (fileno (searchCBPtr
->fileCBPtr
), &statBuf
) < 0)
259 high
= statBuf
.st_size
;
262 * "Binary search routines are never written right the first time around."
263 * - Robert G. Sheldon.
267 middle
= (high
+ low
+ 1) / 2;
269 if (ReadAndCompare (middle
, searchCBPtr
) != TCL_OK
)
272 if (searchCBPtr
->cmpResult
== 0)
273 return TCL_OK
; /* Found */
276 return TCL_BREAK
; /* Failure */
281 if (searchCBPtr
->cmpResult
> 0) {
289 Tcl_AppendResult (searchCBPtr
->interp
, searchCBPtr
->fileHandle
, ": ",
290 Tcl_UnixError (searchCBPtr
->interp
), (char *) NULL
);
295 *-----------------------------------------------------------------------------
298 * Implements the TCL bsearch command:
299 * bsearch filehandle key [retvar]
302 * Standard TCL results.
304 *-----------------------------------------------------------------------------
307 Tcl_BsearchCmd (clientData
, interp
, argc
, argv
)
308 ClientData clientData
;
314 binSearchCB_t searchCB
;
316 if ((argc
< 3) || (argc
> 5)) {
317 Tcl_AppendResult (interp
, tclXWrongArgs
, argv
[0],
318 " handle key [retvar] [compare_proc]"
323 searchCB
.interp
= interp
;
324 searchCB
.fileHandle
= argv
[1];
325 searchCB
.key
= argv
[2];
326 searchCB
.tclProc
= (argc
== 5) ? argv
[4] : NULL
;
327 Tcl_DynBufInit (&searchCB
.dynBuf
);
329 status
= BinSearch (&searchCB
);
330 if (status
== TCL_ERROR
) {
331 Tcl_DynBufFree (&searchCB
.dynBuf
);
335 if (status
== TCL_BREAK
) {
336 Tcl_DynBufFree (&searchCB
.dynBuf
);
337 if ((argc
>= 4) && (argv
[3][0] != '\0'))
338 interp
->result
= "0";
342 if ((argc
== 3) || (argv
[3][0] == '\0')) {
343 Tcl_DynBufReturn (interp
, &searchCB
.dynBuf
);
347 varPtr
= Tcl_SetVar (interp
, argv
[3], searchCB
.dynBuf
.ptr
,
349 Tcl_DynBufFree (&searchCB
.dynBuf
);
352 interp
->result
= "1";