1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1998-2013 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 
22 /*
23  * partition support
24  */
25 
26 static const char usage[] =
27 "[-1i?\n@(#)$Id: pz library 2.4 (AT&T Research) 2011-03-07 $\n]"
28 "[a:append]"
29 "[c:comment]:[text]"
30 "[x:crc]"
31 "[d:debug]#[level]"
32 "[D:dump]"
33 "[G!:gzip]"
34 "[i:include]:[file]"
35 "[l:library]:[library]"
36 "[n:name]:[name]"
37 "[X:prefix?]:[count[*terminator]]]"
38 "[O:sort]"
39 "[P!:pzip]"
40 "[Q:regress]"
41 "[r:row]#[row-size]"
42 "[S:split]:?[pattern]"
43 "[s:summary]"
44 "[T:test]#[mask]"
45 "[v:verbose]"
46 ;
47 
48 #include "pzlib.h"
49 
50 #define VECTOR(z,p,n)	(((z)->flags&PZ_OVERSIZE)?(p)->row:(n))
51 
52 /*
53  * return partition pointer given name
54  */
55 
56 Pzpart_t*
pzpartget(Pz_t * pz,const char * name)57 pzpartget(Pz_t* pz, const char* name)
58 {
59 	if (!name || !*name || !pz->partdict)
60 		return pz->mainpart;
61 	pz->flags &= ~PZ_MAINONLY;
62 	return (Pzpart_t*)dtmatch(pz->partdict, name);
63 }
64 
65 /*
66  * partition iterator
67  * return the next partition after pp
68  * first call should set pp to 0
69  * 0 returned after all partitions visited
70  */
71 
72 Pzpart_t*
pzpartnext(Pz_t * pz,Pzpart_t * pp)73 pzpartnext(Pz_t* pz, Pzpart_t* pp)
74 {
75 	if (pz->partdict)
76 		return pp ? (Pzpart_t*)dtnext(pz->partdict, pp) : (Pzpart_t*)dtfirst(pz->partdict);
77 	return pp ? (Pzpart_t*)0 : pz->mainpart;
78 }
79 
80 /*
81  * set the current partition to pp
82  * old partition returned
83  */
84 
85 Pzpart_t*
pzpartset(Pz_t * pz,Pzpart_t * pp)86 pzpartset(Pz_t* pz, Pzpart_t* pp)
87 {
88 	Pzpart_t*	op;
89 
90 	pz->flags &= ~PZ_MAINONLY;
91 	if (pzsync(pz))
92 		return 0;
93 	op = pz->part;
94 	pz->part = pp;
95 	return op;
96 }
97 
98 /*
99  * parse a column range at s
100  * return
101  *	-1	error
102  *	 0	not a range
103  *	 1	ok
104  */
105 
106 static int
range(Pz_t * pz,register Pzpart_t * pp,char * s,char ** p,int * beg,int * end)107 range(Pz_t* pz, register Pzpart_t* pp, char* s, char** p, int* beg, int* end)
108 {
109 	int	n;
110 	int	m;
111 	char*	e;
112 
113 	for (; isspace(*s) || *s == ','; s++);
114 	if (*s == '-')
115 		n = 0;
116 	else
117 	{
118 		n = (int)strtol(s, &e, 10);
119 		if (s == e)
120 		{
121 			if (p)
122 				*p = e;
123 			return 0;
124 		}
125 		if (pp && n >= pp->row)
126 		{
127 			if (pz->disc->errorf)
128 				(*pz->disc->errorf)(pz, pz->disc, 2, "column %d is larger than row %d", n, pp->row);
129 			return -1;
130 		}
131 		for (s = e; isspace(*s); s++);
132 	}
133 	if (*s == '-')
134 	{
135 		if (!*++s || isspace(*s) || *s == ',')
136 		{
137 			e = s;
138 			m = (pp ? pp->row : INT_MAX) - 1;
139 		}
140 		else
141 		{
142 			m = (int)strtol(s, &e, 10);
143 			if (m < n || pp && m >= pp->row)
144 			{
145 				if (pz->disc->errorf)
146 					(*pz->disc->errorf)(pz, pz->disc, 2, "invalid column range %d-%d", n, m);
147 				return -1;
148 			}
149 		}
150 	}
151 	else
152 		m = n;
153 	if (p)
154 	{
155 		for (s = e; isspace(*s) || *s == ','; s++);
156 		*p = s;
157 	}
158 	if (beg)
159 		*beg = n;
160 	if (end)
161 		*end = m;
162 	return 1;
163 }
164 
165 /*
166  * parse a character value at s
167  */
168 
169 static int
value(Pz_t * pz,char * s,char ** p)170 value(Pz_t* pz, char* s, char** p)
171 {
172 	char*	e;
173 	int	q;
174 	int	v;
175 
176 	for (; isspace(*s); s++);
177 	if (!(q = *s++) || ((v = chresc(s, &e)), s == e))
178 	{
179 		if (pz->disc->errorf)
180 			(*pz->disc->errorf)(pz, pz->disc, 2, "value expected");
181 		return -1;
182 	}
183 	s = e;
184 	if (*s++ != q)
185 	{
186 		if (pz->disc->errorf)
187 			(*pz->disc->errorf)(pz, pz->disc, 2, "unbalanced %c quote in value", q);
188 		return -1;
189 	}
190 	if (*p)
191 		*p = s;
192 	return v;
193 }
194 
195 /*
196  * add fixed column range value to pz
197  */
198 
199 static int
fixed(Pz_t * pz,register Pzpart_t * pp,int n,int m,int k)200 fixed(Pz_t* pz, register Pzpart_t* pp, int n, int m, int k)
201 {
202 	int	i;
203 	int	v;
204 
205 	if (!pp->value)
206 	{
207 		if (!(pp->value = vmnewof(pz->vm, 0, int, pp->row, 0)))
208 			return pznospace(pz);
209 		for (i = 0; i < pp->row; i++)
210 			pp->value[i] = -1;
211 	}
212 	v = k < 0 ? ' ' : k;
213 	for (; n <= m; n++)
214 	{
215 		if (pp->value[n] < 0)
216 			pp->nfix++;
217 		else if (k < 0)
218 			continue;
219 		pp->value[n] = v;
220 	}
221 	return 0;
222 }
223 
224 /*
225  * initialize the partition map from pp.{<map,nmap>,<grp,ngrp>}
226  */
227 
228 int
pzpartmap(Pz_t * pz,register Pzpart_t * pp)229 pzpartmap(Pz_t* pz, register Pzpart_t* pp)
230 {
231 	int	i;
232 	int	j;
233 	int	k;
234 
235 	k = 0;
236 	for (i = 0; i < pp->ngrp; i++)
237 		for (j = 0; j < pp->grp[i]; j++)
238 		{
239 			if (k >= pp->nmap)
240 			{
241 				if (pz->disc->errorf)
242 					(*pz->disc->errorf)(pz, pz->disc, 2, "%s: invalid group", pz->path);
243 				return -1;
244 			}
245 			pp->lab[k] = i;
246 			pp->inc[k++] = pp->grp[i];
247 		}
248 	if (k != pp->nmap)
249 	{
250 		if (pz->disc->errorf)
251 			(*pz->disc->errorf)(pz, pz->disc, 2, "%s: invalid group", pz->path);
252 		return -1;
253 	}
254 	memset(pp->low, 1, pp->row);
255 	for (i = 0; i < pp->nmap; i++)
256 		pp->low[pp->map[i]] = 0;
257 	for (i = 0; i < pp->nfix; i++)
258 		pp->low[pp->fix[i]] = 0;
259 	if ((pz->flags & PZ_READ) && pp->value)
260 		for (i = j = 0; i < pp->nmap; i++)
261 			if (pp->value[i] < 0)
262 				pp->map[j++] = pp->map[i];
263 	pp->flags |= PZ_UPDATE;
264 	return 0;
265 }
266 
267 /*
268  * initialize the partition workspace
269  */
270 
271 int
pzpartinit(Pz_t * pz,Pzpart_t * pp,const char * name)272 pzpartinit(Pz_t* pz, Pzpart_t* pp, const char* name)
273 {
274 	char*	s;
275 	int	i;
276 	int	j;
277 	int	k;
278 	size_t	m;
279 	size_t	n;
280 
281 	m = 0;
282 	if (!(pz->flags & PZ_FORCE) || (pz->flags & PZ_SPLIT))
283 	{
284 		if (!pp->row)
285 		{
286 			if (pz->disc->errorf)
287 				(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition header corrupted", pz->path);
288 			return -1;
289 		}
290 		if (!pp->name)
291 		{
292 			if (!name)
293 				sfprintf(pz->tmp, "%s:%d:%d", PZ_PART_SUF, pp->row, pp->nmap);
294 			else
295 			{
296 				if (s = strrchr(name, '/'))
297 					name = (const char*)s + 1;
298 				if ((s = strrchr(name, '.')) && streq(s + 1, PZ_PART_SUF))
299 					n = s - (char*)name;
300 				else
301 					n = strlen(name);
302 				sfprintf(pz->tmp, "%.*s", n, name);
303 			}
304 			if (!(s = sfstruse(pz->tmp)) || !(pp->name = vmstrdup(pz->vm, s)))
305 				return -1;
306 		}
307 		if (!pp->nmap)
308 		{
309 			pp->nmap = pp->row;
310 			if (!(pp->map = vmnewof(pz->vm, pp->map, size_t, VECTOR(pz, pp, pp->nmap), 0)))
311 				return pznospace(pz);
312 			for (i = 0; i < pp->nmap; i++)
313 				pp->map[i] = i;
314 		}
315 		if (!pp->ngrp)
316 		{
317 			pp->ngrp = 1;
318 			if (!(pp->grp = vmnewof(pz->vm, pp->grp, size_t, VECTOR(pz, pp, pp->ngrp), 0)))
319 				return pznospace(pz);
320 			pp->grp[0] = pp->row;
321 		}
322 		pp->loq = ((pz->win / 8 / pp->row) + 8) * pp->row;
323 		k = VECTOR(pz, pp, pp->nmap);
324 		if (!(pp->low = vmnewof(pz->vm, 0, unsigned char, pp->row, 0)) ||
325 		    !(pp->mix = vmnewof(pz->vm, 0, unsigned char*, k, 0)) ||
326 		    !(pp->inc = vmnewof(pz->vm, 0, size_t, k, 0)) ||
327 		    !(pp->lab = vmnewof(pz->vm, 0, size_t, k, 0)))
328 			return pznospace(pz);
329 		if (pp->nfix)
330 		{
331 			if (!(pp->fix = vmnewof(pz->vm, 0, size_t, VECTOR(pz, pp, pp->nfix), 0)))
332 				return pznospace(pz);
333 			for (i = k = 0; i < pp->row; i++)
334 				if (pp->value[i] >= 0)
335 					pp->fix[k++] = i;
336 		}
337 		if (pzpartmap(pz, pp))
338 			return -1;
339 		if (!(j = pp->nmap))
340 			j = 1;
341 		k = pp->row * j;
342 		pp->col = ((pz->win / k) * k) / j;
343 
344 		/*
345 		 * allocate the global tables
346 		 */
347 
348 		if (pp->row > pz->mrow)
349 		{
350 			m = pz->mrow = roundof(pp->row, 1024);
351 			n = ((pz->win / 8 / m) + 8 ) * m;
352 			if (!(pz->val = vmnewof(pz->vm, pz->val, unsigned char, n, 0)) ||
353 			    !(pz->pat = vmnewof(pz->vm, pz->pat, unsigned char, m, 0)))
354 				return pznospace(pz);
355 		}
356 	}
357 	if (pz->win > pz->mwin)
358 	{
359 		if (pz->disc->errorf)
360 			(*pz->disc->errorf)(pz, pz->disc, -1, "%s: pzpartinit: win=%I*u mwin=%I*u buf=%p", pz->path, sizeof(pz->win), pz->win, sizeof(pz->mwin), pz->mwin, pz->buf);
361 		pz->mwin = roundof(pz->win, 32);
362 		n = pz->mwin;
363 		if (pz->flags & PZ_WRITE)
364 			n *= 2;
365 		if (!(pz->buf = vmnewof(pz->vm, pz->buf, unsigned char, n, 0)))
366 			return pznospace(pz);
367 		if (pz->flags & PZ_WRITE)
368 			pz->wrk = pz->buf + pz->mwin;
369 	}
370 	if (m && !(pz->flags & PZ_WRITE) && !(pz->wrk = vmnewof(pz->vm, pz->wrk, unsigned char, m, 0)))
371 		return pznospace(pz);
372 
373 	/*
374 	 * the discipline functions may change the partition name
375 	 */
376 
377 	n = pp->nfix;
378 	pz->part = pp;
379 	if (pz->options && pzoptions(pz, pp, pz->options, 0))
380 		return -1;
381 	if (pz->disc->eventf && (*pz->disc->eventf)(pz, PZ_PARTITION, pp, 0, pz->disc) < 0)
382 		return -1;
383 	if (pp->flags & PZ_VARIABLE)
384 		return 0;
385 	if (pp->nfix != n)
386 	{
387 		if (!(pp->fix = vmnewof(pz->vm, pp->fix, size_t, VECTOR(pz, pp, pp->nfix), 0)))
388 			return pznospace(pz);
389 		for (i = k = 0; i < pp->row; i++)
390 			if (pp->value[i] >= 0)
391 				pp->low[pp->fix[k++] = i] = 0;
392 	}
393 
394 	/*
395 	 * update the partition dictionary
396 	 * no dictionary if there's only one part
397 	 */
398 
399 	if (pz->mainpart)
400 	{
401 		if (!pz->partdict)
402 		{
403 			pz->partdisc.key = offsetof(Pzpart_t, name);
404 			pz->partdisc.size = -1;
405 			pz->partdisc.link = offsetof(Pzpart_t, link);
406 			if (!(pz->partdict = dtopen(&pz->partdisc, Dtoset)))
407 			{
408 				if (pz->disc->errorf)
409 					(*pz->disc->errorf)(pz, pz->disc, 2, "partition dictionary open error");
410 				return -1;
411 			}
412 			dtinsert(pz->partdict, pz->mainpart);
413 		}
414 		dtinsert(pz->partdict, pp);
415 	}
416 	else
417 	{
418 		pz->mainpart = pp;
419 		if ((pz->flags & (PZ_DUMP|PZ_VERBOSE)) && !(pz->flags & PZ_SPLIT))
420 			pzheadprint(pz, sfstderr, 0);
421 	}
422 	if ((pz->flags & PZ_DUMP) && !(pz->flags & PZ_SPLIT))
423 		pzpartprint(pz, pp, sfstderr);
424 	return 0;
425 }
426 
427 /*
428  * parse the run time options
429  */
430 
431 int
pzoptions(register Pz_t * pz,register Pzpart_t * pp,char * options,int must)432 pzoptions(register Pz_t* pz, register Pzpart_t* pp, char* options, int must)
433 {
434 	register char*		s = options;
435 	char*			e;
436 	char*			b;
437 	int			i;
438 	int			k;
439 	int			n;
440 	int			r;
441 	int			x;
442 	int			skip;
443 	Sfio_t*			sp;
444 	Pzpart_t*		opp;
445 
446 	optget(NiL, usage);
447 	skip = 0;
448 	for (;;)
449 	{
450 		for (; isspace(*s) || *s == ','; s++);
451 		if (!*s)
452 			break;
453 		switch (range(pz, pp, s, &e, &n, &x))
454 		{
455 		case -1:
456 			return -1;
457 		case 1:
458 			s = e;
459 			if (*s++ != '=')
460 				s = "";
461 			if ((k = value(pz, s, &e)) < 0)
462 				return -1;
463 			s = e;
464 			if (pp && fixed(pz, pp, n, x, k))
465 				return -1;
466 			continue;
467 		}
468 		b = s;
469 		opt_info.offset = 0;
470 		switch (optstr(s, NiL))
471 		{
472 		case 0:
473 			break;
474 		case '#':
475 			skip = !streq(opt_info.name, "pzip");
476 			continue;
477 		default:
478 			if (pz->disc->errorf)
479 				(*pz->disc->errorf)(pz, pz->disc, -2, "pzoptions: %-.*s", opt_info.offset, s);
480 			s += opt_info.offset;
481 			if (skip)
482 				continue;
483 
484 			/*
485 			 * the discipline gets the first crack
486 			 *
487 			 *	-1	error
488 			 *	 0	noticed but not consumed
489 			 *	 1	consumed
490 			 */
491 
492 			if (pz->disc->eventf)
493 			{
494 				opp = pz->part;
495 				pz->part = pp;
496 				x = (*pz->disc->eventf)(pz, PZ_OPTION, opt_info.argv[1] + 2, 0, pz->disc);
497 				pz->part = opp;
498 				if (x < 0)
499 					return -1;
500 			}
501 			else
502 				x = 0;
503 			if (!x)
504 			{
505 				x = 2;
506 				switch (optstr(NiL, usage))
507 				{
508 				case 'a':
509 					if (opt_info.num)
510 						pz->flags |= PZ_APPEND;
511 					else
512 						pz->flags &= ~PZ_APPEND;
513 					break;
514 				case 'c':
515 					if (!pz->disc->comment && !(pz->disc->comment = vmstrdup(pz->vm, opt_info.arg)))
516 						return pznospace(pz);
517 					break;
518 				case 'x':
519 					if (opt_info.num)
520 						pz->flags |= PZ_CRC;
521 					else
522 						pz->flags &= ~PZ_CRC;
523 					break;
524 				case 'd':
525 					error_info.trace = -opt_info.num;
526 					break;
527 				case 'D':
528 					if (opt_info.num)
529 						pz->flags |= PZ_DUMP;
530 					else
531 						pz->flags &= ~PZ_DUMP;
532 					break;
533 				case 'G':
534 					if (!opt_info.num)
535 						pz->flags |= PZ_NOGZIP;
536 					else
537 						pz->flags &= ~PZ_NOGZIP;
538 					break;
539 				case 'i':
540 					if (pz->pin && (sp = pzfind(pz, opt_info.arg, PZ_PART_SUF, "r")))
541 					{
542 						sfstack(pz->pin, sp);
543 						return 0;
544 					}
545 					break;
546 				case 'l':
547 					if (!pz->pin || !sfstacked(pz->pin))
548 					{
549 						if (i = pz->options == options)
550 							pz->options = 0;
551 						r = pzlib(pz, opt_info.arg, 0);
552 						if (pz->disc->errorf)
553 							(*pz->disc->errorf)(pz, pz->disc, -2, "pzlib: %s status=%d", opt_info.arg, r);
554 						if (i)
555 							pz->options = options;
556 						if (r < 0)
557 							return -1;
558 					}
559 					break;
560 				case 'n':
561 					pz->partname = vmstrdup(pz->vm, opt_info.arg);
562 					break;
563 				case 'X':
564 					if (pz->prefix.count = strton(opt_info.arg, &e, NiL, 0))
565 					{
566 						pz->prefix.terminator = -1;
567 						if (*e == 'x' || *e == 'X' || *e == '*' || *e == '-')
568 							e++;
569 						if (*e == 'l' || *e == 'L')
570 							pz->prefix.terminator = '\n';
571 						else
572 						{
573 							if (*e == '"' || *e == '\'')
574 							{
575 								pz->prefix.terminator = chresc(e + 1, &e);
576 								for (s = e; *s && !isspace(*s) && *s != ','; s++);
577 							}
578 							else if (*e)
579 							{
580 								pz->prefix.count = 0;
581 								if (pz->disc->errorf)
582 									(*pz->disc->errorf)(pz, pz->disc, 2, "%s: prefix expression expected", opt_info.arg);
583 								return -1;
584 							}
585 						}
586 					}
587 					else if (e > opt_info.arg)
588 						pz->prefix.skip = 1;
589 					break;
590 				case 'O':
591 					if (opt_info.num)
592 						pz->flags |= PZ_SORT;
593 					else
594 						pz->flags &= ~PZ_SORT;
595 					break;
596 				case 'P':
597 					if (!opt_info.num)
598 						pz->flags |= PZ_NOPZIP;
599 					else
600 						pz->flags &= ~PZ_NOPZIP;
601 					break;
602 				case 'Q':
603 					if (opt_info.num)
604 						pz->flags |= PZ_REGRESS;
605 					else
606 						pz->flags &= ~PZ_REGRESS;
607 					break;
608 				case 'r':
609 					pz->row = opt_info.num;
610 					break;
611 				case 'S':
612 					if (opt_info.num)
613 					{
614 						pz->flags |= PZ_FORCE|PZ_SPLIT|PZ_SECTION;
615 						if (opt_info.arg)
616 							pz->split.match = vmstrdup(pz->vm, opt_info.arg);
617 					}
618 					else
619 						pz->flags &= ~PZ_SPLIT;
620 					break;
621 				case 's':
622 					if (opt_info.num)
623 						pz->flags |= PZ_SUMMARY;
624 					else
625 						pz->flags &= ~PZ_SUMMARY;
626 					break;
627 				case 'T':
628 					pz->test |= opt_info.num;
629 					break;
630 				case 'v':
631 					if (opt_info.num)
632 						pz->flags |= PZ_VERBOSE;
633 					else
634 						pz->flags &= ~PZ_VERBOSE;
635 					break;
636 				case '?':
637 					error(ERROR_USAGE|4, "%s", opt_info.arg);
638 					return -1;
639 				case ':':
640 					if (must && !(pz->flags & PZ_PUSHED))
641 					{
642 						if (pz->disc->errorf)
643 							(*pz->disc->errorf)(pz, pz->disc, 2, "%s", opt_info.arg);
644 						return -1;
645 					}
646 					x = 0;
647 					break;
648 				}
649 			}
650 
651 			/*
652 			 * save consumed options for the header and
653 			 * clear so they are not processed again
654 			 */
655 
656 			if (must >= 0 && (x > 1 || x && must))
657 			{
658 				if (pz->det)
659 				{
660 					if (sfstrtell(pz->det))
661 						sfputc(pz->det, ' ');
662 					sfwrite(pz->det, b, s - b);
663 				}
664 				memset(b, ' ', s - b);
665 			}
666 			continue;
667 		}
668 		break;
669 	}
670 	return 0;
671 }
672 
673 static char*
partline(Pz_t * pz,Sfio_t * sp)674 partline(Pz_t* pz, Sfio_t* sp)
675 {
676 	char*	s;
677 
678 	if ((s = sfgetr(sp, '\n', 1)) && pz->disc->errorf)
679 		error_info.line++;
680 	return s;
681 }
682 
683 /*
684  * parse and load a partition file
685  */
686 
687 int
pzpartition(register Pz_t * pz,const char * partition)688 pzpartition(register Pz_t* pz, const char* partition)
689 {
690 	register Pzpart_t*	pp;
691 	int			i;
692 	int			k;
693 	int			m;
694 	char*			s;
695 	char*			e;
696 	char*			t;
697 	char*			np;
698 	int			n;
699 	int			g;
700 	int			gi;
701 	int			x;
702 	int*			cv;
703 	int*			ce;
704 	int*			cp;
705 	int*			gv;
706 	int*			hv;
707 	int			line;
708 	long			f;
709 	char*			file;
710 	Sfio_t*			sp;
711 	Vmalloc_t*		vm;
712 	char			buf[PATH_MAX];
713 
714 	if (pz->disc->errorf)
715 	{
716 		file = error_info.file;
717 		line = error_info.line;
718 	}
719 	sp = 0;
720 	vm = 0;
721 	if (!(s = (char*)partition))
722 	{
723 		if (pz->disc->errorf)
724 			(*pz->disc->errorf)(pz, pz->disc, 2, "partition file omitted");
725 		goto bad;
726 	}
727 	if (s[0] == '/' && s[i=strlen(s)-1] == '/')
728 	{
729 		if (streq(s, "/") || streq(s, "//") || streq(s, "/gzip/"))
730 			n = sfsprintf(buf, sizeof(buf), "nopzip\n1\n0-0\n");
731 		else
732 		{
733 			n = (int)strtol(s + 1, &e, 10);
734 			if (e[0] == '/' && !e[1])
735 				n = sfsprintf(buf, sizeof(buf), "%d\n0-%d\n", n, n - 1);
736 			else
737 			{
738 				n = sfsprintf(buf, sizeof(buf), "%.*s\n", i - 1, s + 1);
739 				for (s = buf; *s; s++)
740 					if (isspace(*s) || *s == ',')
741 						*s = '\n';
742 			}
743 		}
744 		if (!(sp = sfstropen()) || sfstrbuf(sp, buf, n, 0))
745 		{
746 			if (pz->disc->errorf)
747 				(*pz->disc->errorf)(pz, pz->disc, 2, "%s: string stream open error", s);
748 			goto bad;
749 		}
750 	}
751 	else
752 	{
753 		/*
754 		 * consume url-ish options
755 		 */
756 
757 		if ((e = strchr(s, '?')) || (e = strchr(s, '#')))
758 		{
759 			if (!(t = vmoldof(pz->vm, 0, char, e - s, 1)))
760 				goto bad;
761 			memcpy(t, s, e - s);
762 			t[e - s] = 0;
763 			s = t;
764 			if (*e == '#' && !(pz->partname = vmstrdup(pz->vm, e + 1)) || *e == '?' && pzoptions(pz, NiL, e + 1, 1))
765 				goto bad;
766 		}
767 		if (!(sp = pzfind(pz, s, PZ_PART_SUF, "r")))
768 			goto bad;
769 	}
770 	if (pz->disc->errorf)
771 	{
772 		error_info.file = s;
773 		error_info.line = 0;
774 	}
775 	if (!(vm = vmopen(Vmdcheap, Vmlast, 0)))
776 	{
777 		if (pz->disc->errorf)
778 			(*pz->disc->errorf)(pz, pz->disc, ERROR_SYSTEM|2, "partition temporary vmalloc region open error");
779 		goto bad;
780 	}
781 	np = 0;
782 	pp = 0;
783 	s = "";
784 	pz->pin = sp;
785 	do
786 	{
787 		vmclear(vm);
788 		do
789 		{
790 			if (*s != '"' && !(s = partline(pz, sp)))
791 			{
792 				if (pz->disc->errorf)
793 					(*pz->disc->errorf)(pz, pz->disc, 2, "invalid partition file");
794 				goto bad;
795 			}
796 			for (; isspace(*s); s++);
797 			if (*s == '"')
798 			{
799 				if (np)
800 				{
801 					if (pz->disc->errorf)
802 						(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition already named", np);
803 					goto bad;
804 				}
805 				for (e = ++s; *s && *s != '"'; s++);
806 				if (!*s)
807 				{
808 					if (pz->disc->errorf)
809 						(*pz->disc->errorf)(pz, pz->disc, 2, "unbalanced \" in partition name");
810 					goto bad;
811 				}
812 				*s++ = 0;
813 				if (!(np = vmstrdup(pz->vm, e)))
814 					goto bad;
815 				for (; isspace(*s); s++);
816 			}
817 			if (isalpha(*s))
818 			{
819 				if (pzoptions(pz, pp, s, 1))
820 					goto bad;
821 				s = "";
822 			}
823 		} while (!(n = strtol(s, &t, 10)));
824 		if (*t == '@')
825 		{
826 			switch (m = strtol(t + 1, &t, 10))
827 			{
828 			case 1:
829 				m = 0;
830 				break;
831 			case 2:
832 				m = 1;
833 				break;
834 			case 4:
835 				m = 2;
836 				break;
837 			case 8:
838 				m = 3;
839 				break;
840 			default:
841 				if (pz->disc->errorf)
842 					(*pz->disc->errorf)(pz, pz->disc, 2, "%s: %d: invalid size -- power of 2 from 1..8 expected", s, m);
843 				goto bad;
844 			}
845 			n |= (m << 14);
846 			f = PZ_VARIABLE;
847 		}
848 		else
849 			f = 0;
850 		if (pz->flags & PZ_ROWONLY)
851 		{
852 			if (!np || !pz->partname || streq(np, pz->partname))
853 			{
854 				vmclose(vm);
855 				pz->row = n;
856 				return 0;
857 			}
858 			np = 0;
859 			do
860 			{
861 				if (!(s = partline(pz, sp)))
862 				{
863 					if (pz->disc->errorf)
864 						(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition not found", pz->partname);
865 					goto bad;
866 				}
867 				for (; isspace(*s); s++);
868 			} while (*s != '"');
869 			continue;
870 		}
871 		if (!(pp = vmnewof(pz->vm, 0, Pzpart_t, 1, 0)))
872 			goto nope;
873 		pp->row = n;
874 		pp->flags = f;
875 		if (np)
876 		{
877 			pp->name = np;
878 			np = 0;
879 		}
880 		if (!(cv = vmnewof(vm, 0, int, (pp->row + 1) * 4, 0)))
881 			goto nope;
882 		cp = cv;
883 		ce = hv = cv + (pp->row + 1) * 2;
884 		gv = hv + pp->row + 1;
885 		m = 0;
886 		g = 0;
887 		for (s = t; isspace(*s); s++);
888 		if (*s != '-')
889 			s = partline(pz, sp);
890 		for (; s; s = partline(pz, sp))
891 		{
892 			for (; isspace(*s); s++);
893 			if (*s == '"')
894 				break;
895 			else if (isalpha(*s))
896 			{
897 				if (pzoptions(pz, pp, s, 1))
898 					goto bad;
899 				continue;
900 			}
901 			gi = 0;
902 			for (;;)
903 			{
904 				if (range(pz, pp, s, &e, &n, &x) <= 0)
905 				{
906 					if (*e && *e != '#')
907 						goto bad;
908 					break;
909 				}
910 				s = e;
911 				if (*s == '=')
912 				{
913 					if ((k = value(pz, ++s, &e)) < 0)
914 						return -1;
915 					s = e;
916 					if (fixed(pz, pp, n, x, k))
917 						goto bad;
918 					continue;
919 				}
920 				if (cp >= ce)
921 				{
922 					if (pz->disc->errorf)
923 						(*pz->disc->errorf)(pz, pz->disc, 2, "too many columns");
924 					goto bad;
925 				}
926 				for (; n <= x; n++)
927 					if (!pp->value || pp->value[n] < 0)
928 					{
929 						if (hv[n])
930 						{
931 							if (pz->disc->errorf)
932 								(*pz->disc->errorf)(pz, pz->disc, 2, "column %d already specified", n);
933 							goto bad;
934 						}
935 						hv[n] = 1;
936 						*cp++ = n;
937 						gv[m] = g;
938 						m++;
939 						gi = 1;
940 					}
941 			}
942 			if (gi)
943 			{
944 				if (cp >= ce)
945 				{
946 					if (pz->disc->errorf)
947 						(*pz->disc->errorf)(pz, pz->disc, 2, "too many columns");
948 					goto bad;
949 				}
950 				*cp++ = -1;
951 				g += gi;
952 			}
953 		}
954 		if (cp >= ce)
955 		{
956 			if (pz->disc->errorf)
957 				(*pz->disc->errorf)(pz, pz->disc, 2, "too many columns");
958 			goto bad;
959 		}
960 		*cp++ = -1;
961 
962 		/*
963 		 * allocate the map data and work space
964 		 */
965 
966 		pp->nmap = m;
967 		if (!(pp->ngrp = g))
968 			pp->ngrp = 1;
969 		if (!(pp->map = vmnewof(pz->vm, 0, size_t, VECTOR(pz, pp, pp->nmap), 0)) ||
970 		    !(pp->grp = vmnewof(pz->vm, 0, size_t, VECTOR(pz, pp, pp->ngrp), 0)))
971 			goto nope;
972 		m = 0;
973 		cp = cv;
974 		g = 0;
975 		k = 0;
976 		while ((i = *cp++) >= 0)
977 			do
978 			{
979 				if (g != gv[m])
980 				{
981 					pp->grp[g] = k;
982 					g = gv[m];
983 					k = 0;
984 				}
985 				pp->map[m] = i;
986 				k++;
987 				m++;
988 			} while ((i = *cp++) >= 0);
989 		pp->grp[g] = k;
990 		if (pzpartinit(pz, pp, partition))
991 			goto bad;
992 	} while (s);
993 	sfclose(sp);
994 	sp = 0;
995 	vmclose(vm);
996 	vm = 0;
997 	if (pz->partname)
998 	{
999 		if (!(pp = pzpartget(pz, pz->partname)))
1000 		{
1001 			if (pz->disc->errorf)
1002 				(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition not found", pz->partname);
1003 			goto bad;
1004 		}
1005 		pz->flags |= PZ_MAINONLY;
1006 		pz->part = pz->mainpart = pp;
1007 	}
1008 	if (pz->disc->errorf)
1009 	{
1010 		error_info.file = file;
1011 		error_info.line = line;
1012 	}
1013 	pz->pin = 0;
1014 	return 0;
1015  nope:
1016 	pznospace(pz);
1017  bad:
1018 	pz->pin = 0;
1019 	if (sp)
1020 		sfclose(sp);
1021 	if (vm)
1022 		vmclose(vm);
1023 	if (pz->disc->errorf)
1024 	{
1025 		error_info.file = file;
1026 		error_info.line = line;
1027 	}
1028 	return -1;
1029 }
1030 
1031 /*
1032  * allocate and read an array from the input partition header
1033  */
1034 
1035 static int
array(register Pz_t * pz,Pzpart_t * pp,size_t ** pv,size_t * pn,size_t check)1036 array(register Pz_t* pz, Pzpart_t* pp, size_t** pv, size_t* pn, size_t check)
1037 {
1038 	register size_t		n;
1039 	register size_t		m;
1040 	register size_t*	v;
1041 
1042 	n = sfgetu(pz->io);
1043 	if (check && n > check)
1044 		return -1;
1045 	if (pv)
1046 	{
1047 		if (!n)
1048 			v = 0;
1049 		else if (!(v = vmnewof(pz->vm, *pv, size_t, VECTOR(pz, pp, n), 0)))
1050 			return pznospace(pz);
1051 		*pv = v;
1052 		if (pn)
1053 			*pn = n;
1054 		while (n--)
1055 		{
1056 			m = sfgetu(pz->io);
1057 			if (check && m >= check)
1058 				return -1;
1059 			*v++ = m;
1060 		}
1061 	}
1062 	else
1063 		while (n--)
1064 		{
1065 			m = sfgetu(pz->io);
1066 			if (check && m >= check)
1067 				return -1;
1068 		}
1069 	return 0;
1070 }
1071 
1072 /*
1073  * allocate and read a buffer from the input partition header
1074  */
1075 
1076 static int
buffer(register Pz_t * pz,Pzpart_t * pp,char ** pv,size_t * pn)1077 buffer(register Pz_t* pz, Pzpart_t* pp, char** pv, size_t* pn)
1078 {
1079 	register size_t		n;
1080 	register char*		v;
1081 
1082 	if (!(n = sfgetu(pz->io)))
1083 	{
1084 		if (pz->disc->errorf)
1085 			(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition header corrupted", pz->path);
1086 		return -1;
1087 	}
1088 	if (pv)
1089 	{
1090 		if (!(v = vmnewof(pz->vm, *pv, char, n, 0)))
1091 			return pznospace(pz);
1092 		*pv = v;
1093 		if (pn)
1094 			*pn = n;
1095 		sfread(pz->io, v, n);
1096 	}
1097 	else
1098 		sfseek(pz->io, (Sflong_t)n, SEEK_CUR);
1099 	return 0;
1100 }
1101 
1102 /*
1103  * read a pz partition header(s) from pz->io
1104  */
1105 
1106 int
pzpartread(register Pz_t * pz)1107 pzpartread(register Pz_t* pz)
1108 {
1109 	register Pzpart_t*	pp;
1110 	register int		i;
1111 	Pzpart_t*		po;
1112 
1113 	if (pz->major > 1)
1114 	{
1115 		if (!(i = sfgetc(pz->io)))
1116 			return 0;
1117 		if (i == EOF)
1118 			return -1;
1119 		sfungetc(pz->io, i);
1120 	}
1121 	if (!(pp = vmnewof(pz->vm, 0, Pzpart_t, 1, 0)))
1122 		return pznospace(pz);
1123 	if (pz->major == 1)
1124 	{
1125 		pp->row = sfgetu(pz->io);
1126 		pp->col = sfgetu(pz->io);
1127 		pz->win = sfgetu(pz->io);
1128 	}
1129 	po = 0;
1130 	for (;;)
1131 	{
1132 		switch (i = sfgetc(pz->io))
1133 		{
1134 		case EOF:
1135 			goto bad;
1136 		case 0:
1137 			if (!po && pzpartinit(pz, pp, NiL))
1138 				return -1;
1139 			break;
1140 		case PZ_HDR_comment:
1141 			buffer(pz, pp, (char**)&pz->disc->comment, NiL);
1142 			continue;
1143 		case PZ_HDR_fix:
1144 			if (array(pz, pp, &pp->fix, &pp->nfix, pp->row))
1145 				goto bad;
1146 			if (!pp->value)
1147 			{
1148 				if (!(pp->value = vmnewof(pz->vm, 0, int, pp->row, 0)))
1149 					return pznospace(pz);
1150 				for (i = 0; i < pp->row; i++)
1151 					pp->value[i] = -1;
1152 			}
1153 			for (i = 0; i < pp->nfix; i++)
1154 				if (pp->value[pp->fix[i]] < 0)
1155 					pp->value[pp->fix[i]] = ' ';
1156 			continue;
1157 		case PZ_HDR_grp:
1158 			if (array(pz, pp, &pp->grp, &pp->ngrp, pp->row + 1))
1159 				goto bad;
1160 			continue;
1161 		case PZ_HDR_map:
1162 			if (array(pz, pp, &pp->map, &pp->nmap, pp->row))
1163 				goto bad;
1164 			continue;
1165 		case PZ_HDR_options:
1166 			buffer(pz, pp, (char**)&pz->headoptions, NiL);
1167 			continue;
1168 		case PZ_HDR_prefix:
1169 			buffer(pz, pp, &pz->prefix.data, &pz->prefix.count);
1170 			pz->prefix.terminator = -1;
1171 			continue;
1172 		case PZ_HDR_part:
1173 			if (pp->row)
1174 			{
1175 				if (!po && pzpartinit(pz, pp, NiL))
1176 					return -1;
1177 				if (pp = pz->freepart)
1178 					pz->freepart = 0;
1179 				else if (!(pp = vmnewof(pz->vm, 0, Pzpart_t, 1, 0)))
1180 					return pznospace(pz);
1181 			}
1182 			buffer(pz, pp, (char**)&pp->name, NiL);
1183 			pp->row = sfgetu(pz->io);
1184 			pp->col = sfgetu(pz->io);
1185 			if (pz->partdict && (po = (Pzpart_t*)dtsearch(pz->partdict, pp)) || (po = pz->mainpart) && streq(pp->name, po->name))
1186 			{
1187 				if (pp->row != po->row || pp->col != po->col)
1188 				{
1189 					if (pz->disc->errorf)
1190 						(*pz->disc->errorf)(pz, pz->disc, 1, "%s: %s: partition redefinition ignored", pz->path, pp->name);
1191 				}
1192 				else if (pz->flags & PZ_DUMP)
1193 					sfprintf(sfstderr, "\n# %s benign redefinition\n", pp->name);
1194 				vmfree(pz->vm, pp->name);
1195 				pz->freepart = pp;
1196 				pp->name = 0;
1197 				pp = po;
1198 				for (;;)
1199 				{
1200 					switch (i = sfgetc(pz->io))
1201 					{
1202 					case EOF:
1203 						break;
1204 					case 0:
1205 					case PZ_HDR_part:
1206 						sfungetc(pz->io, i);
1207 						break;
1208 					default:
1209 						if (PZ_HDR_ARR(i))
1210 							array(pz, pp, NiL, NiL, 0);
1211 						else if (PZ_HDR_BUF(i))
1212 							buffer(pz, pp, NiL, NiL);
1213 						else
1214 							goto bad;
1215 						continue;
1216 					}
1217 					break;
1218 				}
1219 			}
1220 			else
1221 				po = 0;
1222 			continue;
1223 		case PZ_HDR_split:
1224 			array(pz, pp, &pz->split.data, &pz->split.size, 0);
1225 			pz->split.flags |= PZ_SPLIT_INFLATE;
1226 			pz->flags |= PZ_SECTION;
1227 			continue;
1228 		default:
1229 			if (PZ_HDR_ARR(i))
1230 				array(pz, pp, NiL, NiL, 0);
1231 			else if (PZ_HDR_BUF(i))
1232 				buffer(pz, pp, NiL, NiL);
1233 			else
1234 				goto bad;
1235 			continue;
1236 		}
1237 		break;
1238 	}
1239 	return 0;
1240  bad:
1241 	if (pz->disc->errorf)
1242 		(*pz->disc->errorf)(pz, pz->disc, 2, "%s: partition header corrupted", pz->path);
1243 	return -1;
1244 }
1245 
1246 /*
1247  * write any new pz partition headers to op
1248  */
1249 
1250 int
pzpartwrite(Pz_t * pz,Sfio_t * op)1251 pzpartwrite(Pz_t* pz, Sfio_t* op)
1252 {
1253 	register Pzpart_t*	pp;
1254 	register int		i;
1255 	register size_t		m;
1256 	int			all;
1257 
1258 	if (pz->flags & PZ_MAINONLY)
1259 	{
1260 		pz->flags &= ~PZ_MAINONLY;
1261 		all = 0;
1262 		pp = pz->mainpart;
1263 	}
1264 	else if (pz->partdict)
1265 	{
1266 		all = 1;
1267 		pp = (Pzpart_t*)dtfirst(pz->partdict);
1268 	}
1269 	else
1270 	{
1271 		all = 0;
1272 		pp = pz->part;
1273 	}
1274 	while (pp)
1275 	{
1276 		if ((pp->flags & (PZ_UPDATE|PZ_HEAD)) == PZ_UPDATE)
1277 		{
1278 			pp->flags |= PZ_HEAD;
1279 			sfputc(op, PZ_HDR_part);
1280 			m = strlen(pp->name) + 1;
1281 			sfputu(op, m);
1282 			sfwrite(op, pp->name, m);
1283 			sfputu(op, pp->row);
1284 			sfputu(op, pp->col);
1285 			if (pp->nmap != pp->row || pp->ngrp != 1)
1286 			{
1287 				sfputc(op, PZ_HDR_map);
1288 				sfputu(op, pp->nmap);
1289 				for (i = 0; i < pp->nmap; i++)
1290 					sfputu(op, pp->map[i]);
1291 				sfputc(op, PZ_HDR_grp);
1292 				sfputu(op, pp->ngrp);
1293 				for (i = 0; i < pp->ngrp; i++)
1294 					sfputu(op, pp->grp[i]);
1295 			}
1296 			if (pp->nfix)
1297 			{
1298 				sfputc(op, PZ_HDR_fix);
1299 				sfputu(op, pp->nfix);
1300 				for (i = 0; i < pp->nfix; i++)
1301 					sfputu(op, pp->fix[i]);
1302 			}
1303 			if ((pz->split.flags & (PZ_SPLIT_DEFLATE|PZ_SPLIT_HEADER)) == PZ_SPLIT_DEFLATE)
1304 			{
1305 				pz->split.flags |= PZ_SPLIT_HEADER;
1306 				sfputc(op, PZ_HDR_split);
1307 				sfputu(op, 0);
1308 			}
1309 		}
1310 		if (!all)
1311 			break;
1312 		pp = (Pzpart_t*)dtnext(pz->partdict, pp);
1313 	}
1314 	sfputc(op, 0);
1315 	return 0;
1316 }
1317 
1318 /*
1319  * pretty print pp on op
1320  */
1321 
1322 int
pzpartprint(Pz_t * pz,register Pzpart_t * pp,register Sfio_t * op)1323 pzpartprint(Pz_t* pz, register Pzpart_t* pp, register Sfio_t* op)
1324 {
1325 	register int		i;
1326 	register int		j;
1327 	register int		g;
1328 	register char*		s;
1329 	char			esc[2];
1330 
1331 	sfprintf(op, "\n\"%s\"\n", pp->name);
1332 	sfprintf(op, "\n%I*u\t# high frequency %I*u\n", sizeof(pp->row), pp->row, sizeof(pp->nmap), pp->nmap);
1333 	if (pp->nfix)
1334 	{
1335 		sfprintf(op, "\n");
1336 		esc[1] = 0;
1337 		for (i = 0; i < pp->nfix; i++)
1338 		{
1339 			for (j = i + 1; j < pp->nfix && pp->fix[j] == pp->fix[j - 1] + 1 && pp->value[pp->fix[j]] == pp->value[pp->fix[j - 1]]; j++);
1340 			sfprintf(op, "%I*u", sizeof(pp->fix[i]), pp->fix[i]);
1341 			if (j > (i + 2))
1342 			{
1343 				i = j - 1;
1344 				sfprintf(op, "-%I*u", sizeof(pp->fix[i]), pp->fix[i]);
1345 			}
1346 			s = fmtesc((esc[0] = pp->value[pp->fix[i]], esc));
1347 			j = *s == '\'' ? '"' : '\'';
1348 			sfprintf(op, "=%c%s%c\n", j, s, j);
1349 		}
1350 	}
1351 	g = -1;
1352 	for (i = 0; i < pp->nmap; i++)
1353 	{
1354 		if (g != pp->lab[i])
1355 		{
1356 			g = pp->lab[i];
1357 			sfprintf(op, "\n");
1358 		}
1359 		else
1360 			sfprintf(op, " ");
1361 		for (j = i + 1; j < pp->nmap && pp->map[j] == pp->map[j - 1] + 1 && pp->lab[j] == g; j++);
1362 		sfprintf(op, "%I*u", sizeof(pp->map[i]), pp->map[i]);
1363 		if (j > (i + 2))
1364 		{
1365 			i = j - 1;
1366 			sfprintf(op, "-%I*u", sizeof(pp->map[i]), pp->map[i]);
1367 		}
1368 	}
1369 	sfprintf(op, "\n");
1370 	return sferror(op) ? -1 : 0;
1371 }
1372