From 201cdcc462ca8edcd6aab42b0c462c6852717435 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Wed, 25 Jan 2006 13:14:12 +0100 Subject: [PATCH] iphlpapi: Remove one IP address per interface restriction. - remove restriction of one IP address per interface - remove dead code, and make static functions that can be - update comments and copyright notice --- dlls/iphlpapi/ifenum.c | 265 +++++++++++++++------------------- dlls/iphlpapi/ifenum.h | 22 +-- dlls/iphlpapi/iphlpapi_main.c | 67 +++++++-- dlls/iphlpapi/ipstats.c | 2 +- dlls/iphlpapi/ipstats.h | 2 +- 5 files changed, 171 insertions(+), 187 deletions(-) diff --git a/dlls/iphlpapi/ifenum.c b/dlls/iphlpapi/ifenum.c index 0283d367238..31b05327766 100644 --- a/dlls/iphlpapi/ifenum.c +++ b/dlls/iphlpapi/ifenum.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Juan Lang +/* Copyright (C) 2003,2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -455,38 +455,7 @@ InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void) return ret; } -DWORD getInterfaceIPAddrByName(const char *name) -{ - DWORD ret = INADDR_ANY; - - if (name) { - int fd = socket(PF_INET, SOCK_DGRAM, 0); - - if (fd != -1) { - struct ifreq ifr; - - lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); - if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) - memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); - close(fd); - } - } - return ret; -} - -DWORD getInterfaceIPAddrByIndex(DWORD index) -{ - DWORD ret; - const char *name = getInterfaceNameByIndex(index); - - if (name) - ret = getInterfaceIPAddrByName(name); - else - ret = INADDR_ANY; - return ret; -} - -DWORD getInterfaceBCastAddrByName(const char *name) +static DWORD getInterfaceBCastAddrByName(const char *name) { DWORD ret = INADDR_ANY; @@ -505,19 +474,7 @@ DWORD getInterfaceBCastAddrByName(const char *name) return ret; } -DWORD getInterfaceBCastAddrByIndex(DWORD index) -{ - DWORD ret; - const char *name = getInterfaceNameByIndex(index); - - if (name) - ret = getInterfaceBCastAddrByName(name); - else - ret = INADDR_ANY; - return ret; -} - -DWORD getInterfaceMaskByName(const char *name) +static DWORD getInterfaceMaskByName(const char *name) { DWORD ret = INADDR_NONE; @@ -536,18 +493,6 @@ DWORD getInterfaceMaskByName(const char *name) return ret; } -DWORD getInterfaceMaskByIndex(DWORD index) -{ - DWORD ret; - const char *name = getInterfaceNameByIndex(index); - - if (name) - ret = getInterfaceMaskByName(name); - else - ret = INADDR_NONE; - return ret; -} - #if defined (SIOCGIFHWADDR) DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, PDWORD type) @@ -657,12 +602,16 @@ DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, else { struct arpreq arp; struct sockaddr_in *saddr; + struct ifreq ifr; + /* get IP addr */ + lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); + ioctl(fd, SIOCGIFADDR, &ifr); memset(&arp, 0, sizeof(struct arpreq)); arp.arp_pa.sa_family = AF_INET; saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */ saddr->sin_family = AF_INET; - saddr->sin_addr.s_addr = getInterfaceIPAddrByName(name); + memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); if ((ioctl(fd, SIOCGARP, &arp))) ret = ERROR_INVALID_DATA; else { @@ -793,7 +742,7 @@ DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr, return ERROR_INVALID_DATA; } -DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) +static DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) { DWORD ret; int fd; @@ -825,17 +774,7 @@ DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) return ret; } -DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu) -{ - const char *name = getInterfaceNameByIndex(index); - - if (name) - return getInterfaceMtuByName(name, mtu); - else - return ERROR_INVALID_DATA; -} - -DWORD getInterfaceStatusByName(const char *name, PDWORD status) +static DWORD getInterfaceStatusByName(const char *name, PDWORD status) { DWORD ret; int fd; @@ -866,16 +805,6 @@ DWORD getInterfaceStatusByName(const char *name, PDWORD status) return ret; } -DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status) -{ - const char *name = getInterfaceNameByIndex(index); - - if (name) - return getInterfaceStatusByName(name, status); - else - return ERROR_INVALID_DATA; -} - DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry) { BYTE addr[MAX_INTERFACE_PHYSADDR]; @@ -927,6 +856,74 @@ DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry) return ERROR_INVALID_DATA; } +/* Enumerates the IP addresses in the system using SIOCGIFCONF, returning + * the count to you in *pcAddresses. It also returns to you the struct ifconf + * used by the call to ioctl, so that you may process the addresses further. + * Free ifc->ifc_buf using HeapFree. + * Returns NO_ERROR on success, something else on failure. + */ +static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc) +{ + DWORD ret; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd != -1) { + int ioctlRet = 0; + DWORD guessedNumAddresses = 0, numAddresses = 0; + caddr_t ifPtr; + + ret = NO_ERROR; + ifc->ifc_len = 0; + ifc->ifc_buf = NULL; + /* there is no way to know the interface count beforehand, + so we need to loop again and again upping our max each time + until returned < max */ + do { + HeapFree(GetProcessHeap(), 0, ifc->ifc_buf); + if (guessedNumAddresses == 0) + guessedNumAddresses = INITIAL_INTERFACES_ASSUMED; + else + guessedNumAddresses *= 2; + ifc->ifc_len = sizeof(struct ifreq) * guessedNumAddresses; + ifc->ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc->ifc_len); + ioctlRet = ioctl(fd, SIOCGIFCONF, ifc); + } while (ioctlRet == 0 && + ifc->ifc_len == (sizeof(struct ifreq) * guessedNumAddresses)); + + if (ioctlRet == 0) { + ifPtr = ifc->ifc_buf; + while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) { + numAddresses++; + ifPtr += ifreq_len((struct ifreq *)ifPtr); + } + } + else + ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */ + if (!ret) + *pcAddresses = numAddresses; + else + { + HeapFree(GetProcessHeap(), 0, ifc->ifc_buf); + ifc->ifc_buf = NULL; + } + close(fd); + } + else + ret = ERROR_NO_SYSTEM_RESOURCES; + return ret; +} + +DWORD getNumIPAddresses(void) +{ + DWORD numAddresses = 0; + struct ifconf ifc; + + if (!enumIPAddresses(&numAddresses, &ifc)) + HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); + return numAddresses; +} + DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags) { DWORD ret; @@ -935,84 +932,50 @@ DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags) ret = ERROR_INVALID_PARAMETER; else { - int fd; + DWORD numAddresses = 0; + struct ifconf ifc; - fd = socket(PF_INET, SOCK_DGRAM, 0); - if (fd != -1) { - int ioctlRet; - DWORD guessedNumAddresses, numAddresses; - struct ifconf ifc; - caddr_t ifPtr; + ret = enumIPAddresses(&numAddresses, &ifc); + if (!ret) + { + *ppIpAddrTable = HeapAlloc(heap, flags, sizeof(MIB_IPADDRTABLE) + + (numAddresses - 1) * sizeof(MIB_IPADDRROW)); + if (*ppIpAddrTable) { + DWORD i = 0, bcast; + caddr_t ifPtr; - guessedNumAddresses = 0; - ioctlRet = 0; - memset(&ifc, 0, sizeof(ifc)); - /* there is no way to know the interface count beforehand, - so we need to loop again and again upping our max each time - until returned < max */ - do { - if (guessedNumAddresses == 0) - guessedNumAddresses = INITIAL_INTERFACES_ASSUMED; - else - guessedNumAddresses *= 2; - HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); - ifc.ifc_len = sizeof(struct ifreq) * guessedNumAddresses; - ifc.ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc.ifc_len); - ioctlRet = ioctl(fd, SIOCGIFCONF, &ifc); - } while (ioctlRet == 0 && - ifc.ifc_len == (sizeof(struct ifreq) * guessedNumAddresses)); - - if (ioctlRet == 0) { - numAddresses = 0; + ret = NO_ERROR; + (*ppIpAddrTable)->dwNumEntries = numAddresses; ifPtr = ifc.ifc_buf; - while (ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) { - numAddresses++; - ifPtr += ifreq_len((struct ifreq *)ifPtr); + while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) { + struct ifreq *ifr = (struct ifreq *)ifPtr; + + ret = getInterfaceIndexByName(ifr->ifr_name, + &(*ppIpAddrTable)->table[i].dwIndex); + memcpy(&(*ppIpAddrTable)->table[i].dwAddr, ifr->ifr_addr.sa_data + 2, + sizeof(DWORD)); + (*ppIpAddrTable)->table[i].dwMask = + getInterfaceMaskByName(ifr->ifr_name); + /* the dwBCastAddr member isn't the broadcast address, it indicates + * whether the interface uses the 1's broadcast address (1) or the + * 0's broadcast address (0). + */ + bcast = getInterfaceBCastAddrByName(ifr->ifr_name); + (*ppIpAddrTable)->table[i].dwBCastAddr = + (bcast & (*ppIpAddrTable)->table[i].dwMask) ? 1 : 0; + /* FIXME: hardcoded reasm size, not sure where to get it */ + (*ppIpAddrTable)->table[i].dwReasmSize = 65535; + + (*ppIpAddrTable)->table[i].unused1 = 0; + (*ppIpAddrTable)->table[i].wType = 0; + ifPtr += ifreq_len(ifr); + i++; } - *ppIpAddrTable = HeapAlloc(heap, flags, sizeof(MIB_IPADDRTABLE) + - (numAddresses - 1) * sizeof(MIB_IPADDRROW)); - if (*ppIpAddrTable) { - DWORD i = 0, bcast; - - ret = NO_ERROR; - (*ppIpAddrTable)->dwNumEntries = numAddresses; - ifPtr = ifc.ifc_buf; - while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) { - struct ifreq *ifr = (struct ifreq *)ifPtr; - - ret = getInterfaceIndexByName(ifr->ifr_name, - &(*ppIpAddrTable)->table[i].dwIndex); - (*ppIpAddrTable)->table[i].dwAddr = - getInterfaceIPAddrByIndex((*ppIpAddrTable)->table[i].dwIndex); - (*ppIpAddrTable)->table[i].dwMask = - getInterfaceMaskByIndex((*ppIpAddrTable)->table[i].dwIndex); - /* the dwBCastAddr member isn't the broadcast address, it indicates - * whether the interface uses the 1's broadcast address (1) or the - * 0's broadcast address (0). - */ - bcast = getInterfaceBCastAddrByIndex( - (*ppIpAddrTable)->table[i].dwIndex); - (*ppIpAddrTable)->table[i].dwBCastAddr = - (bcast & (*ppIpAddrTable)->table[i].dwMask) ? 1 : 0; - /* FIXME: hardcoded reasm size, not sure where to get it */ - (*ppIpAddrTable)->table[i].dwReasmSize = 65535; - - (*ppIpAddrTable)->table[i].unused1 = 0; - (*ppIpAddrTable)->table[i].wType = 0; - ifPtr += ifreq_len(ifr); - i++; - } - } - else - ret = ERROR_OUTOFMEMORY; } else - ret = ERROR_INVALID_PARAMETER; + ret = ERROR_OUTOFMEMORY; HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); - close(fd); } - else - ret = ERROR_NO_SYSTEM_RESOURCES; } return ret; } diff --git a/dlls/iphlpapi/ifenum.h b/dlls/iphlpapi/ifenum.h index 3dd913589a2..75a2ad43854 100644 --- a/dlls/iphlpapi/ifenum.h +++ b/dlls/iphlpapi/ifenum.h @@ -1,5 +1,5 @@ /* ifenum.h - * Copyright (C) 2003 Juan Lang + * Copyright (C) 2003,2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -81,16 +81,6 @@ const char *getInterfaceNameByIndex(DWORD index); */ DWORD getInterfaceIndexByName(const char *name, PDWORD index); -/* This bunch returns IP addresses, and INADDR_ANY or INADDR_NONE if not found, - * appropriately depending on the f/n. - */ -DWORD getInterfaceIPAddrByName(const char *name); -DWORD getInterfaceIPAddrByIndex(DWORD index); -DWORD getInterfaceMaskByName(const char *name); -DWORD getInterfaceMaskByIndex(DWORD index); -DWORD getInterfaceBCastAddrByName(const char *name); -DWORD getInterfaceBCastAddrByIndex(DWORD index); - /* Gets a few physical charactersistics of a device: MAC addr len, MAC addr, * and type as one of the MIB_IF_TYPEs. * len's in-out: on in, needs to say how many bytes are available in addr, @@ -109,14 +99,6 @@ DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr, PDWORD type); -/* Get the operational status as a (MIB_)IF_OPER_STATUS type. - */ -DWORD getInterfaceStatusByName(const char *name, PDWORD status); -DWORD getInterfaceStatusByIndex(DWORD index, PDWORD status); - -DWORD getInterfaceMtuByName(const char *name, PDWORD mtu); -DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu); - /* Fills in the MIB_IFROW by name/index. Doesn't fill in interface statistics, * see ipstats.h for that. * Returns ERROR_INVALID_PARAMETER if name or entry is NULL, ERROR_INVALID_DATA @@ -125,6 +107,8 @@ DWORD getInterfaceMtuByIndex(DWORD index, PDWORD mtu); DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry); DWORD getInterfaceEntryByIndex(DWORD index, PMIB_IFROW entry); +DWORD getNumIPAddresses(void); + /* Gets the configured IP addresses for the system, and sets *ppIpAddrTable to * a table of them allocated from heap, or NULL if out of memory. Returns * NO_ERROR on success, something else on failure. Note there may be more than diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index df90fd6fbcb..9e43db06535 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -1,7 +1,7 @@ /* * iphlpapi dll implementation * - * Copyright (C) 2003 Juan Lang + * Copyright (C) 2003,2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -618,6 +618,7 @@ DWORD WINAPI FlushIpNetTable(DWORD dwIfIndex) DWORD WINAPI GetAdapterIndex(LPWSTR AdapterName, PULONG IfIndex) { FIXME("(AdapterName %p, IfIndex %p): stub\n", AdapterName, IfIndex); + /* FIXME: implement using getInterfaceIndexByName */ return ERROR_NOT_SUPPORTED; } @@ -646,22 +647,34 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) DWORD numNonLoopbackInterfaces = getNumNonLoopbackInterfaces(); if (numNonLoopbackInterfaces > 0) { - /* this calculation assumes only one address in the IP_ADDR_STRING lists. - that's okay, because: - - we don't get multiple addresses per adapter anyway - - we don't know about per-adapter gateways - - DHCP and WINS servers can have max one entry per list */ - ULONG size = sizeof(IP_ADAPTER_INFO) * numNonLoopbackInterfaces; + DWORD numIPAddresses = getNumIPAddresses(); + ULONG size; + /* This may slightly overestimate the amount of space needed, because + * the IP addresses include the loopback address, but it's easier + * to make sure there's more than enough space than to make sure there's + * precisely enough space. + */ + size = sizeof(IP_ADAPTER_INFO) * numNonLoopbackInterfaces; + if (numIPAddresses > numNonLoopbackInterfaces) + size += (numIPAddresses - numNonLoopbackInterfaces) * + sizeof(IP_ADDR_STRING); if (!pAdapterInfo || *pOutBufLen < size) { *pOutBufLen = size; ret = ERROR_BUFFER_OVERFLOW; } else { - InterfaceIndexTable *table = getNonLoopbackInterfaceIndexTable(); + InterfaceIndexTable *table = NULL; + PMIB_IPADDRTABLE ipAddrTable = NULL; + ret = getIPAddrTable(&ipAddrTable, GetProcessHeap(), 0); + if (!ret) + table = getNonLoopbackInterfaceIndexTable(); if (table) { size = sizeof(IP_ADAPTER_INFO) * table->numIndexes; + if (ipAddrTable->dwNumEntries > numNonLoopbackInterfaces) + size += (ipAddrTable->dwNumEntries - numNonLoopbackInterfaces) * + sizeof(IP_ADDR_STRING); if (*pOutBufLen < size) { *pOutBufLen = size; ret = ERROR_INSUFFICIENT_BUFFER; @@ -671,10 +684,13 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) HKEY hKey; BOOL winsEnabled = FALSE; IP_ADDRESS_STRING primaryWINS, secondaryWINS; + PIP_ADDR_STRING nextIPAddr = (PIP_ADDR_STRING)((LPBYTE)pAdapterInfo + + numNonLoopbackInterfaces * sizeof(IP_ADAPTER_INFO)); memset(pAdapterInfo, 0, size); /* @@ Wine registry key: HKCU\Software\Wine\Network */ - if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Network", &hKey) == ERROR_SUCCESS) { + if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Network", + &hKey) == ERROR_SUCCESS) { DWORD size = sizeof(primaryWINS.String); unsigned long addr; @@ -693,7 +709,9 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) } for (ndx = 0; ndx < table->numIndexes; ndx++) { PIP_ADAPTER_INFO ptr = &pAdapterInfo[ndx]; - DWORD addrLen = sizeof(ptr->Address), type; + DWORD addrLen = sizeof(ptr->Address), type, i; + PIP_ADDR_STRING currentIPAddr = &ptr->IpAddressList; + BOOL firstIPAddr = TRUE; /* on Win98 this is left empty, but whatever */ lstrcpynA(ptr->AdapterName, @@ -707,10 +725,26 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) ptr->AddressLength = addrLen; ptr->Type = type; ptr->Index = table->indexes[ndx]; - toIPAddressString(getInterfaceIPAddrByIndex(table->indexes[ndx]), - ptr->IpAddressList.IpAddress.String); - toIPAddressString(getInterfaceMaskByIndex(table->indexes[ndx]), - ptr->IpAddressList.IpMask.String); + for (i = 0; i < ipAddrTable->dwNumEntries; i++) { + if (ipAddrTable->table[i].dwIndex == ptr->Index) { + if (firstIPAddr) { + toIPAddressString(ipAddrTable->table[i].dwAddr, + ptr->IpAddressList.IpAddress.String); + toIPAddressString(ipAddrTable->table[i].dwBCastAddr, + ptr->IpAddressList.IpMask.String); + firstIPAddr = FALSE; + } + else { + currentIPAddr->Next = nextIPAddr; + currentIPAddr = nextIPAddr; + toIPAddressString(ipAddrTable->table[i].dwAddr, + currentIPAddr->IpAddress.String); + toIPAddressString(ipAddrTable->table[i].dwBCastAddr, + currentIPAddr->IpMask.String); + nextIPAddr++; + } + } + } if (winsEnabled) { ptr->HaveWins = TRUE; memcpy(ptr->PrimaryWinsServer.IpAddress.String, @@ -729,6 +763,8 @@ DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) } else ret = ERROR_OUTOFMEMORY; + if (ipAddrTable) + HeapFree(GetProcessHeap(), 0, ipAddrTable); } } else @@ -1038,6 +1074,7 @@ DWORD WINAPI GetInterfaceInfo(PIP_INTERFACE_INFO pIfTable, PULONG dwOutBufLen) else { DWORD ndx; + *dwOutBufLen = size; pIfTable->NumAdapters = 0; for (ndx = 0; ndx < table->numIndexes; ndx++) { const char *walker, *name; @@ -1791,7 +1828,7 @@ DWORD WINAPI SendARP(IPAddr DestIP, IPAddr SrcIP, PULONG pMacAddr, PULONG PhyAdd DWORD WINAPI SetIfEntry(PMIB_IFROW pIfRow) { FIXME("(pIfRow %p): stub\n", pIfRow); - /* this is supposed to set an administratively interface up or down. + /* this is supposed to set an interface administratively up or down. Could do SIOCSIFFLAGS and set/clear IFF_UP, but, not sure I want to, and this sort of down is indistinguishable from other sorts of down (e.g. no link). */ diff --git a/dlls/iphlpapi/ipstats.c b/dlls/iphlpapi/ipstats.c index 8a6d8d6b18b..f49a7cd4917 100644 --- a/dlls/iphlpapi/ipstats.c +++ b/dlls/iphlpapi/ipstats.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Juan Lang +/* Copyright (C) 2003,2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/dlls/iphlpapi/ipstats.h b/dlls/iphlpapi/ipstats.h index 7055d2bd7d0..15ce8bb1629 100644 --- a/dlls/iphlpapi/ipstats.h +++ b/dlls/iphlpapi/ipstats.h @@ -1,5 +1,5 @@ /* ipstats.h - * Copyright (C) 2003 Juan Lang + * Copyright (C) 2003,2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public