xref: /plan9/sys/src/cmd/aux/antiword/listlist.c (revision f5736e95f14e1485b3a0291fa82d86cca323ab61)
1 /*
2  * listlist.c
3  * Copyright (C) 2002,2003 A.J. van Os; Released under GPL
4  *
5  * Description:
6  * Build, read and destroy a list of Word list information
7  *
8  * Note:
9  * This list only exists when the Word document is saved by Word 8 or later
10  */
11 
12 #include "antiword.h"
13 
14 /*
15  * Private structure to hide the way the information
16  * is stored from the rest of the program
17  */
18 typedef struct list_desc_tag {
19 	list_block_type		tInfo;
20 	ULONG			ulListID;
21 	USHORT			usIstd;
22 	UCHAR			ucListLevel;
23 	struct list_desc_tag	*pNext;
24 } list_desc_type;
25 
26 typedef struct list_value_tag {
27 	USHORT			usValue;
28 	USHORT			usListIndex;
29 	UCHAR			ucListLevel;
30 	struct list_value_tag	*pNext;
31 } list_value_type;
32 
33 /* Variables needed to describe the LFO list (pllfo) */
34 static ULONG		*aulLfoList = NULL;
35 static USHORT		usLfoLen = 0;
36 /* Variables needed to write the List Information List */
37 static list_desc_type	*pAnchor = NULL;
38 static list_desc_type	*pBlockLast = NULL;
39 /* Variable needed for numbering new lists */
40 static list_value_type	*pValues = NULL;
41 /* Variables needed for numbering old lists */
42 static int	iOldListSeqNumber = 0;
43 static USHORT	usOldListValue = 0;
44 
45 
46 /*
47  * vDestroyListInfoList - destroy the List Information List
48  */
49 void
vDestroyListInfoList(void)50 vDestroyListInfoList(void)
51 {
52 	list_desc_type	*pCurr, *pNext;
53 	list_value_type	*pValueCurr, *pValueNext;
54 
55 	DBG_MSG("vDestroyListInfoList");
56 
57 	/* Free the LFO list */
58 	usLfoLen = 0;
59 	aulLfoList = xfree(aulLfoList);
60 
61 	/* Free the List Information List */
62 	pCurr = pAnchor;
63 	while (pCurr != NULL) {
64 		pNext = pCurr->pNext;
65 		pCurr = xfree(pCurr);
66 		pCurr = pNext;
67 	}
68 	pAnchor = NULL;
69 	/* Reset all control variables */
70 	pBlockLast = NULL;
71 
72 	/* Free the values list */
73 	pValueCurr = pValues;
74 	while (pValueCurr != NULL) {
75 		pValueNext = pValueCurr->pNext;
76 		pValueCurr = xfree(pValueCurr);
77 		pValueCurr = pValueNext;
78 	}
79 	pValues = NULL;
80 	/* Reset the values for the old lists */
81 	iOldListSeqNumber = 0;
82 	usOldListValue = 0;
83 } /* end of vDestroyListInfoList */
84 
85 /*
86  * vBuildLfoList - build the LFO list (pllfo)
87  */
88 void
vBuildLfoList(const UCHAR * aucBuffer,size_t tBufLen)89 vBuildLfoList(const UCHAR *aucBuffer, size_t tBufLen)
90 {
91 	size_t	tRecords;
92 	int	iIndex;
93 
94 	fail(aucBuffer == NULL);
95 
96 	if (tBufLen < 4) {
97 		return;
98 	}
99 	tRecords = (size_t)ulGetLong(0, aucBuffer);
100 	NO_DBG_DEC(tRecords);
101 	if (4 + 16 * tRecords > tBufLen || tRecords >= 0x7fff) {
102 		/* Just a sanity check */
103 		DBG_DEC(tRecords);
104 		DBG_DEC(4 + 16 * tRecords);
105 		DBG_DEC(tBufLen);
106 		return;
107 	}
108 	aulLfoList = xcalloc(tRecords, sizeof(ULONG));
109 	for (iIndex = 0; iIndex < (int)tRecords; iIndex++) {
110 		aulLfoList[iIndex] = ulGetLong(4 + 16 * iIndex, aucBuffer);
111 		NO_DBG_HEX(aulLfoList[iIndex]);
112 	}
113 	usLfoLen = (USHORT)tRecords;
114 } /* end of vBuildLfoList */
115 
116 /*
117  * vAdd2ListInfoList - add an element to the List Information list
118  */
119 void
vAdd2ListInfoList(ULONG ulListID,USHORT usIstd,UCHAR ucListLevel,const list_block_type * pListBlock)120 vAdd2ListInfoList(ULONG ulListID, USHORT usIstd, UCHAR ucListLevel,
121 	const list_block_type *pListBlock)
122 {
123 	list_desc_type	*pListMember;
124 
125 	fail(pListBlock == NULL);
126 
127 	NO_DBG_HEX(ulListID);
128 	NO_DBG_DEC(usIstd);
129 	NO_DBG_DEC(ucListLevel);
130 	NO_DBG_DEC(pListBlock->ulStartAt);
131 	NO_DBG_DEC(pListBlock->bNoRestart);
132 	NO_DBG_DEC(pListBlock->sLeftIndent);
133 	NO_DBG_HEX(pListBlock->ucNFC);
134 	NO_DBG_HEX(pListBlock->usListChar);
135 
136 	/* Create list member */
137 	pListMember = xmalloc(sizeof(list_desc_type));
138 	/* Fill the list member */
139 	pListMember->tInfo = *pListBlock;
140 	pListMember->ulListID = ulListID;
141 	pListMember->usIstd = usIstd;
142 	pListMember->ucListLevel = ucListLevel;
143 	pListMember->pNext = NULL;
144 	/* Correct the values where needed */
145 	if (pListMember->tInfo.ulStartAt > 0xffff) {
146 		DBG_DEC(pListMember->tInfo.ulStartAt);
147 		pListMember->tInfo.ulStartAt = 1;
148 	}
149 	/* Add the new member to the list */
150 	if (pAnchor == NULL) {
151 		pAnchor = pListMember;
152 	} else {
153 		fail(pBlockLast == NULL);
154 		pBlockLast->pNext = pListMember;
155 	}
156 	pBlockLast = pListMember;
157 } /* end of vAdd2ListInfoList */
158 
159 /*
160  * Get a matching record from the List Information List
161  *
162  * Returns NULL if no matching records is found
163  */
164 const list_block_type *
pGetListInfo(USHORT usListIndex,UCHAR ucListLevel)165 pGetListInfo(USHORT usListIndex, UCHAR ucListLevel)
166 {
167 	list_desc_type	*pCurr;
168 	list_block_type	*pNearMatch;
169 	ULONG	ulListID;
170 
171 	if (usListIndex == 0) {
172 		return NULL;
173 	}
174 	if (usListIndex - 1 >= usLfoLen || ucListLevel > 8) {
175 		DBG_DEC(usListIndex);
176 		DBG_DEC(ucListLevel);
177 		return NULL;
178 	}
179 	fail(aulLfoList == NULL);
180 	ulListID = aulLfoList[usListIndex - 1];
181 	NO_DBG_HEX(ulListID);
182 
183 	pNearMatch = NULL;
184 	for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
185 		if (pCurr->ulListID != ulListID) {
186 			/* No match */
187 			continue;
188 		}
189 		if (pCurr->ucListLevel == ucListLevel) {
190 			/* Exact match */
191 			return &pCurr->tInfo;
192 		}
193 		if (pCurr->ucListLevel == 0) {
194 			/* Near match */
195 			pNearMatch = &pCurr->tInfo;
196 		}
197 	}
198 	/* No exact match, use a near match if any */
199 	return pNearMatch;
200 } /* end of pGetListInfo */
201 
202 /*
203  * Get a matching record from the List Information List
204  *
205  * Returns NULL if no matching records is found
206  */
207 const list_block_type *
pGetListInfoByIstd(USHORT usIstd)208 pGetListInfoByIstd(USHORT usIstd)
209 {
210 	list_desc_type	*pCurr;
211 
212 	if (usIstd == ISTD_INVALID || usIstd == STI_NIL || usIstd == STI_USER) {
213 		return NULL;
214 	}
215 
216 	for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
217 		if (pCurr->usIstd == usIstd) {
218 			return &pCurr->tInfo;
219 		}
220 	}
221 	return NULL;
222 } /* end of pGetListInfoByIstd */
223 
224 /*
225  * vRestartListValues - reset the less significant list levels
226  */
227 static void
vRestartListValues(USHORT usListIndex,UCHAR ucListLevel)228 vRestartListValues(USHORT usListIndex, UCHAR ucListLevel)
229 {
230 	list_value_type	*pPrev, *pCurr, *pNext;
231 	int		iCounter;
232 
233 	iCounter = 0;
234 	pPrev = NULL;
235 	pCurr = pValues;
236 
237 	while (pCurr != NULL) {
238 		if (pCurr->usListIndex != usListIndex ||
239 		    pCurr->ucListLevel <= ucListLevel) {
240 			pPrev = pCurr;
241 			pCurr = pCurr->pNext;
242 			continue;
243 		}
244 		/* Reset the level by deleting the record */
245 		pNext = pCurr->pNext;
246 		if (pPrev == NULL) {
247 			pValues = pNext;
248 		} else {
249 			pPrev->pNext = pNext;
250 		}
251 		DBG_DEC(pCurr->usListIndex);
252 		DBG_DEC(pCurr->ucListLevel);
253 		pCurr = xfree(pCurr);
254 		pCurr = pNext;
255 		iCounter++;
256 	}
257 	DBG_DEC_C(iCounter > 0, iCounter);
258 } /* end of vRestartListValues */
259 
260 /*
261  * usGetListValue - Get the current value of the given list
262  *
263  * Returns the value of the given list
264  */
265 USHORT
usGetListValue(int iListSeqNumber,int iWordVersion,const style_block_type * pStyle)266 usGetListValue(int iListSeqNumber, int iWordVersion,
267 	const style_block_type *pStyle)
268 {
269 	list_value_type	*pCurr;
270 	USHORT		usValue;
271 
272 	fail(iListSeqNumber < 0);
273 	fail(iListSeqNumber < iOldListSeqNumber);
274 	fail(iWordVersion < 0);
275 	fail(pStyle == NULL);
276 
277 	if (iListSeqNumber <= 0) {
278 		return 0;
279 	}
280 
281 	if (iWordVersion < 8) {
282 		/* Old style list */
283 		if (iListSeqNumber == iOldListSeqNumber ||
284 		    (iListSeqNumber == iOldListSeqNumber + 1 &&
285 		     eGetNumType(pStyle->ucNumLevel) == level_type_sequence)) {
286 			if (!pStyle->bNumPause) {
287 				usOldListValue++;
288 			}
289 		} else {
290 			usOldListValue = pStyle->usStartAt;
291 		}
292 		iOldListSeqNumber = iListSeqNumber;
293 		return usOldListValue;
294 	}
295 
296 	/* New style list */
297 	if (pStyle->usListIndex == 0 ||
298 	    pStyle->usListIndex - 1 >= usLfoLen ||
299 	    pStyle->ucListLevel > 8) {
300 		/* Out of range; no need to search */
301 		return 0;
302 	}
303 
304 	for (pCurr = pValues; pCurr != NULL; pCurr = pCurr->pNext) {
305 		if (pCurr->usListIndex == pStyle->usListIndex &&
306 		    pCurr->ucListLevel == pStyle->ucListLevel) {
307 			/* Record found; increment and return the value */
308 			pCurr->usValue++;
309 			usValue = pCurr->usValue;
310 			if (!pStyle->bNoRestart) {
311 				vRestartListValues(pStyle->usListIndex,
312 						pStyle->ucListLevel);
313 			}
314 			return usValue;
315 		}
316 	}
317 
318 	/* Record not found; create it and add it to the front of the list */
319 	pCurr = xmalloc(sizeof(list_value_type));
320 	pCurr->usValue = pStyle->usStartAt;
321 	pCurr->usListIndex = pStyle->usListIndex;
322 	pCurr->ucListLevel = pStyle->ucListLevel;
323 	pCurr->pNext = pValues;
324 	pValues = pCurr;
325 	usValue = pCurr->usValue;
326 	if (!pStyle->bNoRestart) {
327 		vRestartListValues(pStyle->usListIndex, pStyle->ucListLevel);
328 	}
329 	return usValue;
330 } /* end of usGetListValue */
331