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