Home | History | Annotate | Download | only in lpadmin
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     22 /*	  All Rights Reserved  	*/
     23 
     24 
     25 /*
     26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     27  * Use is subject to license terms.
     28  */
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 #include <stdio.h>
     33 #include <string.h>
     34 #include <errno.h>
     35 #include <limits.h>
     36 #include <sys/types.h>
     37 #include <sys/zone.h>
     38 #include <stdlib.h>
     39 #include <libintl.h>
     40 #include <sys/tsol/label_macro.h>
     41 #include <bsm/devices.h>
     42 #include "lp.h"
     43 #include "class.h"
     44 #if defined PS_FAULTED
     45 #undef	PS_FAULTED
     46 #endif
     47 #include "printers.h"
     48 #include "msgs.h"
     49 
     50 #define	WHO_AM_I	I_AM_LPADMIN
     51 #include	"oam.h"
     52 
     53 #include "lpadmin.h"
     54 
     55 extern	void	fromallclasses();
     56 
     57 #if     !defined(PATH_MAX)
     58 # define PATH_MAX       1024
     59 #endif
     60 #if     PATH_MAX < 1024
     61 # undef PATH_MAX
     62 # define PATH_MAX       1024
     63 #endif
     64 
     65 extern char		*label;
     66 
     67 static void		configure_printer();
     68 static char		*fullpath();
     69 char			*nameit();
     70 static void		pack_white(char *ptr);
     71 
     72 /**
     73  ** do_printer() - CREATE OR CHANGE PRINTER
     74  **/
     75 
     76 void			do_printer ()
     77 {
     78 	int			rc;
     79 
     80 	/*
     81 	 * Set or change the printer configuration.
     82 	 */
     83 	if (strlen(modifications))
     84 		configure_printer (modifications);
     85 
     86 	/*
     87 	 * Allow/deny forms.
     88 	 */
     89 	BEGIN_CRITICAL
     90 		if (!oldp)
     91 			if (allow_form_printer(getlist(NAME_NONE, "", ","), p) == -1) {
     92 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
     93 				done(1);
     94 			}
     95 
     96 		if (f_allow || f_deny) {
     97 			if (f_allow && allow_form_printer(f_allow, p) == -1) {
     98 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
     99 				done(1);
    100 			}
    101 
    102 			if (f_deny && deny_form_printer(f_deny, p) == -1) {
    103 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    104 				done(1);
    105 			}
    106 		}
    107 	END_CRITICAL
    108 
    109 	/* Add/remove types of paper */
    110 
    111 	BEGIN_CRITICAL
    112 		if (!oldp)
    113 			if (add_paper_to_printer(getlist(NAME_NONE, "", ","),p) == -1) {
    114 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    115 				done(1);
    116 			}
    117 
    118 
    119 			if (p_add && add_paper_to_printer(p_add, p) == -1) {
    120 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    121 				done(1);
    122 			}
    123 
    124 			if (p_remove && remove_paper_from_printer(p_remove, p) == -1) {
    125 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    126 				done(1);
    127 			}
    128 	END_CRITICAL
    129 
    130 	/*
    131 	 * Allow/deny users.
    132 	 */
    133 	BEGIN_CRITICAL
    134 		if (!oldp)
    135 			if (allow_user_printer(getlist(NAME_ALL, "", ","), p) == -1) {
    136 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    137 				done(1);
    138 			}
    139 
    140 		if (u_allow || u_deny) {
    141 			if (u_allow && allow_user_printer(u_allow, p) == -1) {
    142 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    143 				done(1);
    144 			}
    145 
    146 			if (u_deny && deny_user_printer(u_deny, p) == -1) {
    147 				LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR);
    148 				done(1);
    149 			}
    150 		}
    151 	END_CRITICAL
    152 
    153 	/*
    154 	 * Tell the Spooler about the printer
    155 	 */
    156 	send_message(S_LOAD_PRINTER, p, "", "");
    157 	rc = output(R_LOAD_PRINTER);
    158 
    159 	switch (rc) {
    160 	case MOK:
    161 		break;
    162 
    163 	case MNODEST:
    164 	case MERRDEST:
    165 		LP_ERRMSG (ERROR, E_ADM_ERRDEST);
    166 		done (1);
    167 		/*NOTREACHED*/
    168 
    169 	case MNOSPACE:
    170 		LP_ERRMSG (WARNING, E_ADM_NOPSPACE);
    171 		break;
    172 
    173 	case MNOPERM:	/* taken care of up front */
    174 	default:
    175 		LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
    176 		done (1);
    177 		/*NOTREACHED*/
    178 	}
    179 
    180 	/*
    181 	 * Now that the Spooler knows about the printer,
    182 	 * we can do the balance of the changes.
    183 	 */
    184 
    185 	/*
    186 	 * Mount or unmount form, print-wheel.
    187 	 */
    188 	if (M)
    189 		do_mount(p, (f? f : (char *)0), (S? *S : (char *)0));
    190 	else if (t) do_max_trays(p);
    191 
    192 	/*
    193 	 * Display the alert type.
    194 	 */
    195 	if (A && STREQU(A, NAME_LIST)) {
    196 		if (label)
    197 			(void) printf(gettext("Printer %s: "), label);
    198 		printalert (stdout, &(oldp->fault_alert), 1);
    199 	}
    200 
    201 	/*
    202 	 * -A quiet.
    203 	 */
    204 	if (A && STREQU(A, NAME_QUIET)) {
    205 
    206 		send_message(S_QUIET_ALERT, p, (char *)QA_PRINTER, "");
    207 		rc = output(R_QUIET_ALERT);
    208 
    209 		switch(rc) {
    210 		case MOK:
    211 			break;
    212 
    213 		case MNODEST:	/* not quite, but not a lie either */
    214 		case MERRDEST:
    215 			LP_ERRMSG1 (WARNING, E_LP_NOQUIET, p);
    216 			break;
    217 
    218 		case MNOPERM:	/* taken care of up front */
    219 		default:
    220 			LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
    221 			done (1);
    222 			/*NOTREACHED*/
    223 		}
    224 	}
    225 
    226 	/*
    227 	 * Add printer p to class c
    228 	 */
    229 	if (c)  {
    230 		CLASS			*pc,
    231 					clsbuf;
    232 
    233 		if (STREQU(c, NAME_ANY))
    234 			c = NAME_ALL;
    235 
    236 Loop:		if (!(pc = getclass(c))) {
    237 			if (STREQU(c, NAME_ALL))
    238 				goto Done;
    239 
    240 			if (errno != ENOENT) {
    241 				LP_ERRMSG2 (
    242 					ERROR,
    243 					E_LP_GETCLASS,
    244 					c,
    245 					PERROR
    246 				);
    247 				done (1);
    248 			}
    249 
    250 			/*
    251 			 * Create the class
    252 			 */
    253 			clsbuf.name = strdup(c);
    254 			clsbuf.members = 0;
    255 			if (addlist(&clsbuf.members, p) == -1) {
    256 				LP_ERRMSG (ERROR, E_LP_MALLOC);
    257 				done (1);
    258 			}
    259 			pc = &clsbuf;
    260 
    261 		} else if (searchlist(p, pc->members))
    262 			LP_ERRMSG2 (WARNING, E_ADM_INCLASS, p, pc->name);
    263 
    264 		else if (addlist(&pc->members, p) == -1) {
    265 			LP_ERRMSG (ERROR, E_LP_MALLOC);
    266 			done (1);
    267 		}
    268 
    269 		BEGIN_CRITICAL
    270 			if (putclass(pc->name, pc) == -1) {
    271 				LP_ERRMSG2 (
    272 					ERROR,
    273 					E_LP_PUTCLASS,
    274 					pc->name,
    275 					PERROR
    276 				);
    277 				done(1);
    278 			}
    279 		END_CRITICAL
    280 
    281 		send_message (S_LOAD_CLASS, pc->name);
    282 		rc = output(R_LOAD_CLASS);
    283 
    284 		switch(rc) {
    285 		case MOK:
    286 			break;
    287 
    288 		case MNODEST:
    289 		case MERRDEST:
    290 			LP_ERRMSG (ERROR, E_ADM_ERRDEST);
    291 			done (1);
    292 			/*NOTREACHED*/
    293 
    294 		case MNOSPACE:
    295 			LP_ERRMSG (WARNING, E_ADM_NOCSPACE);
    296 			break;
    297 
    298 		case MNOPERM:	/* taken care of up front */
    299 		default:
    300 			LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc);
    301 			done (1);
    302 			/*NOTREACHED*/
    303 		}
    304 
    305 		if (STREQU(c, NAME_ALL))
    306 			goto Loop;
    307 	}
    308 Done:
    309 	/*
    310 	 * Remove printer p from class r
    311 	 */
    312 	if (r) {
    313 		if (STREQU(r, NAME_ALL) || STREQU(r, NAME_ANY))
    314 			fromallclasses(p);
    315 		else
    316 			fromclass(p, r);
    317 	}
    318 
    319 	return;
    320 }
    321 
    322 /**
    323  ** configure_printer() - SET OR CHANGE CONFIGURATION OF PRINTER
    324  **/
    325 
    326 static void		configure_printer (list)
    327 	char			*list;
    328 {
    329 	register PRINTER	*prbufp;
    330 
    331 	PRINTER			printer_struct;
    332 
    333 	char			type;
    334 	char *	infile_opts = NULL;
    335 
    336 
    337 	if (oldp) {
    338 
    339 		prbufp = oldp;
    340 
    341 		if (!T)
    342 			T = prbufp->printer_types;
    343 
    344 		if (!i && !e && !m)
    345 			/*
    346 			 * Don't copy the original interface program
    347 			 * again, but do keep the name of the original.
    348 			 */
    349 			ignprinter = BAD_INTERFACE;
    350 		else
    351 			ignprinter = 0;
    352 
    353 		/*
    354 		 * If we are making this a remote printer,
    355 		 * make sure that local-only attributes are
    356 		 * cleared.
    357 		 */
    358 		if (s) {
    359 			prbufp->banner = 0;
    360 			prbufp->cpi.val = 0;
    361 			prbufp->cpi.sc = 0;
    362 			prbufp->device = 0;
    363 			prbufp->dial_info = 0;
    364 			prbufp->fault_rec = 0;
    365 			prbufp->interface = 0;
    366 			prbufp->lpi.val = 0;
    367 			prbufp->lpi.sc = 0;
    368 			prbufp->plen.val = 0;
    369 			prbufp->plen.sc = 0;
    370 			prbufp->login = 0;
    371 			prbufp->speed = 0;
    372 			prbufp->stty = 0;
    373 			prbufp->pwid.val = 0;
    374 			prbufp->pwid.sc = 0;
    375 			prbufp->fault_alert.shcmd = strdup(NAME_NONE);
    376 			prbufp->fault_alert.Q = 0;
    377 			prbufp->fault_alert.W = 0;
    378 #if	defined(CAN_DO_MODULES)
    379 			prbufp->modules = 0;
    380 #endif
    381 
    382 		/*
    383 		 * If we are making this a local printer, make
    384 		 * sure that some local-only attributes are set.
    385 		 * (If the user has specified these as well, his/her
    386 		 * values will overwrite what we set here.)
    387 		 */
    388 		} else if (oldp->remote) {
    389 			prbufp->banner = BAN_ALWAYS;
    390 			prbufp->interface = makepath(Lp_Model, STANDARD, (char *)0);
    391 			prbufp->fault_alert.shcmd = nameit(NAME_MAIL);
    392 
    393 			/*
    394 			 * Being here means "!s && oldp->remote" is true,
    395 			 * i.e. this printer never had an interface pgm
    396 			 * before. Thus we can safely clear the following.
    397 			 * This is needed to let "putprinter()" copy the
    398 			 * (default) interface program.
    399 			 */
    400 			ignprinter = 0;
    401 		}
    402 
    403 	} else {
    404 		/*
    405 		 * The following takes care of the lion's share
    406 		 * of the initialization of a new printer structure.
    407 		 * However, special initialization (e.g. non-zero,
    408 		 * or substructure members) needs to be considered
    409 		 * for EACH NEW MEMBER added to the structure.
    410 		 */
    411 		(void)memset (&printer_struct, 0, sizeof(printer_struct));
    412 
    413 		prbufp = &printer_struct;
    414 		prbufp->banner = BAN_ALWAYS;
    415 		prbufp->cpi.val = 0;
    416 		prbufp->cpi.sc = 0;
    417 		if (!s)
    418 			prbufp->interface = makepath(Lp_Model, m, (char *)0);
    419 		prbufp->lpi.val = 0;
    420 		prbufp->lpi.sc = 0;
    421 		prbufp->plen.val = 0;
    422 		prbufp->plen.sc = 0;
    423 		prbufp->pwid.val = 0;
    424 		prbufp->pwid.sc = 0;
    425 		if (!s && !A)
    426 			prbufp->fault_alert.shcmd = nameit(NAME_MAIL);
    427 		prbufp->fault_alert.Q = 0;
    428 		prbufp->fault_alert.W = 0;
    429 		prbufp->options = NULL;
    430 	}
    431 
    432 	while ((type = *list++) != '\0')  switch(type) {
    433 
    434 	case 'A':
    435 		if (!s) {
    436 			if (STREQU(A, NAME_MAIL) || STREQU(A, NAME_WRITE))
    437 				prbufp->fault_alert.shcmd = nameit(A);
    438 			else if (!STREQU(A, NAME_QUIET))
    439 				prbufp->fault_alert.shcmd = A;
    440 		}
    441 		break;
    442 
    443 	case 'b':
    444 		if (!s)
    445 			prbufp->banner = banner;
    446 		break;
    447 
    448 	case 'c':
    449 		if (!s)
    450 			prbufp->cpi = cpi_sdn;
    451 		break;
    452 
    453 	case 'D':
    454 		prbufp->description = D;
    455 		break;
    456 
    457 	case 'e':
    458 		if (!s) {
    459 			prbufp->interface = makepath(
    460 				Lp_A_Interfaces,
    461 				e,
    462 				(char *)0
    463 			);
    464 		}
    465 		break;
    466 
    467 	case 'F':
    468 		if (!s)
    469 			prbufp->fault_rec = F;
    470 		break;
    471 
    472 #if	defined(CAN_DO_MODULES)
    473 	case 'H':
    474 		if (!s)
    475 			prbufp->modules = H;
    476 		break;
    477 #endif
    478 
    479 	case 'h':
    480 		if (!s)
    481 			prbufp->login = 0;
    482 		break;
    483 
    484 	case 'i':
    485 		if (!s)
    486 			prbufp->interface = fullpath(i);
    487 		break;
    488 
    489 	case 'I':
    490 		prbufp->input_types = I;
    491 		break;
    492 
    493 	case 'l':
    494 		if (!s)
    495 			prbufp->login = 1;
    496 		break;
    497 
    498 	case 'L':
    499 		if (!s)
    500 			prbufp->plen = length_sdn;
    501 		break;
    502 
    503 	case 'm':
    504 		if (!s)
    505 			prbufp->interface = makepath(Lp_Model, m, (char *)0);
    506 		break;
    507 
    508 	case 'M':
    509 		if (!s)
    510 			prbufp->lpi = lpi_sdn;
    511 		break;
    512 
    513 #ifdef LP_USE_PAPI_ATTR
    514 	case 'n':
    515 	{
    516 		if (n_opt != NULL)
    517 		{
    518 			if (*n_opt == '/')
    519 			{
    520 				prbufp->ppd = fullpath(n_opt);
    521 			}
    522 			else
    523 			{
    524 				prbufp->ppd =
    525 				    makepath(Lp_Model, "ppd", n_opt, (char *)0);
    526 			}
    527 			ppdopt = 1;
    528 		}
    529 		break;
    530 	}
    531 #endif
    532 
    533 	case 'o':
    534 		/*
    535 		 * The "undefined" key-value -o options
    536 		 *
    537 		 * Options requires special handling. It is a
    538 		 * list whose members are to be handled
    539 		 * individually.
    540 		 *
    541 		 * Need to: set new options, keep old options if not
    542 		 * redefined, remove old options if defined as "key=".
    543 		 *
    544 		 *
    545 		 * "p" is a global containing the printer name
    546 		 */
    547 
    548 		if (!s) {
    549 
    550 			if ((infile_opts = getpentry(p, PR_OPTIONS)) == NULL)
    551 				prbufp->options = o_options;
    552 			else {
    553 				prbufp->options =
    554 					pick_opts(infile_opts, o_options);
    555 			}
    556 		}
    557 		break;
    558 
    559 	case 'R':
    560 		if (s) {
    561 			prbufp->remote = s;
    562 			prbufp->dial_info = 0;
    563 			prbufp->device = 0;
    564 		} else
    565 			prbufp->remote = 0;
    566 		break;
    567 
    568 	case 's':
    569 		if (!s) {
    570 			/*
    571 			 * lpadmin always defers to stty
    572 			 */
    573 			prbufp->speed = 0;
    574 			prbufp->stty = stty_opt;
    575 		}
    576 		break;
    577 
    578 	case 'S':
    579 		if (!M)
    580 			if (STREQU(*S, NAME_NONE))
    581 				prbufp->char_sets = 0;
    582 			else
    583 				prbufp->char_sets = S;
    584 		break;
    585 
    586 	case 'T':
    587 		prbufp->printer_types = T;
    588 		break;
    589 
    590 	case 'U':
    591 		if (!s) {
    592 			prbufp->dial_info = U;
    593 			prbufp->device = 0;
    594 			prbufp->remote = 0;
    595 		}
    596 		break;
    597 
    598 	case 'v':
    599 		if (!s) {
    600 			prbufp->device = v;
    601 			prbufp->dial_info = 0;
    602 			prbufp->remote = 0;
    603 		}
    604 		break;
    605 
    606 	case 'w':
    607 		if (!s)
    608 			prbufp->pwid = width_sdn;
    609 		break;
    610 
    611 	case 'W':
    612 		if (!s)
    613 			prbufp->fault_alert.W = W;
    614 		break;
    615 
    616 	}
    617 
    618 
    619 	BEGIN_CRITICAL
    620 		if (putprinter(p, prbufp) == -1) {
    621 			if (
    622 				errno == EINVAL
    623 			     && (badprinter & BAD_INTERFACE)
    624 			)
    625 				LP_ERRMSG1 (
    626 					ERROR,
    627 					E_ADM_BADINTF,
    628 					prbufp->interface
    629 				);
    630 			else
    631 				LP_ERRMSG2 (
    632 					ERROR,
    633 					E_LP_PUTPRINTER,
    634 					p,
    635 					PERROR
    636 				);
    637 			done(1);
    638 		}
    639 
    640 		if ((getzoneid() == GLOBAL_ZONEID) && system_labeled &&
    641 		    (prbufp->device != NULL))
    642 			update_dev_dbs(p, prbufp->device, "ADD");
    643 
    644 	END_CRITICAL
    645 
    646 	return;
    647 }
    648 
    649 /**
    650  ** fullpath()
    651  **/
    652 
    653 static char		*fullpath (str)
    654 	char			*str;
    655 {
    656 	register char		*cur_dir,
    657 				*path;
    658 
    659 
    660 	while (*str && *str == ' ')
    661 		str++;
    662 	if (*str == '/')
    663 		return (str);
    664 
    665 	if (!(cur_dir = malloc(PATH_MAX + 1)))
    666 		return (str);
    667 
    668 	getcwd (cur_dir, PATH_MAX);
    669 	path = makepath(cur_dir, str, (char *)0);
    670 
    671 	/*
    672 	 * Here we could be nice and strip out /./ and /../
    673 	 * stuff, but it isn't necessary.
    674 	 */
    675 
    676 	return (path);
    677 }
    678 
    679 /**
    680  ** nameit() - ADD USER NAME TO COMMAND
    681  **/
    682 
    683 char			*nameit (cmd)
    684 	char			*cmd;
    685 {
    686 	register char		*nm = getname(),
    687 				*copy = malloc(
    688 					(unsigned) (strlen(cmd) + 1 +
    689 					strlen(nm) + 1)
    690 	);
    691 
    692 	(void) strcpy (copy, cmd);
    693 	(void) strcat (copy, " ");
    694 	(void) strcat (copy, nm);
    695 	return (copy);
    696 }
    697 
    698 /*
    699  * update_dev_dbs - ADD/REMOVE ENTRIES FOR THE PRINTER IN DEVICE
    700  * 			ALLOCATION FILES
    701  *
    702  * We intentionally ignore errors, since we don't want the printer
    703  * installation to be viewed as failing just because we didn't add
    704  * the device_allocate entry.
    705  *
    706  *	Input:
    707  *		prtname - printer name
    708  *		devname - device associated w/ this printer
    709  *		func - [ADD|REMOVE] entries in /etc/security/device_allocate
    710  *			and /etc/security/device_maps
    711  *
    712  *	Return:
    713  *		Always 'quiet' return.  Failures are ignored.
    714  */
    715 void
    716 update_dev_dbs(char *prtname, char *devname, char *func)
    717 {
    718 	int		fd, status;
    719 	pid_t		pid;
    720 
    721 	pid = fork();
    722 	switch (pid) {
    723 	case -1:
    724 		/* fork failed, just return quietly */
    725 		return;
    726 	case 0:
    727 		/* child */
    728 		/* redirect to /dev/null */
    729 		(void) close(1);
    730 		(void) close(2);
    731 		fd = open("/dev/null", O_WRONLY);
    732 		fd = dup(fd);
    733 
    734 		if (strcmp(func, "ADD") == 0) {
    735 			execl("/usr/sbin/add_allocatable", "add_allocatable",
    736 			    "-n", prtname, "-t", "lp", "-l", devname,
    737 			    "-o", "minlabel=admin_low:maxlabel=admin_high",
    738 			    "-a", "*", "-c", "/bin/true", NULL);
    739 		} else {
    740 			if (strcmp(func, "REMOVE") == 0) {
    741 				execl("/usr/sbin/remove_allocatable",
    742 				    "remove_allocatable", "-n", prtname, NULL);
    743 			}
    744 		}
    745 		_exit(1);
    746 		/* NOT REACHED */
    747 	default:
    748 		waitpid(pid, &status, 0);
    749 		return;
    750 	}
    751 }
    752 
    753 /*
    754  * pack_white(ptr) trims off multiple occurances of white space from a NULL
    755  * terminated string pointed to by "ptr".
    756  */
    757 static void
    758 pack_white(char *ptr)
    759 {
    760 	char	*tptr;
    761 	char	*mptr;
    762 	int	cnt;
    763 
    764 	if (ptr == NULL)
    765 		return;
    766 	cnt = strlen(ptr);
    767 	if (cnt == 0)
    768 		return;
    769 	mptr = (char *)calloc((unsigned)cnt+1, sizeof (char));
    770 	if (mptr == NULL)
    771 		return;
    772 	tptr = strtok(ptr, " \t");
    773 	while (tptr != NULL) {
    774 		(void) strcat(mptr, tptr);
    775 		(void) strcat(mptr, " ");
    776 		tptr = strtok(NULL, " \t");
    777 	}
    778 	cnt = strlen(mptr);
    779 	(void) strcpy(ptr, mptr);
    780 	free(mptr);
    781 }
    782