1 /* This file is part of pam-modules.
2    Copyright (C) 2008, 2010-2012, 2014-2015, 2018 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16 
17 #include <graypam.h>
18 
19 #define GRAY_SLIST_BUCKET_SIZE 1024
20 
21 struct gray_slist_bucket {
22 	struct gray_slist_bucket *next;
23 	char *buf;
24 	size_t level;
25 	size_t size;
26 };
27 
28 struct gray_slist {
29 	struct gray_slist_bucket *head, *tail;
30 	struct gray_slist_bucket *free;
31 };
32 
33 static struct gray_slist_bucket *
alloc_bucket(size_t size)34 alloc_bucket(size_t size)
35 {
36 	struct gray_slist_bucket *p = gray_malloc(sizeof(*p) + size);
37 	p->buf = (char*)(p + 1);
38 	p->level = 0;
39 	p->size = size;
40 	p->next = NULL;
41 	return p;
42 }
43 
44 static void
alloc_pool(gray_slist_t slist,size_t size)45 alloc_pool(gray_slist_t slist, size_t size)
46 {
47 	struct gray_slist_bucket *p = alloc_bucket(GRAY_SLIST_BUCKET_SIZE);
48 	if (slist->tail)
49 		slist->tail->next = p;
50 	else
51 		slist->head = p;
52 	slist->tail = p;
53 }
54 
55 static size_t
copy_chars(gray_slist_t slist,const char * str,size_t n)56 copy_chars(gray_slist_t slist, const char *str, size_t n)
57 {
58 	size_t rest;
59 
60 
61 	if (!slist->head || slist->tail->level == slist->tail->size)
62 		alloc_pool(slist, GRAY_SLIST_BUCKET_SIZE);
63 	rest = slist->tail->size - slist->tail->level;
64 	if (n > rest)
65 		n = rest;
66 	memcpy(slist->tail->buf + slist->tail->level, str, n);
67 	slist->tail->level += n;
68 	return n;
69 }
70 
71 gray_slist_t
gray_slist_create()72 gray_slist_create()
73 {
74 	gray_slist_t slist = gray_malloc(sizeof(*slist));
75 	slist->head = slist->tail = slist->free = 0;
76 	return slist;
77 }
78 
79 void
gray_slist_clear(gray_slist_t slist)80 gray_slist_clear(gray_slist_t slist)
81 {
82 	if (slist->tail) {
83 		slist->tail->next = slist->free;
84 		slist->free = slist->head;
85 		slist->head = slist->tail = NULL;
86 	}
87 }
88 
89 
90 void
gray_slist_free(gray_slist_t * slist)91 gray_slist_free(gray_slist_t *slist)
92 {
93 	struct gray_slist_bucket *p;
94 	if (*slist) {
95 		gray_slist_clear(*slist);
96 		for (p = (*slist)->free; p; ) {
97 			struct gray_slist_bucket *next = p->next;
98 			free(p);
99 			p = next;
100 		}
101 	}
102 	free(*slist);
103 	*slist = NULL;
104 }
105 
106 void
gray_slist_append(gray_slist_t slist,const char * str,size_t n)107 gray_slist_append(gray_slist_t slist, const char *str, size_t n)
108 {
109 	const char *ptr = str;
110 	while (n) {
111 		size_t s = copy_chars(slist, ptr, n);
112 		ptr += s;
113 		n -= s;
114 	}
115 }
116 
117 void
gray_slist_append_char(gray_slist_t slist,char c)118 gray_slist_append_char(gray_slist_t slist, char c)
119 {
120 	gray_slist_append(slist, &c, 1);
121 }
122 
123 size_t
gray_slist_size(gray_slist_t slist)124 gray_slist_size(gray_slist_t slist)
125 {
126 	size_t size = 0;
127 	struct gray_slist_bucket *p;
128 	for (p = slist->head; p; p = p->next)
129 		size += p->level;
130 	return size;
131 }
132 
133 size_t
gray_slist_coalesce(gray_slist_t slist)134 gray_slist_coalesce(gray_slist_t slist)
135 {
136 	size_t size;
137 
138 	if (slist->head && slist->head->next == NULL)
139 		size = slist->head->level;
140 	else {
141 		size = gray_slist_size(slist);
142 		struct gray_slist_bucket *bucket = alloc_bucket(size);
143 		struct gray_slist_bucket *p;
144 
145 		for (p = slist->head; p; ) {
146 			struct gray_slist_bucket *next = p->next;
147 			memcpy(bucket->buf + bucket->level, p->buf, p->level);
148 			bucket->level += p->level;
149 			free(p);
150 			p = next;
151 		}
152 		slist->head = slist->tail = bucket;
153 	}
154 	return size;
155 }
156 
157 void *
gray_slist_head(gray_slist_t slist,size_t * psize)158 gray_slist_head(gray_slist_t slist, size_t *psize)
159 {
160 	if (*psize)
161 		*psize = slist->head ? slist->head->level : 0;
162 	return slist->head ? slist->head->buf : NULL;
163 }
164 
165 void *
gray_slist_finish(gray_slist_t slist)166 gray_slist_finish(gray_slist_t slist)
167 {
168 	gray_slist_coalesce(slist);
169 	gray_slist_clear(slist);
170 	return slist->free->buf;
171 }
172 
173 
174 #define to_num(c) \
175   (isdigit(c) ? c - '0' : (isxdigit(c) ? toupper(c) - 'A' + 10 : 255 ))
176 
177 void
gray_slist_grow_backslash_num(gray_slist_t slist,char * text,char ** pend,int len,int base)178 gray_slist_grow_backslash_num(gray_slist_t slist, char *text, char **pend,
179 			      int len, int base)
180 {
181 	int i;
182 	int val = 0;
183 	char *start = text;
184 
185 	if (text[0] == '\\') {
186 		text++;
187 		if (base == 16)
188 			text++;
189 	}
190 
191 	for (i = 0; i < len; i++) {
192 		int n = (unsigned char)text[i];
193 		if (n > 127 || (n = to_num(n)) >= base)
194 			break;
195 		val = val*base + n;
196 	}
197 
198 	if (i == 0) {
199 		gray_slist_append(slist, start, 1);
200 		if (pend)
201 			*pend = start + 1;
202 	} else {
203 		gray_slist_append_char(slist, val);
204 		if (pend)
205 			*pend = text + i;
206 	}
207 }
208 
209 int
gray_decode_backslash(int c)210 gray_decode_backslash(int c)
211 {
212         static char transtab[] = "a\ab\bf\fn\nr\rt\t";
213         char *p;
214 
215         for (p = transtab; *p; p += 2) {
216                 if (*p == c)
217                         return p[1];
218         }
219         return c;
220 }
221 
222 void
gray_slist_grow_backslash(gray_slist_t slist,char * text,char ** endp)223 gray_slist_grow_backslash(gray_slist_t slist, char *text, char **endp)
224 {
225 	if (text[1] == '\\' || (unsigned char)text[1] > 127) {
226 		gray_slist_append_char(slist, text[1]);
227 		text += 2;
228 	} else if (isdigit(text[1]))
229 		gray_slist_grow_backslash_num(slist, text, &text, 3, 8);
230 	else if (text[1] == 'x' || text[1] == 'X')
231 		gray_slist_grow_backslash_num(slist, text, &text, 2, 16);
232 	else {
233 		int c = gray_decode_backslash(text[1]);
234 		gray_slist_append_char(slist, c);
235 		text += 2;
236 	}
237 
238 	*endp = text;
239 }
240 
241