gr_util.c revision 242319
1178431Sscf/*- 2178431Sscf * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org> 3178431Sscf * All rights reserved. 4178431Sscf * 5178431Sscf * Redistribution and use in source and binary forms, with or without 6178431Sscf * modification, are permitted provided that the following conditions 7178431Sscf * are met: 8178431Sscf * 1. Redistributions of source code must retain the above copyright 9178431Sscf * notice, this list of conditions and the following disclaimer, 10178431Sscf * without modification, immediately at the beginning of the file. 11178431Sscf * 2. Redistributions in binary form must reproduce the above copyright 12178431Sscf * notice, this list of conditions and the following disclaimer in the 13178431Sscf * documentation and/or other materials provided with the distribution. 14178431Sscf * 15178431Sscf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16178431Sscf * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17178431Sscf * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18178431Sscf * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19178431Sscf * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20178431Sscf * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21178431Sscf * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22178431Sscf * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23178431Sscf * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24178431Sscf * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25178431Sscf */ 26178431Sscf 27178431Sscf#include <sys/cdefs.h> 28178431Sscf__FBSDID("$FreeBSD: head/lib/libutil/gr_util.c 242319 2012-10-29 17:19:43Z bapt $"); 29178431Sscf 30178431Sscf#include <sys/param.h> 31228545Sbapt#include <sys/errno.h> 32228545Sbapt#include <sys/stat.h> 33184831Sscf 34228545Sbapt#include <ctype.h> 35228545Sbapt#include <err.h> 36228545Sbapt#include <fcntl.h> 37178431Sscf#include <grp.h> 38178431Sscf#include <inttypes.h> 39184831Sscf#include <libutil.h> 40228545Sbapt#include <paths.h> 41178431Sscf#include <stdbool.h> 42178431Sscf#include <stdio.h> 43178431Sscf#include <stdlib.h> 44178431Sscf#include <string.h> 45228545Sbapt#include <unistd.h> 46178431Sscf 47185237Sscfstruct group_storage { 48185237Sscf struct group gr; 49185237Sscf char *members[]; 50185237Sscf}; 51178431Sscf 52228545Sbaptstatic int lockfd = -1; 53228545Sbaptstatic char group_dir[PATH_MAX]; 54228545Sbaptstatic char group_file[PATH_MAX]; 55228545Sbaptstatic char tempname[PATH_MAX]; 56228545Sbaptstatic int initialized; 57228545Sbapt 58185237Sscfstatic const char group_line_format[] = "%s:%s:%ju:"; 59185237Sscf 60178431Sscf/* 61228545Sbapt * Initialize statics 62228545Sbapt */ 63228545Sbaptint 64228545Sbaptgr_init(const char *dir, const char *group) 65228545Sbapt{ 66242319Sbapt struct stat st; 67242319Sbapt 68228545Sbapt if (dir == NULL) { 69228545Sbapt strcpy(group_dir, _PATH_ETC); 70228545Sbapt } else { 71228545Sbapt if (strlen(dir) >= sizeof(group_dir)) { 72228545Sbapt errno = ENAMETOOLONG; 73228545Sbapt return (-1); 74228545Sbapt } 75228545Sbapt strcpy(group_dir, dir); 76228545Sbapt } 77228545Sbapt 78228545Sbapt if (group == NULL) { 79228545Sbapt if (dir == NULL) { 80228545Sbapt strcpy(group_file, _PATH_GROUP); 81228545Sbapt } else if (snprintf(group_file, sizeof(group_file), "%s/group", 82228545Sbapt group_dir) > (int)sizeof(group_file)) { 83228545Sbapt errno = ENAMETOOLONG; 84228545Sbapt return (-1); 85228545Sbapt } 86228545Sbapt } else { 87228545Sbapt if (strlen(group) >= sizeof(group_file)) { 88228545Sbapt errno = ENAMETOOLONG; 89228545Sbapt return (-1); 90228545Sbapt } 91228545Sbapt strcpy(group_file, group); 92228545Sbapt } 93242319Sbapt 94242319Sbapt if (stat(group_file, &st) == -1) 95242319Sbapt return (-1); 96242319Sbapt 97242319Sbapt if (S_ISDIR(st.st_mode)) { 98242319Sbapt errno = EISDIR; 99242319Sbapt return (-1); 100242319Sbapt } 101242319Sbapt 102228545Sbapt initialized = 1; 103228545Sbapt return (0); 104228545Sbapt} 105228545Sbapt 106228545Sbapt/* 107228545Sbapt * Lock the group file 108228545Sbapt */ 109228545Sbaptint 110228545Sbaptgr_lock(void) 111228545Sbapt{ 112228545Sbapt if (*group_file == '\0') 113228545Sbapt return (-1); 114228545Sbapt 115228545Sbapt for (;;) { 116228545Sbapt struct stat st; 117228545Sbapt 118228545Sbapt lockfd = open(group_file, O_RDONLY, 0); 119228545Sbapt if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) 120228545Sbapt err(1, "%s", group_file); 121228545Sbapt if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { 122228545Sbapt if (errno == EWOULDBLOCK) { 123228545Sbapt errx(1, "the group file is busy"); 124228545Sbapt } else { 125228545Sbapt err(1, "could not lock the group file: "); 126228545Sbapt } 127228545Sbapt } 128228545Sbapt if (fstat(lockfd, &st) == -1) 129228545Sbapt err(1, "fstat() failed: "); 130228545Sbapt if (st.st_nlink != 0) 131228545Sbapt break; 132228545Sbapt close(lockfd); 133228545Sbapt lockfd = -1; 134228545Sbapt } 135228545Sbapt return (lockfd); 136228545Sbapt} 137228545Sbapt 138228545Sbapt/* 139228545Sbapt * Create and open a presmuably safe temp file for editing group data 140228545Sbapt */ 141228545Sbaptint 142228545Sbaptgr_tmp(int mfd) 143228545Sbapt{ 144228545Sbapt char buf[8192]; 145228545Sbapt ssize_t nr; 146228545Sbapt const char *p; 147228545Sbapt int tfd; 148228545Sbapt 149228545Sbapt if (*group_file == '\0') 150228545Sbapt return (-1); 151228545Sbapt if ((p = strrchr(group_file, '/'))) 152228545Sbapt ++p; 153228545Sbapt else 154228545Sbapt p = group_file; 155228545Sbapt if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", 156228545Sbapt (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { 157228545Sbapt errno = ENAMETOOLONG; 158228545Sbapt return (-1); 159228545Sbapt } 160228545Sbapt if ((tfd = mkstemp(tempname)) == -1) 161228545Sbapt return (-1); 162228545Sbapt if (mfd != -1) { 163228545Sbapt while ((nr = read(mfd, buf, sizeof(buf))) > 0) 164228545Sbapt if (write(tfd, buf, (size_t)nr) != nr) 165228545Sbapt break; 166228545Sbapt if (nr != 0) { 167228545Sbapt unlink(tempname); 168228545Sbapt *tempname = '\0'; 169228545Sbapt close(tfd); 170228545Sbapt return (-1); 171228545Sbapt } 172228545Sbapt } 173228545Sbapt return (tfd); 174228545Sbapt} 175228545Sbapt 176228545Sbapt/* 177228545Sbapt * Copy the group file from one descriptor to another, replacing, deleting 178228545Sbapt * or adding a single record on the way. 179228545Sbapt */ 180228545Sbaptint 181228545Sbaptgr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) 182228545Sbapt{ 183228545Sbapt char buf[8192], *end, *line, *p, *q, *r, t; 184228545Sbapt struct group *fgr; 185228545Sbapt const struct group *sgr; 186228545Sbapt size_t len; 187228545Sbapt int eof, readlen; 188228545Sbapt 189228545Sbapt sgr = gr; 190228545Sbapt if (gr == NULL) { 191228545Sbapt line = NULL; 192228545Sbapt if (old_gr == NULL) 193228545Sbapt return (-1); 194228545Sbapt sgr = old_gr; 195228545Sbapt } else if ((line = gr_make(gr)) == NULL) 196228545Sbapt return (-1); 197228545Sbapt 198228545Sbapt eof = 0; 199228545Sbapt len = 0; 200228545Sbapt p = q = end = buf; 201228545Sbapt for (;;) { 202228545Sbapt /* find the end of the current line */ 203228545Sbapt for (p = q; q < end && *q != '\0'; ++q) 204228545Sbapt if (*q == '\n') 205228545Sbapt break; 206228545Sbapt 207228545Sbapt /* if we don't have a complete line, fill up the buffer */ 208228545Sbapt if (q >= end) { 209228545Sbapt if (eof) 210228545Sbapt break; 211228545Sbapt if ((size_t)(q - p) >= sizeof(buf)) { 212228545Sbapt warnx("group line too long"); 213228545Sbapt errno = EINVAL; /* hack */ 214228545Sbapt goto err; 215228545Sbapt } 216228545Sbapt if (p < end) { 217228545Sbapt q = memmove(buf, p, end -p); 218228545Sbapt end -= p - buf; 219228545Sbapt } else { 220228545Sbapt p = q = end = buf; 221228545Sbapt } 222228545Sbapt readlen = read(ffd, end, sizeof(buf) - (end -buf)); 223228545Sbapt if (readlen == -1) 224228545Sbapt goto err; 225228545Sbapt else 226228545Sbapt len = (size_t)readlen; 227228545Sbapt if (len == 0 && p == buf) 228228545Sbapt break; 229228545Sbapt end += len; 230228545Sbapt len = end - buf; 231228545Sbapt if (len < (ssize_t)sizeof(buf)) { 232228545Sbapt eof = 1; 233228545Sbapt if (len > 0 && buf[len -1] != '\n') 234228545Sbapt ++len, *end++ = '\n'; 235228545Sbapt } 236228545Sbapt continue; 237228545Sbapt } 238228545Sbapt 239228545Sbapt /* is it a blank line or a comment? */ 240228545Sbapt for (r = p; r < q && isspace(*r); ++r) 241228545Sbapt /* nothing */; 242228545Sbapt if (r == q || *r == '#') { 243228545Sbapt /* yep */ 244228545Sbapt if (write(tfd, p, q -p + 1) != q - p + 1) 245228545Sbapt goto err; 246228545Sbapt ++q; 247228545Sbapt continue; 248228545Sbapt } 249228545Sbapt 250228545Sbapt /* is it the one we're looking for? */ 251228545Sbapt 252228545Sbapt t = *q; 253228545Sbapt *q = '\0'; 254228545Sbapt 255228545Sbapt fgr = gr_scan(r); 256228545Sbapt 257228545Sbapt /* fgr is either a struct group for the current line, 258228545Sbapt * or NULL if the line is malformed. 259228545Sbapt */ 260228545Sbapt 261228545Sbapt *q = t; 262228545Sbapt if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { 263228545Sbapt /* nope */ 264228545Sbapt if (fgr != NULL) 265228545Sbapt free(fgr); 266228545Sbapt if (write(tfd, p, q - p + 1) != q - p + 1) 267228545Sbapt goto err; 268228545Sbapt ++q; 269228545Sbapt continue; 270228545Sbapt } 271228545Sbapt if (old_gr && !gr_equal(fgr, old_gr)) { 272228545Sbapt warnx("entry inconsistent"); 273228545Sbapt free(fgr); 274228545Sbapt errno = EINVAL; /* hack */ 275228545Sbapt goto err; 276228545Sbapt } 277228545Sbapt free(fgr); 278228545Sbapt 279228545Sbapt /* it is, replace or remove it */ 280228545Sbapt if (line != NULL) { 281228545Sbapt len = strlen(line); 282228545Sbapt if (write(tfd, line, len) != (int) len) 283228545Sbapt goto err; 284228545Sbapt } else { 285228545Sbapt /* when removed, avoid the \n */ 286228545Sbapt q++; 287228545Sbapt } 288228545Sbapt /* we're done, just copy the rest over */ 289228545Sbapt for (;;) { 290228545Sbapt if (write(tfd, q, end - q) != end - q) 291228545Sbapt goto err; 292228545Sbapt q = buf; 293228545Sbapt readlen = read(ffd, buf, sizeof(buf)); 294228545Sbapt if (readlen == 0) 295228545Sbapt break; 296228545Sbapt else 297228545Sbapt len = (size_t)readlen; 298228545Sbapt if (readlen == -1) 299228545Sbapt goto err; 300228545Sbapt end = buf + len; 301228545Sbapt } 302228545Sbapt goto done; 303228545Sbapt } 304228545Sbapt 305228545Sbapt /* if we got here, we didn't find the old entry */ 306228545Sbapt if (line == NULL) { 307228545Sbapt errno = ENOENT; 308228545Sbapt goto err; 309228545Sbapt } 310228545Sbapt len = strlen(line); 311228545Sbapt if ((size_t)write(tfd, line, len) != len || 312228545Sbapt write(tfd, "\n", 1) != 1) 313228545Sbapt goto err; 314228545Sbapt done: 315228545Sbapt if (line != NULL) 316228545Sbapt free(line); 317228545Sbapt return (0); 318228545Sbapt err: 319228545Sbapt if (line != NULL) 320228545Sbapt free(line); 321228545Sbapt return (-1); 322228545Sbapt} 323228545Sbapt 324228545Sbapt/* 325228545Sbapt * Regenerate the group file 326228545Sbapt */ 327228545Sbaptint 328228545Sbaptgr_mkdb(void) 329228545Sbapt{ 330228545Sbapt return (rename(tempname, group_file)); 331228545Sbapt} 332228545Sbapt 333228545Sbapt/* 334228545Sbapt * Clean up. Preserver errno for the caller's convenience. 335228545Sbapt */ 336228545Sbaptvoid 337228545Sbaptgr_fini(void) 338228545Sbapt{ 339228545Sbapt int serrno; 340228545Sbapt 341228545Sbapt if (!initialized) 342228545Sbapt return; 343228545Sbapt initialized = 0; 344228545Sbapt serrno = errno; 345228545Sbapt if (*tempname != '\0') { 346228545Sbapt unlink(tempname); 347228545Sbapt *tempname = '\0'; 348228545Sbapt } 349228545Sbapt if (lockfd != -1) 350228545Sbapt close(lockfd); 351228545Sbapt errno = serrno; 352228545Sbapt} 353228545Sbapt 354228545Sbapt/* 355178431Sscf * Compares two struct group's. 356178431Sscf */ 357178431Sscfint 358178431Sscfgr_equal(const struct group *gr1, const struct group *gr2) 359178431Sscf{ 360185237Sscf int gr1_ndx; 361185237Sscf int gr2_ndx; 362184831Sscf bool found; 363178431Sscf 364178431Sscf /* Check that the non-member information is the same. */ 365185237Sscf if (gr1->gr_name == NULL || gr2->gr_name == NULL) { 366185237Sscf if (gr1->gr_name != gr2->gr_name) 367185237Sscf return (false); 368185237Sscf } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) 369185237Sscf return (false); 370185237Sscf if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { 371185237Sscf if (gr1->gr_passwd != gr2->gr_passwd) 372185237Sscf return (false); 373185237Sscf } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) 374185237Sscf return (false); 375185237Sscf if (gr1->gr_gid != gr2->gr_gid) 376185237Sscf return (false); 377178431Sscf 378178431Sscf /* Check all members in both groups. */ 379185237Sscf if (gr1->gr_mem == NULL || gr2->gr_mem == NULL) { 380185237Sscf if (gr1->gr_mem != gr2->gr_mem) 381185237Sscf return (false); 382185237Sscf } else { 383185237Sscf for (found = false, gr1_ndx = 0; gr1->gr_mem[gr1_ndx] != NULL; 384185237Sscf gr1_ndx++) { 385185237Sscf for (gr2_ndx = 0; gr2->gr_mem[gr2_ndx] != NULL; 386185237Sscf gr2_ndx++) 387185237Sscf if (strcmp(gr1->gr_mem[gr1_ndx], 388185237Sscf gr2->gr_mem[gr2_ndx]) == 0) { 389178431Sscf found = true; 390178431Sscf break; 391178431Sscf } 392185237Sscf if (!found) 393185237Sscf return (false); 394178431Sscf } 395178431Sscf 396178431Sscf /* Check that group2 does not have more members than group1. */ 397185237Sscf if (gr2->gr_mem[gr1_ndx] != NULL) 398185237Sscf return (false); 399178431Sscf } 400178431Sscf 401185237Sscf return (true); 402178431Sscf} 403178431Sscf 404178431Sscf/* 405178431Sscf * Make a group line out of a struct group. 406178431Sscf */ 407178431Sscfchar * 408178431Sscfgr_make(const struct group *gr) 409178431Sscf{ 410178431Sscf char *line; 411185237Sscf size_t line_size; 412178431Sscf int ndx; 413178431Sscf 414178431Sscf /* Calculate the length of the group line. */ 415185237Sscf line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, 416178431Sscf gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; 417185237Sscf if (gr->gr_mem != NULL) { 418185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) 419185237Sscf line_size += strlen(gr->gr_mem[ndx]) + 1; 420185237Sscf if (ndx > 0) 421185237Sscf line_size--; 422185237Sscf } 423178431Sscf 424178431Sscf /* Create the group line and fill it. */ 425185237Sscf if ((line = malloc(line_size)) == NULL) 426178431Sscf return (NULL); 427200423Sscf snprintf(line, line_size, group_line_format, gr->gr_name, gr->gr_passwd, 428200423Sscf (uintmax_t)gr->gr_gid); 429185237Sscf if (gr->gr_mem != NULL) 430185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { 431185237Sscf strcat(line, gr->gr_mem[ndx]); 432185237Sscf if (gr->gr_mem[ndx + 1] != NULL) 433185237Sscf strcat(line, ","); 434185237Sscf } 435178431Sscf 436178431Sscf return (line); 437178431Sscf} 438178431Sscf 439178431Sscf/* 440178431Sscf * Duplicate a struct group. 441178431Sscf */ 442178431Sscfstruct group * 443178431Sscfgr_dup(const struct group *gr) 444178431Sscf{ 445185237Sscf char *dst; 446184831Sscf size_t len; 447185237Sscf struct group_storage *gs; 448178431Sscf int ndx; 449185237Sscf int num_mem; 450178431Sscf 451185237Sscf /* Calculate size of the group. */ 452185237Sscf len = sizeof(*gs); 453185237Sscf if (gr->gr_name != NULL) 454185237Sscf len += strlen(gr->gr_name) + 1; 455185237Sscf if (gr->gr_passwd != NULL) 456185237Sscf len += strlen(gr->gr_passwd) + 1; 457178431Sscf if (gr->gr_mem != NULL) { 458185237Sscf for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) 459185237Sscf len += strlen(gr->gr_mem[num_mem]) + 1; 460185237Sscf len += (num_mem + 1) * sizeof(*gr->gr_mem); 461185237Sscf } else 462185237Sscf num_mem = -1; 463178431Sscf 464178431Sscf /* Create new group and copy old group into it. */ 465185237Sscf if ((gs = calloc(1, len)) == NULL) 466178431Sscf return (NULL); 467185237Sscf dst = (char *)&gs->members[num_mem + 1]; 468178431Sscf if (gr->gr_name != NULL) { 469185237Sscf gs->gr.gr_name = dst; 470185237Sscf dst = stpcpy(gs->gr.gr_name, gr->gr_name) + 1; 471178431Sscf } 472178431Sscf if (gr->gr_passwd != NULL) { 473185237Sscf gs->gr.gr_passwd = dst; 474185237Sscf dst = stpcpy(gs->gr.gr_passwd, gr->gr_passwd) + 1; 475178431Sscf } 476185237Sscf gs->gr.gr_gid = gr->gr_gid; 477178431Sscf if (gr->gr_mem != NULL) { 478185237Sscf gs->gr.gr_mem = gs->members; 479185237Sscf for (ndx = 0; ndx < num_mem; ndx++) { 480185237Sscf gs->gr.gr_mem[ndx] = dst; 481185237Sscf dst = stpcpy(gs->gr.gr_mem[ndx], gr->gr_mem[ndx]) + 1; 482178431Sscf } 483185237Sscf gs->gr.gr_mem[ndx] = NULL; 484178431Sscf } 485178431Sscf 486185237Sscf return (&gs->gr); 487178431Sscf} 488178431Sscf 489178431Sscf/* 490178431Sscf * Scan a line and place it into a group structure. 491178431Sscf */ 492178431Sscfstatic bool 493178431Sscf__gr_scan(char *line, struct group *gr) 494178431Sscf{ 495178431Sscf char *loc; 496178431Sscf int ndx; 497178431Sscf 498178431Sscf /* Assign non-member information to structure. */ 499178431Sscf gr->gr_name = line; 500178431Sscf if ((loc = strchr(line, ':')) == NULL) 501178431Sscf return (false); 502178431Sscf *loc = '\0'; 503178431Sscf gr->gr_passwd = loc + 1; 504184831Sscf if (*gr->gr_passwd == ':') 505184831Sscf *gr->gr_passwd = '\0'; 506178431Sscf else { 507178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 508178431Sscf return (false); 509178431Sscf *loc = '\0'; 510178431Sscf } 511184831Sscf if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) 512178431Sscf return (false); 513178431Sscf 514178431Sscf /* Assign member information to structure. */ 515178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 516178431Sscf return (false); 517178431Sscf line = loc + 1; 518178431Sscf gr->gr_mem = NULL; 519185237Sscf ndx = 0; 520185237Sscf do { 521185237Sscf gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * 522185237Sscf (ndx + 1)); 523185237Sscf if (gr->gr_mem == NULL) 524185237Sscf return (false); 525185237Sscf 526185237Sscf /* Skip locations without members (i.e., empty string). */ 527178431Sscf do { 528178431Sscf gr->gr_mem[ndx] = strsep(&line, ","); 529185237Sscf } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); 530185237Sscf } while (gr->gr_mem[ndx++] != NULL); 531178431Sscf 532178431Sscf return (true); 533178431Sscf} 534178431Sscf 535178431Sscf/* 536178431Sscf * Create a struct group from a line. 537178431Sscf */ 538178431Sscfstruct group * 539178431Sscfgr_scan(const char *line) 540178431Sscf{ 541184831Sscf struct group gr; 542185237Sscf char *line_copy; 543185237Sscf struct group *new_gr; 544178431Sscf 545185237Sscf if ((line_copy = strdup(line)) == NULL) 546178431Sscf return (NULL); 547185237Sscf if (!__gr_scan(line_copy, &gr)) { 548185237Sscf free(line_copy); 549178431Sscf return (NULL); 550178431Sscf } 551185237Sscf new_gr = gr_dup(&gr); 552185237Sscf free(line_copy); 553178431Sscf if (gr.gr_mem != NULL) 554178431Sscf free(gr.gr_mem); 555178431Sscf 556185237Sscf return (new_gr); 557178431Sscf} 558