Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

chared.c

Go to the documentation of this file.
00001 /*      $NetBSD: chared.c,v 1.14 2001/05/17 01:02:17 christos Exp $     */
00002 
00003 /*-
00004  * Copyright (c) 1992, 1993
00005  *      The Regents of the University of California.  All rights reserved.
00006  *
00007  * This code is derived from software contributed to Berkeley by
00008  * Christos Zoulas of Cornell University.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted provided that the following conditions
00012  * are met:
00013  * 1. Redistributions of source code must retain the above copyright
00014  *    notice, this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in the
00017  *    documentation and/or other materials provided with the distribution.
00018  * 3. All advertising materials mentioning features or use of this software
00019  *    must display the following acknowledgement:
00020  *      This product includes software developed by the University of
00021  *      California, Berkeley and its contributors.
00022  * 4. Neither the name of the University nor the names of its contributors
00023  *    may be used to endorse or promote products derived from this software
00024  *    without specific prior written permission.
00025  *
00026  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00027  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00028  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00029  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00030  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00031  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00032  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00033  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00034  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00035  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00036  * SUCH DAMAGE.
00037  */
00038 
00039 #include "compat.h"
00040 #if !defined(lint) && !defined(SCCSID)
00041 #if 0
00042 static char sccsid[] = "@(#)chared.c    8.1 (Berkeley) 6/4/93";
00043 #else
00044 __RCSID("$NetBSD: chared.c,v 1.14 2001/05/17 01:02:17 christos Exp $");
00045 #endif
00046 #endif /* not lint && not SCCSID */
00047 
00048 /*
00049  * chared.c: Character editor utilities
00050  */
00051 #include "sys.h"
00052 
00053 #include <stdlib.h>
00054 #include "el.h"
00055 
00056 /* value to leave unused in line buffer */
00057 #define EL_LEAVE        2
00058 
00059 /* cv_undo():
00060  *      Handle state for the vi undo command
00061  */
00062 protected void
00063 cv_undo(EditLine *el,int action, size_t size, char *ptr)
00064 {
00065         c_undo_t *vu = &el->el_chared.c_undo;
00066         vu->action = action;
00067         vu->ptr    = ptr;
00068         vu->isize  = size;
00069         (void) memcpy(vu->buf, vu->ptr, size);
00070 #ifdef DEBUG_UNDO
00071         (void) fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n",
00072                vu->ptr, vu->isize, vu->dsize);
00073 #endif
00074 }
00075 
00076 
00077 /* c_insert():
00078  *      Insert num characters
00079  */
00080 protected void
00081 c_insert(EditLine *el, int num)
00082 {
00083         char *cp;
00084 
00085         if (el->el_line.lastchar + num >= el->el_line.limit)
00086                 return;                 /* can't go past end of buffer */
00087 
00088         if (el->el_line.cursor < el->el_line.lastchar) {
00089                 /* if I must move chars */
00090                 for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
00091                         cp[num] = *cp;
00092         }
00093         el->el_line.lastchar += num;
00094 }
00095 
00096 
00097 /* c_delafter():
00098  *      Delete num characters after the cursor
00099  */
00100 protected void
00101 c_delafter(EditLine *el, int num)
00102 {
00103 
00104         if (el->el_line.cursor + num > el->el_line.lastchar)
00105                 num = el->el_line.lastchar - el->el_line.cursor;
00106 
00107         if (num > 0) {
00108                 char *cp;
00109 
00110                 if (el->el_map.current != el->el_map.emacs)
00111                         cv_undo(el, INSERT, (size_t)num, el->el_line.cursor);
00112 
00113                 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
00114                         *cp = cp[num];
00115 
00116                 el->el_line.lastchar -= num;
00117         }
00118 }
00119 
00120 
00121 /* c_delbefore():
00122  *      Delete num characters before the cursor
00123  */
00124 protected void
00125 c_delbefore(EditLine *el, int num)
00126 {
00127 
00128         if (el->el_line.cursor - num < el->el_line.buffer)
00129                 num = el->el_line.cursor - el->el_line.buffer;
00130 
00131         if (num > 0) {
00132                 char *cp;
00133 
00134                 if (el->el_map.current != el->el_map.emacs)
00135                         cv_undo(el, INSERT, (size_t)num,
00136                             el->el_line.cursor - num);
00137 
00138                 for (cp = el->el_line.cursor - num;
00139                     cp <= el->el_line.lastchar;
00140                     cp++)
00141                         *cp = cp[num];
00142 
00143                 el->el_line.lastchar -= num;
00144         }
00145 }
00146 
00147 
00148 /* ce__isword():
00149  *      Return if p is part of a word according to emacs
00150  */
00151 protected int
00152 ce__isword(int p)
00153 {
00154         return (isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL);
00155 }
00156 
00157 
00158 /* cv__isword():
00159  *      Return if p is part of a word according to vi
00160  */
00161 protected int
00162 cv__isword(int p)
00163 {
00164         return (!isspace(p));
00165 }
00166 
00167 
00168 /* c__prev_word():
00169  *      Find the previous word
00170  */
00171 protected char *
00172 c__prev_word(char *p, char *low, int n, int (*wtest)(int))
00173 {
00174         p--;
00175 
00176         while (n--) {
00177                 while ((p >= low) && !(*wtest)((unsigned char) *p))
00178                         p--;
00179                 while ((p >= low) && (*wtest)((unsigned char) *p))
00180                         p--;
00181         }
00182 
00183         /* cp now points to one character before the word */
00184         p++;
00185         if (p < low)
00186                 p = low;
00187         /* cp now points where we want it */
00188         return (p);
00189 }
00190 
00191 
00192 /* c__next_word():
00193  *      Find the next word
00194  */
00195 protected char *
00196 c__next_word(char *p, char *high, int n, int (*wtest)(int))
00197 {
00198         while (n--) {
00199                 while ((p < high) && !(*wtest)((unsigned char) *p))
00200                         p++;
00201                 while ((p < high) && (*wtest)((unsigned char) *p))
00202                         p++;
00203         }
00204         if (p > high)
00205                 p = high;
00206         /* p now points where we want it */
00207         return (p);
00208 }
00209 
00210 /* cv_next_word():
00211  *      Find the next word vi style
00212  */
00213 protected char *
00214 cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int))
00215 {
00216         int test;
00217 
00218         while (n--) {
00219                 test = (*wtest)((unsigned char) *p);
00220                 while ((p < high) && (*wtest)((unsigned char) *p) == test)
00221                         p++;
00222                 /*
00223                  * vi historically deletes with cw only the word preserving the
00224                  * trailing whitespace! This is not what 'w' does..
00225                  */
00226                 if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
00227                         while ((p < high) && isspace((unsigned char) *p))
00228                                 p++;
00229         }
00230 
00231         /* p now points where we want it */
00232         if (p > high)
00233                 return (high);
00234         else
00235                 return (p);
00236 }
00237 
00238 
00239 /* cv_prev_word():
00240  *      Find the previous word vi style
00241  */
00242 protected char *
00243 cv_prev_word(EditLine *el, char *p, char *low, int n, int (*wtest)(int))
00244 {
00245         int test;
00246 
00247         while (n--) {
00248                 p--;
00249                 /*
00250                  * vi historically deletes with cb only the word preserving the
00251                  * leading whitespace! This is not what 'b' does..
00252                  */
00253                 if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
00254                         while ((p > low) && isspace((unsigned char) *p))
00255                                 p--;
00256                 test = (*wtest)((unsigned char) *p);
00257                 while ((p >= low) && (*wtest)((unsigned char) *p) == test)
00258                         p--;
00259                 p++;
00260                 while (isspace((unsigned char) *p))
00261                         p++;
00262         }
00263 
00264         /* p now points where we want it */
00265         if (p < low)
00266                 return (low);
00267         else
00268                 return (p);
00269 }
00270 
00271 
00272 #ifdef notdef
00273 /* c__number():
00274  *      Ignore character p points to, return number appearing after that.
00275  *      A '$' by itself means a big number; "$-" is for negative; '^' means 1.
00276  *      Return p pointing to last char used.
00277  */
00278 protected char *
00279 c__number(
00280     char *p,    /* character position */
00281     int *num,   /* Return value */
00282     int dval)   /* dval is the number to subtract from like $-3 */
00283 {
00284         int i;
00285         int sign = 1;
00286 
00287         if (*++p == '^') {
00288                 *num = 1;
00289                 return (p);
00290         }
00291         if (*p == '$') {
00292                 if (*++p != '-') {
00293                         *num = 0x7fffffff;      /* Handle $ */
00294                         return (--p);
00295                 }
00296                 sign = -1;                      /* Handle $- */
00297                 ++p;
00298         }
00299         for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
00300                 continue;
00301         *num = (sign < 0 ? dval - i : i);
00302         return (--p);
00303 }
00304 #endif
00305 
00306 /* cv_delfini():
00307  *      Finish vi delete action
00308  */
00309 protected void
00310 cv_delfini(EditLine *el)
00311 {
00312         int size;
00313         int oaction;
00314 
00315         if (el->el_chared.c_vcmd.action & INSERT)
00316                 el->el_map.current = el->el_map.key;
00317 
00318         oaction = el->el_chared.c_vcmd.action;
00319         el->el_chared.c_vcmd.action = NOP;
00320 
00321         if (el->el_chared.c_vcmd.pos == 0)
00322                 return;
00323 
00324 
00325         if (el->el_line.cursor > el->el_chared.c_vcmd.pos) {
00326                 size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos);
00327                 c_delbefore(el, size);
00328                 el->el_line.cursor = el->el_chared.c_vcmd.pos;
00329                 re_refresh_cursor(el);
00330         } else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) {
00331                 size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor);
00332                 c_delafter(el, size);
00333         } else {
00334                 size = 1;
00335                 c_delafter(el, size);
00336         }
00337         switch (oaction) {
00338         case DELETE|INSERT:
00339                 el->el_chared.c_undo.action = DELETE|INSERT;
00340                 break;
00341         case DELETE:
00342                 el->el_chared.c_undo.action = INSERT;
00343                 break;
00344         case NOP:
00345         case INSERT:
00346         default:
00347                 EL_ABORT((el->el_errfile, "Bad oaction %d\n", oaction));
00348                 break;
00349         }
00350 
00351 
00352         el->el_chared.c_undo.ptr = el->el_line.cursor;
00353         el->el_chared.c_undo.dsize = size;
00354 }
00355 
00356 
00357 #ifdef notdef
00358 /* ce__endword():
00359  *      Go to the end of this word according to emacs
00360  */
00361 protected char *
00362 ce__endword(char *p, char *high, int n)
00363 {
00364         p++;
00365 
00366         while (n--) {
00367                 while ((p < high) && isspace((unsigned char) *p))
00368                         p++;
00369                 while ((p < high) && !isspace((unsigned char) *p))
00370                         p++;
00371         }
00372 
00373         p--;
00374         return (p);
00375 }
00376 #endif
00377 
00378 
00379 /* cv__endword():
00380  *      Go to the end of this word according to vi
00381  */
00382 protected char *
00383 cv__endword(char *p, char *high, int n)
00384 {
00385         p++;
00386 
00387         while (n--) {
00388                 while ((p < high) && isspace((unsigned char) *p))
00389                         p++;
00390 
00391                 if (isalnum((unsigned char) *p))
00392                         while ((p < high) && isalnum((unsigned char) *p))
00393                                 p++;
00394                 else
00395                         while ((p < high) && !(isspace((unsigned char) *p) ||
00396                             isalnum((unsigned char) *p)))
00397                                 p++;
00398         }
00399         p--;
00400         return (p);
00401 }
00402 
00403 /* ch_init():
00404  *      Initialize the character editor
00405  */
00406 protected int
00407 ch_init(EditLine *el)
00408 {
00409         el->el_line.buffer              = (char *) el_malloc(EL_BUFSIZ);
00410         if (el->el_line.buffer == NULL)
00411                 return (-1);
00412 
00413         (void) memset(el->el_line.buffer, 0, EL_BUFSIZ);
00414         el->el_line.cursor              = el->el_line.buffer;
00415         el->el_line.lastchar            = el->el_line.buffer;
00416         el->el_line.limit               = &el->el_line.buffer[EL_BUFSIZ - 2];
00417 
00418         el->el_chared.c_undo.buf        = (char *) el_malloc(EL_BUFSIZ);
00419         if (el->el_chared.c_undo.buf == NULL)
00420                 return (-1);
00421         (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
00422         el->el_chared.c_undo.action     = NOP;
00423         el->el_chared.c_undo.isize      = 0;
00424         el->el_chared.c_undo.dsize      = 0;
00425         el->el_chared.c_undo.ptr        = el->el_line.buffer;
00426 
00427         el->el_chared.c_vcmd.action     = NOP;
00428         el->el_chared.c_vcmd.pos        = el->el_line.buffer;
00429         el->el_chared.c_vcmd.ins        = el->el_line.buffer;
00430 
00431         el->el_chared.c_kill.buf        = (char *) el_malloc(EL_BUFSIZ);
00432         if (el->el_chared.c_kill.buf == NULL)
00433                 return (-1);
00434         (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
00435         el->el_chared.c_kill.mark       = el->el_line.buffer;
00436         el->el_chared.c_kill.last       = el->el_chared.c_kill.buf;
00437 
00438         el->el_map.current              = el->el_map.key;
00439 
00440         el->el_state.inputmode          = MODE_INSERT; /* XXX: save a default */
00441         el->el_state.doingarg           = 0;
00442         el->el_state.metanext           = 0;
00443         el->el_state.argument           = 1;
00444         el->el_state.lastcmd            = ED_UNASSIGNED;
00445 
00446         el->el_chared.c_macro.nline     = NULL;
00447         el->el_chared.c_macro.level     = -1;
00448         el->el_chared.c_macro.macro     = (char **) el_malloc(EL_MAXMACRO *
00449             sizeof(char *));
00450         if (el->el_chared.c_macro.macro == NULL)
00451                 return (-1);
00452         return (0);
00453 }
00454 
00455 /* ch_reset():
00456  *      Reset the character editor
00457  */
00458 protected void
00459 ch_reset(EditLine *el)
00460 {
00461         el->el_line.cursor              = el->el_line.buffer;
00462         el->el_line.lastchar            = el->el_line.buffer;
00463 
00464         el->el_chared.c_undo.action     = NOP;
00465         el->el_chared.c_undo.isize      = 0;
00466         el->el_chared.c_undo.dsize      = 0;
00467         el->el_chared.c_undo.ptr        = el->el_line.buffer;
00468 
00469         el->el_chared.c_vcmd.action     = NOP;
00470         el->el_chared.c_vcmd.pos        = el->el_line.buffer;
00471         el->el_chared.c_vcmd.ins        = el->el_line.buffer;
00472 
00473         el->el_chared.c_kill.mark       = el->el_line.buffer;
00474 
00475         el->el_map.current              = el->el_map.key;
00476 
00477         el->el_state.inputmode          = MODE_INSERT; /* XXX: save a default */
00478         el->el_state.doingarg           = 0;
00479         el->el_state.metanext           = 0;
00480         el->el_state.argument           = 1;
00481         el->el_state.lastcmd            = ED_UNASSIGNED;
00482 
00483         el->el_chared.c_macro.level     = -1;
00484 
00485         el->el_history.eventno          = 0;
00486 }
00487 
00488 /* ch_enlargebufs():
00489  *      Enlarge line buffer to be able to hold twice as much characters.
00490  *      Returns 1 if successful, 0 if not.
00491  */
00492 protected int
00493 ch_enlargebufs(el, addlen)
00494         EditLine *el;
00495         size_t addlen;
00496 {
00497         size_t sz, newsz;
00498         char *newbuffer, *oldbuf, *oldkbuf;
00499 
00500         sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE;
00501         newsz = sz * 2;
00502         /*
00503          * If newly required length is longer than current buffer, we need
00504          * to make the buffer big enough to hold both old and new stuff.
00505          */
00506         if (addlen > sz) {
00507                 while(newsz - sz < addlen)
00508                         newsz *= 2;
00509         }
00510 
00511         /*
00512          * Reallocate line buffer.
00513          */
00514         newbuffer = el_realloc(el->el_line.buffer, newsz);
00515         if (!newbuffer)
00516                 return 0;
00517 
00518         /* zero the newly added memory, leave old data in */
00519         (void) memset(&newbuffer[sz], 0, newsz - sz);
00520             
00521         oldbuf = el->el_line.buffer;
00522 
00523         el->el_line.buffer = newbuffer;
00524         el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
00525         el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
00526         el->el_line.limit  = &newbuffer[newsz - EL_LEAVE];
00527 
00528         /*
00529          * Reallocate kill buffer.
00530          */
00531         newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz);
00532         if (!newbuffer)
00533                 return 0;
00534 
00535         /* zero the newly added memory, leave old data in */
00536         (void) memset(&newbuffer[sz], 0, newsz - sz);
00537 
00538         oldkbuf = el->el_chared.c_kill.buf;
00539 
00540         el->el_chared.c_kill.buf = newbuffer;
00541         el->el_chared.c_kill.last = newbuffer +
00542                                         (el->el_chared.c_kill.last - oldkbuf);
00543         el->el_chared.c_kill.mark = el->el_line.buffer +
00544                                         (el->el_chared.c_kill.mark - oldbuf);
00545 
00546         /*
00547          * Reallocate undo buffer.
00548          */
00549         newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz);
00550         if (!newbuffer)
00551                 return 0;
00552 
00553         /* zero the newly added memory, leave old data in */
00554         (void) memset(&newbuffer[sz], 0, newsz - sz);
00555 
00556         el->el_chared.c_undo.ptr = el->el_line.buffer +
00557                                     (el->el_chared.c_undo.ptr - oldbuf);
00558         el->el_chared.c_undo.buf = newbuffer;
00559         
00560         if (!hist_enlargebuf(el, sz, newsz))
00561                 return 0;
00562 
00563         return 1;
00564 }
00565 
00566 /* ch_end():
00567  *      Free the data structures used by the editor
00568  */
00569 protected void
00570 ch_end(EditLine *el)
00571 {
00572         el_free((ptr_t) el->el_line.buffer);
00573         el->el_line.buffer = NULL;
00574         el->el_line.limit = NULL;
00575         el_free((ptr_t) el->el_chared.c_undo.buf);
00576         el->el_chared.c_undo.buf = NULL;
00577         el_free((ptr_t) el->el_chared.c_kill.buf);
00578         el->el_chared.c_kill.buf = NULL;
00579         el_free((ptr_t) el->el_chared.c_macro.macro);
00580         el->el_chared.c_macro.macro = NULL;
00581         ch_reset(el);
00582 }
00583 
00584 
00585 /* el_insertstr():
00586  *      Insert string at cursorI
00587  */
00588 public int
00589 el_insertstr(EditLine *el, const char *s)
00590 {
00591         size_t len;
00592 
00593         if ((len = strlen(s)) == 0)
00594                 return (-1);
00595         if (el->el_line.lastchar + len >= el->el_line.limit) {
00596                 if (!ch_enlargebufs(el, len))
00597                         return (-1);
00598         }
00599 
00600         c_insert(el, (int)len);
00601         while (*s)
00602                 *el->el_line.cursor++ = *s++;
00603         return (0);
00604 }
00605 
00606 
00607 /* el_deletestr():
00608  *      Delete num characters before the cursor
00609  */
00610 public void
00611 el_deletestr(EditLine *el, int n)
00612 {
00613         if (n <= 0)
00614                 return;
00615 
00616         if (el->el_line.cursor < &el->el_line.buffer[n])
00617                 return;
00618 
00619         c_delbefore(el, n);             /* delete before dot */
00620         el->el_line.cursor -= n;
00621         if (el->el_line.cursor < el->el_line.buffer)
00622                 el->el_line.cursor = el->el_line.buffer;
00623 }
00624 
00625 /* c_gets():
00626  *      Get a string
00627  */
00628 protected int
00629 c_gets(EditLine *el, char *buf)
00630 {
00631         char ch;
00632         int len = 0;
00633 
00634         for (ch = 0; ch == 0;) {
00635                 if (el_getc(el, &ch) != 1)
00636                         return (ed_end_of_file(el, 0));
00637                 switch (ch) {
00638                 case 0010:      /* Delete and backspace */
00639                 case 0177:
00640                         if (len > 1) {
00641                                 *el->el_line.cursor-- = '\0';
00642                                 el->el_line.lastchar = el->el_line.cursor;
00643                                 buf[len--] = '\0';
00644                         } else {
00645                                 el->el_line.buffer[0] = '\0';
00646                                 el->el_line.lastchar = el->el_line.buffer;
00647                                 el->el_line.cursor = el->el_line.buffer;
00648                                 return (CC_REFRESH);
00649                         }
00650                         re_refresh(el);
00651                         ch = 0;
00652                         break;
00653 
00654                 case 0033:      /* ESC */
00655                 case '\r':      /* Newline */
00656                 case '\n':
00657                         break;
00658 
00659                 default:
00660                         if (len >= EL_BUFSIZ)
00661                                 term_beep(el);
00662                         else {
00663                                 buf[len++] = ch;
00664                                 *el->el_line.cursor++ = ch;
00665                                 el->el_line.lastchar = el->el_line.cursor;
00666                         }
00667                         re_refresh(el);
00668                         ch = 0;
00669                         break;
00670                 }
00671         }
00672         buf[len] = ch;
00673         return (len);
00674 }
00675 
00676 
00677 /* c_hpos():
00678  *      Return the current horizontal position of the cursor
00679  */
00680 protected int
00681 c_hpos(EditLine *el)
00682 {
00683         char *ptr;
00684 
00685         /*
00686          * Find how many characters till the beginning of this line.
00687          */
00688         if (el->el_line.cursor == el->el_line.buffer)
00689                 return (0);
00690         else {
00691                 for (ptr = el->el_line.cursor - 1;
00692                      ptr >= el->el_line.buffer && *ptr != '\n';
00693                      ptr--)
00694                         continue;
00695                 return (el->el_line.cursor - ptr - 1);
00696         }
00697 }

Generated on Wed Sep 14 02:55:59 2005 for bro_docs by doxygen 1.3.5