gecko-dev/lib/libmisc/bkmutils.c
1998-03-28 02:44:41 +00:00

889 lines
22 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "xp_mcom.h"
#include "client.h"
#include "hotlist.h"
static int32 hot_measure_Separator(HotlistStruct * item, int bLongFormat, int nIndent);
#define TEXT_INDENT 3
#define SEPATATOR_COUNT 20
/*
* Measure a boring URL hotlist entry and return the length in bytes
*
* Short format:
* " item->address\n"
* where there are nIndent number of spaces before the item
*
* Long format:
* int16 type
* item->name\n
* item->address\n
* int32 addition_date
* int32 last_visit_date
* item->description\0
*
* The item->description field is *NOT* \n terminated since it might
* be a multi-line string and is therefore \0 terminated
*/
PRIVATE int32
hot_measure_URL(HotlistStruct * item, int bLongFormat, int nIndent)
{
int32 iSpace = 0;
if(!item)
return(0);
if(bLongFormat) {
/* type, addition_date, last_visit_date + 2 \n's + 1 \0 */
iSpace += 2 + 4 + 4 + 2 + 1;
if(item->name)
iSpace += XP_STRLEN(item->name);
if(item->description)
iSpace += XP_STRLEN(item->description);
} else {
/* space indentation and '\n' terminator */
iSpace = 1 + nIndent;
}
/* the address appears in both formats */
if(item->address)
iSpace += XP_STRLEN(item->address);
return(iSpace);
}
/*
* Measure a header entry and all its children
*
* Short format:
* " item->name\n"
* " child1->address\n"
* " child2->address\n"
* where there are nIndent number of spaces before the item and
* TEXT_INDENT spaces between levels
*
* Long format:
* int16 type
* item->name\n
* int32 addition_date
* int32 number of children
* item->description\0
*
* The item->description field is *NOT* \n terminated since it might
* be a multi-line string and is therefore \0 terminated. Note that
* the address field is *NOT* written for headers since its it meaningless
*/
PRIVATE int32
hot_measure_Header(HotlistStruct * item, int bLongFormat, int nIndent)
{
XP_List * list;
int32 iSpace = 0;
if(!item)
return(0);
if(bLongFormat) {
/* type, addition_date, last_visit_date + # children + 2 \n's + 1 \0 */
iSpace += 2 + 4 + 4 + 2 + 1;
if(item->description)
iSpace += XP_STRLEN(item->description);
} else {
/* space indentation and '\n' terminator */
iSpace = 1 + nIndent;
}
/* the name appears in both formats */
if(item->name)
iSpace += XP_STRLEN(item->name);
/* if no children just return */
if(!item->children)
return(iSpace);
/* measure the amount of space taken up by this item's children */
for(list = item->children->next; list; list = list->next) {
HotlistStruct * child = (HotlistStruct *) list->object;
if(!child)
continue;
switch(child->type) {
case HOT_URLType:
iSpace += hot_measure_URL(child, bLongFormat, nIndent + TEXT_INDENT);
break;
case HOT_HeaderType:
iSpace += hot_measure_Header(child, bLongFormat, nIndent + TEXT_INDENT);
break;
case HOT_SeparatorType:
iSpace += hot_measure_Separator(child, bLongFormat, nIndent + TEXT_INDENT);
break;
default:
break;
}
}
return(iSpace);
}
/*
* Measure a separator entry and return the length in bytes
*
* Short format:
* " ---------------------\n"
* where there are nIndent number of spaces before the item
* and 20 dashes
*
* Long format:
* int16 type
*/
PRIVATE int32
hot_measure_Separator(HotlistStruct * item, int bLongFormat, int nIndent)
{
int32 iSpace = 0;
if(!item)
return(0);
if(bLongFormat) {
/* type */
iSpace = 2;
} else {
/* space indentation and '\n' terminator */
iSpace = 1 + nIndent + 20;
}
return(iSpace);
}
/*
* Write out a boring URL hotlist entry. See comment at the top of
* hot_measure_URL for the format used. Assume we start writing at
* the start of the buffer passed in. Return a pointer to where the
* buffer ends when we get done.
*/
PRIVATE char *
hot_write_URL(char * buffer, HotlistStruct * item, int bLongFormat, int nIndent)
{
int iLen;
int32 lVal;
int32 sVal;
if(!item || !buffer)
return(buffer);
if(bLongFormat) {
/* copy the type */
sVal = (int16) item->type;
iLen = 2;
XP_MEMCPY(buffer, &sVal, iLen);
buffer += iLen;
/* copy the name */
if(item->name) {
iLen = XP_STRLEN(item->name);
XP_MEMCPY(buffer, item->name, iLen);
buffer += iLen;
}
/* put the \n terminator on */
*buffer++ = '\n';
/* copy the address */
if(item->address) {
iLen = XP_STRLEN(item->address);
XP_MEMCPY(buffer, item->address, iLen);
buffer += iLen;
}
/* put the \n terminator on */
*buffer++ = '\n';
/* addition date */
lVal = (int32) item->addition_date;
iLen = 4;
XP_MEMCPY(buffer, &lVal, iLen);
buffer += iLen;
/* last visit date */
lVal = (int32) item->last_visit;
iLen = 4;
XP_MEMCPY(buffer, &lVal, iLen);
buffer += iLen;
/* copy the description */
if(item->description) {
iLen = XP_STRLEN(item->description);
XP_MEMCPY(buffer, item->description, iLen);
buffer += iLen;
}
/* put the \n terminator on */
*buffer++ = '\0';
} else {
XP_MEMSET(buffer, ' ', nIndent);
buffer += nIndent;
if(item->address) {
XP_STRCPY(buffer, item->address);
buffer += XP_STRLEN(item->address);
}
*buffer++ = '\n';
}
return(buffer);
}
/*
* Write out a separator entry. See comment at the top of
* hot_measure_Separator for the format used. Assume we start writing at
* the start of the buffer passed in. Return a pointer to where the
* buffer ends when we get done.
*/
PRIVATE char *
hot_write_Separator(char * buffer, HotlistStruct * item, int bLongFormat, int nIndent)
{
int iLen;
#if 0
int32 lVal;
#endif
int32 sVal;
if(!item || !buffer)
return(buffer);
if(bLongFormat) {
/* copy the type */
sVal = (int16) item->type;
iLen = 2;
XP_MEMCPY(buffer, &sVal, iLen);
buffer += iLen;
} else {
XP_MEMSET(buffer, ' ', nIndent);
buffer += nIndent;
XP_MEMSET(buffer, '-', SEPATATOR_COUNT);
buffer += SEPATATOR_COUNT;
*buffer++ = '\n';
}
return(buffer);
}
/*
* Write out a hotlist header entry. See comment at the top of
* hot_measure_Header for the format used. Assume we start writing at
* the start of the buffer passed in. Return a pointer to where the
* buffer ends when we get done.
*/
PRIVATE char *
hot_write_Header(char * buffer, HotlistStruct * item, int bLongFormat, int nIndent)
{
XP_List * list;
int iLen;
int32 lVal;
int32 sVal;
if(!item || !buffer)
return(buffer);
if(bLongFormat) {
/* copy the type */
sVal = (int16) item->type;
iLen = 2;
XP_MEMCPY(buffer, &sVal, iLen);
buffer += iLen;
/* copy the name */
if(item->name) {
iLen = XP_STRLEN(item->name);
XP_MEMCPY(buffer, item->name, iLen);
buffer += iLen;
}
/* put the \n terminator on */
*buffer++ = '\n';
/* addition date */
lVal = (int32) item->addition_date;
iLen = 4;
XP_MEMCPY(buffer, &lVal, iLen);
buffer += iLen;
/* number of children */
lVal = XP_ListCount(item->children);
iLen = 4;
XP_MEMCPY(buffer, &lVal, iLen);
buffer += iLen;
/* copy the description */
if(item->description) {
iLen = XP_STRLEN(item->description);
XP_MEMCPY(buffer, item->description, iLen);
buffer += iLen;
}
/* put the \n terminator on */
*buffer++ = '\0';
} else {
XP_MEMSET(buffer, ' ', nIndent);
buffer += nIndent;
if(item->name) {
XP_STRCPY(buffer, item->name);
buffer += XP_STRLEN(item->name);
}
*buffer++ = '\n';
}
/* if no children just get out now */
if(!item->children)
return(buffer);
/* write out the children */
for(list = item->children->next; list; list = list->next) {
HotlistStruct * child = (HotlistStruct *) list->object;
if(!child)
continue;
switch(child->type) {
case HOT_URLType:
buffer = hot_write_URL(buffer, child, bLongFormat, nIndent + TEXT_INDENT);
break;
case HOT_HeaderType:
buffer = hot_write_Header(buffer, child, bLongFormat, nIndent + TEXT_INDENT);
break;
case HOT_SeparatorType:
buffer = hot_write_Separator(buffer, child, bLongFormat, nIndent + TEXT_INDENT);
break;
default:
break;
}
}
return(buffer);
}
/*
* Take a URL packed in a block the way hot_write_URL packs it.
* Return the new item if we created one
*/
PRIVATE HotlistStruct *
hot_read_URL(char * buffer, HotlistStruct * pListParent, HotlistStruct * item, int bLongFormat, int32 * lBytesEaten)
{
HotlistStruct * new_item = NULL;
if(!buffer)
return(NULL);
if(bLongFormat) {
int32 addition, visit;
/* get the name */
char * name = buffer;
char * address = strchr(name, '\n');
char * description = NULL;
char * ptr;
if(!address)
return(NULL);
*address++ = '\0';
/* get the address */
ptr = strchr(address, '\n');
if(!ptr)
return(NULL);
*ptr++ = '\0';
/* addition date */
XP_MEMCPY(&addition, ptr, 4);
ptr += 4;
/* visiting date */
XP_MEMCPY(&visit, ptr, 4);
ptr += 4;
/* get the description (it should be NULL terminated) */
description = ptr;
/* we should really strip leading whitespace */
new_item = HOT_CreateEntry(HOT_URLType, name, address, 0, visit);
new_item->addition_date = addition;
new_item->description = XP_STRDUP(description);
*lBytesEaten = XP_STRLEN(description) + (description - buffer) + 1;
} else {
char * end = strchr(buffer, '\n');
/* if there was a return NULL terminate the current string */
if(end)
*end++ = '\0';
/* we should really strip leading whitespace */
new_item = HOT_CreateEntry(HOT_URLType, buffer, buffer, 0, 0);
new_item->addition_date = time ((time_t *) NULL);
*lBytesEaten = XP_STRLEN(buffer) + 1;
}
if(item)
HOT_InsertItemAfter(item, new_item);
else
HOT_InsertItemInHeaderOrAfterItem(pListParent, new_item);
return(new_item);
}
/*
* Take a separator and insert it
*/
PRIVATE HotlistStruct *
hot_read_Separator(char * buffer, HotlistStruct * pListParent, HotlistStruct * item, int bLongFormat, int32 * lBytesEaten)
{
#if 0
int32 kids = 0;
int16 sVal;
#endif
HotlistStruct * new_item = NULL;
if(!buffer)
return(NULL);
/* can only read long format headers */
if(bLongFormat) {
new_item = HOT_CreateEntry(HOT_SeparatorType, NULL, NULL, 0, 0);
*lBytesEaten = 0;
if(item)
HOT_InsertItemAfter(item, new_item);
else
HOT_InsertItemInHeaderOrAfterItem(pListParent, new_item);
return(new_item);
}
return(NULL);
}
/*
* Take a header and children packed in a block the way hot_write_Header
* packs it. Return the new header item if we created one
*/
PRIVATE HotlistStruct *
hot_read_Header(char * buffer, HotlistStruct * pListParent, HotlistStruct * item, int bLongFormat, int32 * lBytesEaten)
{
int32 kids = 0;
int16 sVal;
HotlistStruct * new_item = NULL;
if(!buffer)
return(NULL);
/* can only read long format headers */
if(bLongFormat) {
int32 addition;
/* get the name */
char * name = buffer;
char * ptr = strchr(name, '\n');
char * description = NULL;
if(!ptr)
return(NULL);
/* skip over the \n but change it to a \0 so strcpy() will work */
*ptr++ = '\0';
/* addition date */
XP_MEMCPY(&addition, ptr, 4);
ptr += 4;
/* number of children to read */
XP_MEMCPY(&kids, ptr, 4);
ptr += 4;
/* get the description (it should be NULL terminated) */
description = ptr;
/* we should really strip leading whitespace */
new_item = HOT_CreateEntry(HOT_HeaderType, name, NULL, 0, 0);
new_item->addition_date = addition;
new_item->description = XP_STRDUP(description);
*lBytesEaten = XP_STRLEN(description) + (description - buffer) + 1;
/* handle all of the kids now */
if(kids) {
int i;
HotlistStruct * kid = NULL;
new_item->children = XP_ListNew();
buffer += *lBytesEaten;
for(i = 0; i < kids; i++) {
int32 lEat;
/* determine the type of the next entry */
sVal = 0;
XP_MEMCPY(&sVal, buffer, 2);
buffer += 2;
*lBytesEaten += 2;
switch(sVal) {
case HOT_URLType:
kid = hot_read_URL(buffer, new_item, kid, bLongFormat, &lEat);
*lBytesEaten += lEat;
buffer += lEat;
break;
case HOT_HeaderType:
kid = hot_read_Header(buffer, new_item, kid, bLongFormat, &lEat);
*lBytesEaten += lEat;
buffer += lEat;
break;
case HOT_SeparatorType:
kid = hot_read_Separator(buffer, new_item, kid, bLongFormat, &lEat);
*lBytesEaten += lEat;
buffer += lEat;
break;
default:
/* bogus type. Who knows whats going on. Just quit and get out */
break;
}
}
} else {
/* no kids */
new_item->children = NULL;
}
}
if(item)
HOT_InsertItemAfter(item, new_item);
else
HOT_InsertItemInHeaderOrAfterItem(pListParent, new_item);
return(new_item);
}
/*
* Allocate and return a string that contains the text representation of
* a list of hotlist entries (including headers and their contents).
* The caller is responsible for freeing the string
*/
PUBLIC char *
HOT_ConvertSelectionsToBlock(HotlistStruct ** list, int iCount, int bLongFormat, int32 * lTotalLen)
{
int i, j;
int bSkip;
int16 marker;
int32 iSpace = 0;
HotlistStruct * item;
HotlistStruct * pParent;
char * pString;
char * pOriginal;
for(i = 0; i < iCount; i++) {
/* don't skip yet */
bSkip = FALSE;
/* make sure we have a valid item */
item = list[i];
if(!item)
continue;
/*
* if our parent is in the list don't add ourselves, we will have
* been added when our parent got added. Ugh. This is going to be
* n^2 over the number of selected items
*/
for(pParent = item->parent; pParent && !bSkip; pParent = pParent->parent) {
for(j = 0; (j < iCount) && (!bSkip) ; j++)
if(list[j] == pParent)
bSkip = TRUE;
}
if(bSkip)
continue;
/* decide how much space we need */
switch(item->type) {
case HOT_URLType:
iSpace += hot_measure_URL(item, bLongFormat, 0);
break;
case HOT_HeaderType:
iSpace += hot_measure_Header(item, bLongFormat, 0);
break;
case HOT_SeparatorType:
iSpace += hot_measure_Separator(item, bLongFormat, 0);
break;
default:
break;
}
}
/* leave room for end of list marker */
if(bLongFormat)
iSpace += 2;
/* leave room for the termination character */
iSpace++;
#ifdef XP_WIN16
if(iSpace > 32000)
return(NULL);
#endif
/* allocate the string */
pOriginal = pString = (char *) XP_ALLOC(iSpace * sizeof(char));
if(!pString)
return(NULL);
/* Make a big string */
for(i = 0; i < iCount; i++) {
bSkip = FALSE;
/* make sure we have a valid item */
item = list[i];
if(!item)
continue;
/*
* if our parent is in the list don't add ourselves, we will have
* been added when our parent got added. Ugh. This is going to be
* n^2 over the number of selected items
*/
for(pParent = item->parent; pParent && !bSkip; pParent = pParent->parent) {
for(j = 0; (j < iCount) && (!bSkip) ; j++)
if(list[j] == pParent)
bSkip = TRUE;
}
if(bSkip)
continue;
/* write out the item */
switch(item->type) {
case HOT_URLType:
pString = hot_write_URL(pString, item, bLongFormat, 0);
break;
case HOT_HeaderType:
pString = hot_write_Header(pString, item, bLongFormat, 0);
break;
case HOT_SeparatorType:
pString = hot_write_Separator(pString, item, bLongFormat, 0);
break;
default:
break;
}
}
/* stick the end of list marker on so that when we are decoding this */
/* block we know when we are done */
if(bLongFormat) {
marker = 12345;
XP_MEMCPY(pString, &marker, 2);
pString +=2;
}
/* end the string and return the total length to our caller */
*pString++ = '\0';
*lTotalLen = (pString - pOriginal);
return(pOriginal);
}
/*
* Take a block of memory formatted by HOT_ConvertSelectionsToBlock and insert
* the items it represents into the hotlist following 'item'. If item is
* NULL insert at the beginning of the hotlist.
*/
PUBLIC void
HOT_InsertBlockAt(char * pOriginalBlock, HotlistStruct * item, int bLongFormat, int32 lTotalLen)
{
int16 sVal; /* 16-bit scratch variable */
int32 lBytesEaten = 0; /* total number of bytes eaten */
int32 lEat; /* number of bytes eaten on this item */
char * pCurrentPos;
char * pBlock;
HotlistStruct * pParent = HOT_GetHotlist();
if(!pOriginalBlock)
return;
/* make a copy of the string we can write into */
pCurrentPos = pBlock = (char *) XP_ALLOC(lTotalLen + 1);
if(!pBlock)
return;
/* copy the data over and make sure we are NULL terminated to make life easier */
XP_MEMCPY(pBlock, pOriginalBlock, lTotalLen);
pBlock[lTotalLen] = '\0';
/*
* if our very first element is a header then we really want to insert
* everything into the header and not after it --- I think.
*/
if(item && item->type == HOT_HeaderType) {
pParent = item;
item = NULL;
/*
* gack! if we are inserting under a header make sure its set up
* to accept children
*/
if(!pParent->children)
pParent->children = XP_ListNew();
}
/* long format can have all kinds of different types of things in it */
if(bLongFormat) {
while(lBytesEaten < lTotalLen) {
/* determine the type of the next entry */
sVal = 0;
XP_MEMCPY(&sVal, pCurrentPos, 2);
pCurrentPos += 2;
lBytesEaten += 2;
switch(sVal) {
case HOT_URLType:
item = hot_read_URL(pCurrentPos, pParent, item, bLongFormat, &lEat);
lBytesEaten += lEat;
pCurrentPos += lEat;
break;
case HOT_HeaderType:
item = hot_read_Header(pCurrentPos, pParent, item, bLongFormat, &lEat);
lBytesEaten += lEat;
pCurrentPos += lEat;
break;
case HOT_SeparatorType:
item = hot_read_Separator(pCurrentPos, pParent, item, bLongFormat, &lEat);
lBytesEaten += lEat;
pCurrentPos += lEat;
break;
default:
/* bogus type. Who knows whats going on. Just quit and get out */
return;
break;
}
}
} else {
/* short format is just a list of URLs separated by \n's */
while(lBytesEaten < lTotalLen) {
item = hot_read_URL(pCurrentPos, pParent, item, bLongFormat, &lEat);
lBytesEaten += lEat;
pCurrentPos += lEat;
/* if we just walked over a \0 we are done */
if(pOriginalBlock[lBytesEaten - 1] == '\0')
lBytesEaten = lTotalLen;
}
}
/* mark the bookmark list as changed and clean up */
HOT_SetModified();
XP_FREE(pBlock);
}