pw_conf.c revision 285412
120253Sjoerg/*- 220302Sjoerg * Copyright (C) 1996 320302Sjoerg * David L. Nugent. All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920302Sjoerg * notice, this list of conditions and the following disclaimer. 1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer in the 1220253Sjoerg * documentation and/or other materials provided with the distribution. 1320253Sjoerg * 1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1720302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2420253Sjoerg * SUCH DAMAGE. 2520253Sjoerg */ 2620253Sjoerg 2730259Scharnier#ifndef lint 2830259Scharnierstatic const char rcsid[] = 2950479Speter "$FreeBSD: head/usr.sbin/pw/pw_conf.c 285412 2015-07-11 23:07:17Z bapt $"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 32282681Sbapt#include <sys/types.h> 33282681Sbapt#include <sys/sbuf.h> 3420253Sjoerg#include <string.h> 3520253Sjoerg#include <ctype.h> 3620253Sjoerg#include <fcntl.h> 37282718Sbapt#include <err.h> 3820253Sjoerg 3920253Sjoerg#include "pw.h" 4020253Sjoerg 4120253Sjoerg#define debugging 0 4220253Sjoerg 4320253Sjoergenum { 4420253Sjoerg _UC_NONE, 4520253Sjoerg _UC_DEFAULTPWD, 4620253Sjoerg _UC_REUSEUID, 4720253Sjoerg _UC_REUSEGID, 4821330Sdavidn _UC_NISPASSWD, 4920253Sjoerg _UC_DOTDIR, 5020253Sjoerg _UC_NEWMAIL, 5120253Sjoerg _UC_LOGFILE, 5220253Sjoerg _UC_HOMEROOT, 53168044Sle _UC_HOMEMODE, 5420253Sjoerg _UC_SHELLPATH, 5520253Sjoerg _UC_SHELLS, 5620253Sjoerg _UC_DEFAULTSHELL, 5720253Sjoerg _UC_DEFAULTGROUP, 5820253Sjoerg _UC_EXTRAGROUPS, 5920253Sjoerg _UC_DEFAULTCLASS, 6020253Sjoerg _UC_MINUID, 6120253Sjoerg _UC_MAXUID, 6220253Sjoerg _UC_MINGID, 6320253Sjoerg _UC_MAXGID, 6420253Sjoerg _UC_EXPIRE, 6520253Sjoerg _UC_PASSWORD, 6620253Sjoerg _UC_FIELDS 6720253Sjoerg}; 6820253Sjoerg 6920253Sjoergstatic char bourne_shell[] = "sh"; 7020253Sjoerg 7120253Sjoergstatic char *system_shells[_UC_MAXSHELLS] = 7220253Sjoerg{ 7320253Sjoerg bourne_shell, 7463239Sdavidn "csh", 7563239Sdavidn "tcsh" 7620253Sjoerg}; 7720253Sjoerg 7820253Sjoergstatic char const *booltrue[] = 7920253Sjoerg{ 8020253Sjoerg "yes", "true", "1", "on", NULL 8120253Sjoerg}; 8220253Sjoergstatic char const *boolfalse[] = 8320253Sjoerg{ 8420253Sjoerg "no", "false", "0", "off", NULL 8520253Sjoerg}; 8620253Sjoerg 8720253Sjoergstatic struct userconf config = 8820253Sjoerg{ 8920253Sjoerg 0, /* Default password for new users? (nologin) */ 9020253Sjoerg 0, /* Reuse uids? */ 9120253Sjoerg 0, /* Reuse gids? */ 9221330Sdavidn NULL, /* NIS version of the passwd file */ 9320253Sjoerg "/usr/share/skel", /* Where to obtain skeleton files */ 9420253Sjoerg NULL, /* Mail to send to new accounts */ 9520253Sjoerg "/var/log/userlog", /* Where to log changes */ 9620253Sjoerg "/home", /* Where to create home directory */ 97219408Sjkim _DEF_DIRMODE, /* Home directory perms, modified by umask */ 9820253Sjoerg "/bin", /* Where shells are located */ 9920253Sjoerg system_shells, /* List of shells (first is default) */ 10020253Sjoerg bourne_shell, /* Default shell */ 10120253Sjoerg NULL, /* Default group name */ 10220747Sdavidn NULL, /* Default (additional) groups */ 10320253Sjoerg NULL, /* Default login class */ 10420253Sjoerg 1000, 32000, /* Allowed range of uids */ 10520253Sjoerg 1000, 32000, /* Allowed range of gids */ 10620253Sjoerg 0, /* Days until account expires */ 107285412Sbapt 0 /* Days until password expires */ 10820253Sjoerg}; 10920253Sjoerg 11020253Sjoergstatic char const *comments[_UC_FIELDS] = 11120253Sjoerg{ 11220253Sjoerg "#\n# pw.conf - user/group configuration defaults\n#\n", 11320253Sjoerg "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 11420253Sjoerg "\n# Reuse gaps in uid sequence? (yes or no)\n", 11520253Sjoerg "\n# Reuse gaps in gid sequence? (yes or no)\n", 11621330Sdavidn "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 11720253Sjoerg "\n# Obtain default dotfiles from this directory\n", 11820253Sjoerg "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 11920253Sjoerg "\n# Log add/change/remove information in this file\n", 12020253Sjoerg "\n# Root directory in which $HOME directory is created\n", 121168044Sle "\n# Mode for the new $HOME directory, will be modified by umask\n", 12220253Sjoerg "\n# Colon separated list of directories containing valid shells\n", 12370133Sdougb "\n# Comma separated list of available shells (without paths)\n", 12420253Sjoerg "\n# Default shell (without path)\n", 12520253Sjoerg "\n# Default group (leave blank for new group per user)\n", 12620253Sjoerg "\n# Extra groups for new users\n", 12720253Sjoerg "\n# Default login class for new users\n", 12820253Sjoerg "\n# Range of valid default user ids\n", 12920253Sjoerg NULL, 13020253Sjoerg "\n# Range of valid default group ids\n", 13120253Sjoerg NULL, 13220253Sjoerg "\n# Days after which account expires (0=disabled)\n", 13320253Sjoerg "\n# Days after which password expires (0=disabled)\n" 13420253Sjoerg}; 13520253Sjoerg 13620253Sjoergstatic char const *kwds[] = 13720253Sjoerg{ 13820253Sjoerg "", 13920253Sjoerg "defaultpasswd", 14020253Sjoerg "reuseuids", 14120253Sjoerg "reusegids", 14221330Sdavidn "nispasswd", 14320253Sjoerg "skeleton", 14420253Sjoerg "newmail", 14520253Sjoerg "logfile", 14620253Sjoerg "home", 147168044Sle "homemode", 14820253Sjoerg "shellpath", 14920253Sjoerg "shells", 15020253Sjoerg "defaultshell", 15120253Sjoerg "defaultgroup", 15220253Sjoerg "extragroups", 15320253Sjoerg "defaultclass", 15420253Sjoerg "minuid", 15520253Sjoerg "maxuid", 15620253Sjoerg "mingid", 15720253Sjoerg "maxgid", 15820253Sjoerg "expire_days", 15920253Sjoerg "password_days", 16020253Sjoerg NULL 16120253Sjoerg}; 16220253Sjoerg 16320253Sjoergstatic char * 16420253Sjoergunquote(char const * str) 16520253Sjoerg{ 16620253Sjoerg if (str && (*str == '"' || *str == '\'')) { 16720253Sjoerg char *p = strchr(str + 1, *str); 16820253Sjoerg 16920253Sjoerg if (p != NULL) 17020253Sjoerg *p = '\0'; 17120253Sjoerg return (char *) (*++str ? str : NULL); 17220253Sjoerg } 17320253Sjoerg return (char *) str; 17420253Sjoerg} 17520253Sjoerg 17620253Sjoergint 17720253Sjoergboolean_val(char const * str, int dflt) 17820253Sjoerg{ 17920253Sjoerg if ((str = unquote(str)) != NULL) { 18020253Sjoerg int i; 18120253Sjoerg 18220253Sjoerg for (i = 0; booltrue[i]; i++) 18320253Sjoerg if (strcmp(str, booltrue[i]) == 0) 18420253Sjoerg return 1; 18520253Sjoerg for (i = 0; boolfalse[i]; i++) 18620253Sjoerg if (strcmp(str, boolfalse[i]) == 0) 18720253Sjoerg return 0; 18820253Sjoerg 18920253Sjoerg /* 19020253Sjoerg * Special cases for defaultpassword 19120253Sjoerg */ 19220253Sjoerg if (strcmp(str, "random") == 0) 19320253Sjoerg return -1; 19420253Sjoerg if (strcmp(str, "none") == 0) 19520253Sjoerg return -2; 19620253Sjoerg } 19720253Sjoerg return dflt; 19820253Sjoerg} 19920253Sjoerg 20020253Sjoergchar const * 20120253Sjoergboolean_str(int val) 20220253Sjoerg{ 20320253Sjoerg if (val == -1) 20420253Sjoerg return "random"; 20520253Sjoerg else if (val == -2) 20620253Sjoerg return "none"; 20720253Sjoerg else 20820253Sjoerg return val ? booltrue[0] : boolfalse[0]; 20920253Sjoerg} 21020253Sjoerg 21120253Sjoergchar * 21220253Sjoergnewstr(char const * p) 21320253Sjoerg{ 214282718Sbapt char *q; 21520253Sjoerg 216282718Sbapt if ((p = unquote(p)) == NULL) 217282718Sbapt return (NULL); 21820253Sjoerg 219282719Sbapt if ((q = strdup(p)) == NULL) 220282719Sbapt err(1, "strdup()"); 221282718Sbapt 222282718Sbapt return (q); 22320253Sjoerg} 22420253Sjoerg 22520253Sjoergstruct userconf * 22620253Sjoergread_userconfig(char const * file) 22720253Sjoerg{ 228264781Sbapt FILE *fp; 229264781Sbapt char *buf, *p; 230264781Sbapt size_t linecap; 231264781Sbapt ssize_t linelen; 23220253Sjoerg 233264781Sbapt buf = NULL; 234264781Sbapt linecap = 0; 235264781Sbapt 236285412Sbapt config.groups = sl_init(); 237282720Sbapt if (config.groups == NULL) 238285412Sbapt err(1, "sl_init()"); 23920253Sjoerg if (file == NULL) 24020253Sjoerg file = _PATH_PW_CONF; 241264781Sbapt 242283815Sbapt if ((fp = fopen(file, "r")) == NULL) 243283815Sbapt return (&config); 24420747Sdavidn 245283815Sbapt while ((linelen = getline(&buf, &linecap, fp)) > 0) { 246283815Sbapt if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 247283815Sbapt static char const toks[] = " \t\r\n,="; 248283815Sbapt char *q = strtok(NULL, toks); 249283815Sbapt int i = 0; 250283815Sbapt mode_t *modeset; 251283815Sbapt 252283815Sbapt while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 253283815Sbapt ++i; 25420253Sjoerg#if debugging 255283815Sbapt if (i == _UC_FIELDS) 256283815Sbapt printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 257283815Sbapt else 258283815Sbapt printf("Got kwd[%s]=%s\n", p, q); 25920253Sjoerg#endif 260283815Sbapt switch (i) { 261283815Sbapt case _UC_DEFAULTPWD: 262283815Sbapt config.default_password = boolean_val(q, 1); 263283815Sbapt break; 264283815Sbapt case _UC_REUSEUID: 265283815Sbapt config.reuse_uids = boolean_val(q, 0); 266283815Sbapt break; 267283815Sbapt case _UC_REUSEGID: 268283815Sbapt config.reuse_gids = boolean_val(q, 0); 269283815Sbapt break; 270283815Sbapt case _UC_NISPASSWD: 271283815Sbapt config.nispasswd = (q == NULL || !boolean_val(q, 1)) 272283815Sbapt ? NULL : newstr(q); 273283815Sbapt break; 274283815Sbapt case _UC_DOTDIR: 275283815Sbapt config.dotdir = (q == NULL || !boolean_val(q, 1)) 276283815Sbapt ? NULL : newstr(q); 277283815Sbapt break; 27820747Sdavidn case _UC_NEWMAIL: 279283815Sbapt config.newmail = (q == NULL || !boolean_val(q, 1)) 280283815Sbapt ? NULL : newstr(q); 281283815Sbapt break; 282283815Sbapt case _UC_LOGFILE: 283283815Sbapt config.logfile = (q == NULL || !boolean_val(q, 1)) 284283815Sbapt ? NULL : newstr(q); 285283815Sbapt break; 286283815Sbapt case _UC_HOMEROOT: 287283815Sbapt config.home = (q == NULL || !boolean_val(q, 1)) 288283815Sbapt ? "/home" : newstr(q); 289283815Sbapt break; 290283815Sbapt case _UC_HOMEMODE: 291283815Sbapt modeset = setmode(q); 292283815Sbapt config.homemode = (q == NULL || !boolean_val(q, 1)) 293283815Sbapt ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); 294283815Sbapt free(modeset); 295283815Sbapt break; 296283815Sbapt case _UC_SHELLPATH: 297283815Sbapt config.shelldir = (q == NULL || !boolean_val(q, 1)) 298283815Sbapt ? "/bin" : newstr(q); 299283815Sbapt break; 300283815Sbapt case _UC_SHELLS: 301283815Sbapt for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 302283815Sbapt system_shells[i] = newstr(q); 303283815Sbapt if (i > 0) 304283815Sbapt while (i < _UC_MAXSHELLS) 305283815Sbapt system_shells[i++] = NULL; 306283815Sbapt break; 307283815Sbapt case _UC_DEFAULTSHELL: 308283815Sbapt config.shell_default = (q == NULL || !boolean_val(q, 1)) 309283815Sbapt ? (char *) bourne_shell : newstr(q); 310283815Sbapt break; 311283815Sbapt case _UC_DEFAULTGROUP: 312283815Sbapt q = unquote(q); 313283815Sbapt config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 314283815Sbapt ? NULL : newstr(q); 315283815Sbapt break; 316283815Sbapt case _UC_EXTRAGROUPS: 317285412Sbapt for (i = 0; q != NULL; q = strtok(NULL, toks)) 318285412Sbapt sl_add(config.groups, newstr(q)); 319283815Sbapt break; 320283815Sbapt case _UC_DEFAULTCLASS: 321283815Sbapt config.default_class = (q == NULL || !boolean_val(q, 1)) 322283815Sbapt ? NULL : newstr(q); 323283815Sbapt break; 324283815Sbapt case _UC_MINUID: 325283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 326283815Sbapt config.min_uid = (uid_t) atol(q); 327283815Sbapt break; 328283815Sbapt case _UC_MAXUID: 329283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 330283815Sbapt config.max_uid = (uid_t) atol(q); 331283815Sbapt break; 332283815Sbapt case _UC_MINGID: 333283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 334283815Sbapt config.min_gid = (gid_t) atol(q); 335283815Sbapt break; 336283815Sbapt case _UC_MAXGID: 337283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 338283815Sbapt config.max_gid = (gid_t) atol(q); 339283815Sbapt break; 340283815Sbapt case _UC_EXPIRE: 341283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 342283815Sbapt config.expire_days = atoi(q); 343283815Sbapt break; 344283815Sbapt case _UC_PASSWORD: 345283815Sbapt if ((q = unquote(q)) != NULL && isdigit(*q)) 346283815Sbapt config.password_days = atoi(q); 347283815Sbapt break; 348283815Sbapt case _UC_FIELDS: 349283815Sbapt case _UC_NONE: 350283815Sbapt break; 35120253Sjoerg } 35220253Sjoerg } 35320253Sjoerg } 354283818Sbapt free(buf); 355283818Sbapt fclose(fp); 356283818Sbapt 357283815Sbapt return (&config); 35820253Sjoerg} 35920253Sjoerg 36020253Sjoerg 36120253Sjoergint 36220253Sjoergwrite_userconfig(char const * file) 36320253Sjoerg{ 36420253Sjoerg int fd; 365282697Sbapt int i, j; 366282681Sbapt struct sbuf *buf; 367282697Sbapt FILE *fp; 36820253Sjoerg 36920253Sjoerg if (file == NULL) 37020253Sjoerg file = _PATH_PW_CONF; 37120253Sjoerg 372282697Sbapt if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1) 373282697Sbapt return (0); 37420253Sjoerg 375282697Sbapt if ((fp = fdopen(fd, "w")) == NULL) { 376282697Sbapt close(fd); 377282697Sbapt return (0); 378282697Sbapt } 379282681Sbapt 380282697Sbapt buf = sbuf_new_auto(); 381282697Sbapt for (i = _UC_NONE; i < _UC_FIELDS; i++) { 382282697Sbapt int quote = 1; 38320253Sjoerg 384282697Sbapt sbuf_clear(buf); 385282697Sbapt switch (i) { 386282697Sbapt case _UC_DEFAULTPWD: 387282697Sbapt sbuf_cat(buf, boolean_str(config.default_password)); 388282697Sbapt break; 389282697Sbapt case _UC_REUSEUID: 390282697Sbapt sbuf_cat(buf, boolean_str(config.reuse_uids)); 391282697Sbapt break; 392282697Sbapt case _UC_REUSEGID: 393282697Sbapt sbuf_cat(buf, boolean_str(config.reuse_gids)); 394282697Sbapt break; 395282697Sbapt case _UC_NISPASSWD: 396282697Sbapt sbuf_cat(buf, config.nispasswd ? config.nispasswd : 397282697Sbapt ""); 398282697Sbapt quote = 0; 399282697Sbapt break; 400282697Sbapt case _UC_DOTDIR: 401282697Sbapt sbuf_cat(buf, config.dotdir ? config.dotdir : 402282697Sbapt boolean_str(0)); 403282697Sbapt break; 404282697Sbapt case _UC_NEWMAIL: 405282697Sbapt sbuf_cat(buf, config.newmail ? config.newmail : 406282697Sbapt boolean_str(0)); 407282697Sbapt break; 408282697Sbapt case _UC_LOGFILE: 409282697Sbapt sbuf_cat(buf, config.logfile ? config.logfile : 410282697Sbapt boolean_str(0)); 411282697Sbapt break; 412282697Sbapt case _UC_HOMEROOT: 413282697Sbapt sbuf_cat(buf, config.home); 414282697Sbapt break; 415282697Sbapt case _UC_HOMEMODE: 416282697Sbapt sbuf_printf(buf, "%04o", config.homemode); 417282697Sbapt quote = 0; 418282697Sbapt break; 419282697Sbapt case _UC_SHELLPATH: 420282697Sbapt sbuf_cat(buf, config.shelldir); 421282697Sbapt break; 422282697Sbapt case _UC_SHELLS: 423282697Sbapt for (j = 0; j < _UC_MAXSHELLS && 424282697Sbapt system_shells[j] != NULL; j++) 425282697Sbapt sbuf_printf(buf, "%s\"%s\"", j ? 426282697Sbapt "," : "", system_shells[j]); 427282697Sbapt quote = 0; 428282697Sbapt break; 429282697Sbapt case _UC_DEFAULTSHELL: 430282697Sbapt sbuf_cat(buf, config.shell_default ? 431282697Sbapt config.shell_default : bourne_shell); 432282697Sbapt break; 433282697Sbapt case _UC_DEFAULTGROUP: 434282697Sbapt sbuf_cat(buf, config.default_group ? 435282697Sbapt config.default_group : ""); 436282697Sbapt break; 437282697Sbapt case _UC_EXTRAGROUPS: 438285412Sbapt for (j = 0; config.groups != NULL && 439285412Sbapt j < (int)config.groups->sl_cur; j++) 440282697Sbapt sbuf_printf(buf, "%s\"%s\"", j ? 441285412Sbapt "," : "", config.groups->sl_str[j]); 442282697Sbapt quote = 0; 443282697Sbapt break; 444282697Sbapt case _UC_DEFAULTCLASS: 445282697Sbapt sbuf_cat(buf, config.default_class ? 446282697Sbapt config.default_class : ""); 447282697Sbapt break; 448282697Sbapt case _UC_MINUID: 449283842Sbapt sbuf_printf(buf, "%u", config.min_uid); 450282697Sbapt quote = 0; 451282697Sbapt break; 452282697Sbapt case _UC_MAXUID: 453283842Sbapt sbuf_printf(buf, "%u", config.max_uid); 454282697Sbapt quote = 0; 455282697Sbapt break; 456282697Sbapt case _UC_MINGID: 457283842Sbapt sbuf_printf(buf, "%u", config.min_gid); 458282697Sbapt quote = 0; 459282697Sbapt break; 460282697Sbapt case _UC_MAXGID: 461283842Sbapt sbuf_printf(buf, "%u", config.max_gid); 462282697Sbapt quote = 0; 463282697Sbapt break; 464282697Sbapt case _UC_EXPIRE: 465282697Sbapt sbuf_printf(buf, "%d", config.expire_days); 466282697Sbapt quote = 0; 467282697Sbapt break; 468282697Sbapt case _UC_PASSWORD: 469282697Sbapt sbuf_printf(buf, "%d", config.password_days); 470282697Sbapt quote = 0; 471282697Sbapt break; 472282697Sbapt case _UC_NONE: 473282697Sbapt break; 474282697Sbapt } 475282697Sbapt sbuf_finish(buf); 47620253Sjoerg 477282697Sbapt if (comments[i]) 478282697Sbapt fputs(comments[i], fp); 47920253Sjoerg 480282697Sbapt if (*kwds[i]) { 481282697Sbapt if (quote) 482282697Sbapt fprintf(fp, "%s = \"%s\"\n", kwds[i], 483282697Sbapt sbuf_data(buf)); 484282697Sbapt else 485282697Sbapt fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf)); 48620253Sjoerg#if debugging 487282697Sbapt printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf)); 48820253Sjoerg#endif 48920253Sjoerg } 49020253Sjoerg } 491282697Sbapt sbuf_delete(buf); 492282697Sbapt return (fclose(fp) != EOF); 49320253Sjoerg} 494