1*2264Sjacobs /* 2*2264Sjacobs * CDDL HEADER START 3*2264Sjacobs * 4*2264Sjacobs * The contents of this file are subject to the terms of the 5*2264Sjacobs * Common Development and Distribution License (the "License"). 6*2264Sjacobs * You may not use this file except in compliance with the License. 7*2264Sjacobs * 8*2264Sjacobs * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2264Sjacobs * or http://www.opensolaris.org/os/licensing. 10*2264Sjacobs * See the License for the specific language governing permissions 11*2264Sjacobs * and limitations under the License. 12*2264Sjacobs * 13*2264Sjacobs * When distributing Covered Code, include this CDDL HEADER in each 14*2264Sjacobs * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2264Sjacobs * If applicable, add the following below this CDDL HEADER, with the 16*2264Sjacobs * fields enclosed by brackets "[]" replaced with your own identifying 17*2264Sjacobs * information: Portions Copyright [yyyy] [name of copyright owner] 18*2264Sjacobs * 19*2264Sjacobs * CDDL HEADER END 20*2264Sjacobs */ 21*2264Sjacobs 22*2264Sjacobs /* 23*2264Sjacobs * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*2264Sjacobs * Use is subject to license terms. 25*2264Sjacobs * 26*2264Sjacobs */ 27*2264Sjacobs 28*2264Sjacobs /* $Id: common.c 162 2006-05-08 14:17:44Z njacobs $ */ 29*2264Sjacobs 30*2264Sjacobs #pragma ident "%Z%%M% %I% %E% SMI" 31*2264Sjacobs 32*2264Sjacobs #include <stdio.h> 33*2264Sjacobs #include <stdlib.h> 34*2264Sjacobs #include <unistd.h> 35*2264Sjacobs #include <alloca.h> 36*2264Sjacobs #include <string.h> 37*2264Sjacobs #include <libintl.h> 38*2264Sjacobs #include <ctype.h> 39*2264Sjacobs #include <pwd.h> 40*2264Sjacobs #include <papi.h> 41*2264Sjacobs #include "common.h" 42*2264Sjacobs 43*2264Sjacobs #ifndef HAVE_GETPASSPHRASE /* some systems don't have getpassphrase() */ 44*2264Sjacobs #define getpassphrase getpass 45*2264Sjacobs #endif 46*2264Sjacobs 47*2264Sjacobs /* give the most verbose error message to the caller */ 48*2264Sjacobs char * 49*2264Sjacobs verbose_papi_message(papi_service_t svc, papi_status_t status) 50*2264Sjacobs { 51*2264Sjacobs char *mesg; 52*2264Sjacobs 53*2264Sjacobs mesg = papiServiceGetStatusMessage(svc); 54*2264Sjacobs 55*2264Sjacobs if (mesg == NULL) 56*2264Sjacobs mesg = papiStatusString(status); 57*2264Sjacobs 58*2264Sjacobs return (mesg); 59*2264Sjacobs } 60*2264Sjacobs 61*2264Sjacobs static int 62*2264Sjacobs match_job(int id, char *user, int ac, char *av[]) 63*2264Sjacobs { 64*2264Sjacobs int i; 65*2264Sjacobs 66*2264Sjacobs for (i = 0; i < ac; i++) 67*2264Sjacobs if (strcmp("-", av[i]) == 0) 68*2264Sjacobs return (0); /* "current" user match */ 69*2264Sjacobs else if ((isdigit(av[i][0]) != 0) && (id == atoi(av[i]))) 70*2264Sjacobs return (0); /* job-id match */ 71*2264Sjacobs else if (strcmp(user, av[i]) == 0) 72*2264Sjacobs return (0); /* user match */ 73*2264Sjacobs 74*2264Sjacobs return (-1); 75*2264Sjacobs } 76*2264Sjacobs 77*2264Sjacobs /* 78*2264Sjacobs * to support job/printer status 79*2264Sjacobs */ 80*2264Sjacobs static char * 81*2264Sjacobs state_string(int state) 82*2264Sjacobs { 83*2264Sjacobs switch (state) { 84*2264Sjacobs case 3: 85*2264Sjacobs return (gettext("idle")); 86*2264Sjacobs case 4: 87*2264Sjacobs return (gettext("processing")); 88*2264Sjacobs case 5: 89*2264Sjacobs return (gettext("stopped")); 90*2264Sjacobs default: 91*2264Sjacobs return (gettext("unknown")); 92*2264Sjacobs } 93*2264Sjacobs } 94*2264Sjacobs 95*2264Sjacobs static char *_rank_suffixes[] = { 96*2264Sjacobs "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 97*2264Sjacobs }; 98*2264Sjacobs 99*2264Sjacobs static char * 100*2264Sjacobs rank_string(const int rank) 101*2264Sjacobs { 102*2264Sjacobs static char buf[12]; 103*2264Sjacobs 104*2264Sjacobs if (rank < 0) 105*2264Sjacobs snprintf(buf, sizeof (buf), gettext("invalid")); 106*2264Sjacobs else if (rank == 0) 107*2264Sjacobs snprintf(buf, sizeof (buf), gettext("active")); 108*2264Sjacobs else if ((rank > 10) && (rank < 14)) 109*2264Sjacobs sprintf(buf, "%dth", rank); 110*2264Sjacobs else 111*2264Sjacobs sprintf(buf, "%d%s", rank, _rank_suffixes[rank % 10]); 112*2264Sjacobs 113*2264Sjacobs return (buf); 114*2264Sjacobs } 115*2264Sjacobs 116*2264Sjacobs static void 117*2264Sjacobs printer_state_line(FILE *fp, papi_printer_t p, int num_jobs, char *name) 118*2264Sjacobs { 119*2264Sjacobs papi_attribute_t **list = papiPrinterGetAttributeList(p); 120*2264Sjacobs int state = 0; 121*2264Sjacobs char *reason = ""; 122*2264Sjacobs 123*2264Sjacobs (void) papiAttributeListGetInteger(list, NULL, 124*2264Sjacobs "printer-state", &state); 125*2264Sjacobs (void) papiAttributeListGetString(list, NULL, 126*2264Sjacobs "printer-state-reasons", &reason); 127*2264Sjacobs (void) papiAttributeListGetString(list, NULL, 128*2264Sjacobs "printer-name", &name); 129*2264Sjacobs 130*2264Sjacobs if ((state != 0x03) || (num_jobs != 0)) { 131*2264Sjacobs fprintf(fp, "%s: %s", name, state_string(state)); 132*2264Sjacobs if (state == 0x05) /* stopped */ 133*2264Sjacobs fprintf(fp, ": %s\n", reason); 134*2264Sjacobs else 135*2264Sjacobs fprintf(fp, "\n"); 136*2264Sjacobs } else 137*2264Sjacobs fprintf(fp, "no entries\n"); 138*2264Sjacobs } 139*2264Sjacobs 140*2264Sjacobs static void 141*2264Sjacobs print_header(FILE *fp) 142*2264Sjacobs { 143*2264Sjacobs fprintf(fp, gettext("Rank\tOwner\t Job\tFile(s)\t\t\t\tTotal Size\n")); 144*2264Sjacobs } 145*2264Sjacobs 146*2264Sjacobs static void 147*2264Sjacobs print_job_line(FILE *fp, int count, papi_job_t job, int fmt, int ac, char *av[]) 148*2264Sjacobs { 149*2264Sjacobs papi_attribute_t **list = papiJobGetAttributeList(job); 150*2264Sjacobs int copies = 1, id = 0, rank = count, size = 0; 151*2264Sjacobs char *name = "print job"; 152*2264Sjacobs char *user = "nobody"; 153*2264Sjacobs char *host = "localhost"; 154*2264Sjacobs char *suffix = "k"; 155*2264Sjacobs 156*2264Sjacobs (void) papiAttributeListGetInteger(list, NULL, 157*2264Sjacobs "job-id", &id); 158*2264Sjacobs (void) papiAttributeListGetString(list, NULL, 159*2264Sjacobs "job-originating-user-name", &user); 160*2264Sjacobs 161*2264Sjacobs /* if we are looking and it doesn't match, return early */ 162*2264Sjacobs if ((ac > 0) && (match_job(id, user, ac, av) < 0)) 163*2264Sjacobs return; 164*2264Sjacobs 165*2264Sjacobs (void) papiAttributeListGetInteger(list, NULL, 166*2264Sjacobs "copies", &copies); 167*2264Sjacobs (void) papiAttributeListGetInteger(list, NULL, 168*2264Sjacobs "number-of-intervening-jobs", &rank); 169*2264Sjacobs 170*2264Sjacobs if (papiAttributeListGetInteger(list, NULL, "job-octets", &size) 171*2264Sjacobs == PAPI_OK) 172*2264Sjacobs suffix = "bytes"; 173*2264Sjacobs else 174*2264Sjacobs (void) papiAttributeListGetInteger(list, NULL, 175*2264Sjacobs "job-k-octets", &size); 176*2264Sjacobs (void) papiAttributeListGetString(list, NULL, 177*2264Sjacobs "job-name", &name); 178*2264Sjacobs 179*2264Sjacobs size *= copies; 180*2264Sjacobs 181*2264Sjacobs if (fmt == 3) { 182*2264Sjacobs fprintf(fp, gettext("%s\t%-8.8s %d\t%-32.32s%d %s\n"), 183*2264Sjacobs rank_string(++rank), user, id, name, size, suffix); 184*2264Sjacobs } else 185*2264Sjacobs fprintf(fp, gettext( 186*2264Sjacobs "\n%s: %s\t\t\t\t[job %d %s]\n\t%-32.32s\t%d %s\n"), 187*2264Sjacobs user, rank_string(++rank), id, host, name, size, 188*2264Sjacobs suffix); 189*2264Sjacobs } 190*2264Sjacobs 191*2264Sjacobs /* 192*2264Sjacobs * to support job cancelation 193*2264Sjacobs */ 194*2264Sjacobs static void 195*2264Sjacobs cancel_job(papi_service_t svc, FILE *fp, char *printer, papi_job_t job, 196*2264Sjacobs int ac, char *av[]) 197*2264Sjacobs { 198*2264Sjacobs papi_status_t status; 199*2264Sjacobs papi_attribute_t **list = papiJobGetAttributeList(job); 200*2264Sjacobs int id = 0; 201*2264Sjacobs char *user = ""; 202*2264Sjacobs char *mesg = gettext("cancelled"); 203*2264Sjacobs 204*2264Sjacobs papiAttributeListGetInteger(list, NULL, 205*2264Sjacobs "job-id", &id); 206*2264Sjacobs papiAttributeListGetString(list, NULL, 207*2264Sjacobs "job-originating-user-name", &user); 208*2264Sjacobs 209*2264Sjacobs /* if we are looking and it doesn't match, return early */ 210*2264Sjacobs if ((ac > 0) && (match_job(id, user, ac, av) < 0)) 211*2264Sjacobs return; 212*2264Sjacobs 213*2264Sjacobs status = papiJobCancel(svc, printer, id); 214*2264Sjacobs if (status != PAPI_OK) 215*2264Sjacobs mesg = papiStatusString(status); 216*2264Sjacobs 217*2264Sjacobs fprintf(fp, "%s-%d: %s\n", printer, id, mesg); 218*2264Sjacobs } 219*2264Sjacobs 220*2264Sjacobs int 221*2264Sjacobs berkeley_queue_report(papi_service_t svc, FILE *fp, char *dest, int fmt, 222*2264Sjacobs int ac, char *av[]) 223*2264Sjacobs { 224*2264Sjacobs papi_status_t status; 225*2264Sjacobs papi_printer_t p = NULL; 226*2264Sjacobs papi_job_t *jobs = NULL; 227*2264Sjacobs char *pattrs[] = { "printer-name", "printer-state", 228*2264Sjacobs "printer-state-reasons", NULL }; 229*2264Sjacobs char *jattrs[] = { 230*2264Sjacobs "job-name", "job-octets", "job-k-octets", 231*2264Sjacobs "job-originating-user-name", "job-id", 232*2264Sjacobs "number-of-intervening-jobs", NULL }; 233*2264Sjacobs int num_jobs = 0; 234*2264Sjacobs 235*2264Sjacobs status = papiPrinterQuery(svc, dest, pattrs, NULL, &p); 236*2264Sjacobs if (status != PAPI_OK) { 237*2264Sjacobs fprintf(fp, gettext( 238*2264Sjacobs "Failed to query service for state of %s: %s\n"), 239*2264Sjacobs dest, verbose_papi_message(svc, status)); 240*2264Sjacobs return (-1); 241*2264Sjacobs } 242*2264Sjacobs 243*2264Sjacobs status = papiPrinterListJobs(svc, dest, jattrs, PAPI_LIST_JOBS_ALL, 244*2264Sjacobs 0, &jobs); 245*2264Sjacobs if (status != PAPI_OK) { 246*2264Sjacobs fprintf(fp, gettext( 247*2264Sjacobs "Failed to query service for jobs on %s: %s\n"), 248*2264Sjacobs dest, verbose_papi_message(svc, status)); 249*2264Sjacobs return (-1); 250*2264Sjacobs } 251*2264Sjacobs if (jobs != NULL) { 252*2264Sjacobs while (jobs[num_jobs] != NULL) 253*2264Sjacobs num_jobs++; 254*2264Sjacobs } 255*2264Sjacobs 256*2264Sjacobs printer_state_line(fp, p, num_jobs, dest); 257*2264Sjacobs if (num_jobs > 0) { 258*2264Sjacobs int i; 259*2264Sjacobs 260*2264Sjacobs if (fmt == 3) 261*2264Sjacobs print_header(fp); 262*2264Sjacobs for (i = 0; jobs[i] != NULL; i++) 263*2264Sjacobs print_job_line(fp, i, jobs[i], fmt, ac, av); 264*2264Sjacobs } 265*2264Sjacobs 266*2264Sjacobs papiPrinterFree(p); 267*2264Sjacobs papiJobListFree(jobs); 268*2264Sjacobs 269*2264Sjacobs return (num_jobs); 270*2264Sjacobs } 271*2264Sjacobs 272*2264Sjacobs int 273*2264Sjacobs berkeley_cancel_request(papi_service_t svc, FILE *fp, char *dest, 274*2264Sjacobs int ac, char *av[]) 275*2264Sjacobs { 276*2264Sjacobs papi_status_t status; 277*2264Sjacobs papi_job_t *jobs = NULL; 278*2264Sjacobs char *jattrs[] = { "job-originating-user-name", "job-id", NULL }; 279*2264Sjacobs 280*2264Sjacobs status = papiPrinterListJobs(svc, dest, jattrs, PAPI_LIST_JOBS_ALL, 281*2264Sjacobs 0, &jobs); 282*2264Sjacobs 283*2264Sjacobs if (status != PAPI_OK) { 284*2264Sjacobs fprintf(fp, gettext("Failed to query service for %s: %s\n"), 285*2264Sjacobs dest, verbose_papi_message(svc, status)); 286*2264Sjacobs return (-1); 287*2264Sjacobs } 288*2264Sjacobs 289*2264Sjacobs /* cancel the job(s) */ 290*2264Sjacobs if (jobs != NULL) { 291*2264Sjacobs int i; 292*2264Sjacobs 293*2264Sjacobs for (i = 0; jobs[i] != NULL; i++) 294*2264Sjacobs cancel_job(svc, fp, dest, jobs[i], ac, av); 295*2264Sjacobs } 296*2264Sjacobs 297*2264Sjacobs papiJobListFree(jobs); 298*2264Sjacobs 299*2264Sjacobs return (0); 300*2264Sjacobs } 301*2264Sjacobs 302*2264Sjacobs int 303*2264Sjacobs get_printer_id(char *name, char **printer, int *id) 304*2264Sjacobs { 305*2264Sjacobs int result = -1; 306*2264Sjacobs 307*2264Sjacobs if (name != NULL) { 308*2264Sjacobs char *p = strrchr(name, '-'); 309*2264Sjacobs 310*2264Sjacobs *printer = name; 311*2264Sjacobs if (p != NULL) { 312*2264Sjacobs char *s = NULL; 313*2264Sjacobs 314*2264Sjacobs *id = strtol(p + 1, &s, 10); 315*2264Sjacobs if (s[0] != '\0') 316*2264Sjacobs *id = -1; 317*2264Sjacobs else 318*2264Sjacobs *p = '\0'; 319*2264Sjacobs result = 0; 320*2264Sjacobs } else 321*2264Sjacobs *id = -1; 322*2264Sjacobs } 323*2264Sjacobs 324*2264Sjacobs return (result); 325*2264Sjacobs } 326*2264Sjacobs 327*2264Sjacobs /* 328*2264Sjacobs * strsplit() splits a string into a NULL terminated array of substrings 329*2264Sjacobs * determined by a seperator. The original string is modified, and newly 330*2264Sjacobs * allocated space is only returned for the array itself. If more than 331*2264Sjacobs * 1024 substrings exist, they will be ignored. 332*2264Sjacobs */ 333*2264Sjacobs char ** 334*2264Sjacobs strsplit(char *string, const char *seperators) 335*2264Sjacobs { 336*2264Sjacobs char *list[BUFSIZ], 337*2264Sjacobs **result; 338*2264Sjacobs int length = 0; 339*2264Sjacobs 340*2264Sjacobs if ((string == NULL) || (seperators == NULL)) 341*2264Sjacobs return (NULL); 342*2264Sjacobs 343*2264Sjacobs (void) memset(list, 0, sizeof (list)); 344*2264Sjacobs for (list[length] = strtok(string, seperators); 345*2264Sjacobs (list[length] != NULL) && (length < (BUFSIZ - 2)); 346*2264Sjacobs list[length] = strtok(NULL, seperators)) 347*2264Sjacobs length++; 348*2264Sjacobs 349*2264Sjacobs if ((result = (char **)calloc(length+1, sizeof (char *))) != NULL) 350*2264Sjacobs (void) memcpy(result, list, length * sizeof (char *)); 351*2264Sjacobs 352*2264Sjacobs return (result); 353*2264Sjacobs } 354*2264Sjacobs 355*2264Sjacobs papi_status_t 356*2264Sjacobs jobSubmitSTDIN(papi_service_t svc, char *printer, papi_attribute_t **list, 357*2264Sjacobs papi_job_t *job) 358*2264Sjacobs { 359*2264Sjacobs papi_status_t status; 360*2264Sjacobs papi_stream_t stream = NULL; 361*2264Sjacobs int rc; 362*2264Sjacobs char buf[BUFSIZ]; 363*2264Sjacobs 364*2264Sjacobs status = papiJobStreamOpen(svc, printer, list, NULL, &stream); 365*2264Sjacobs while ((status == PAPI_OK) && ((rc = read(0, buf, sizeof (buf))) > 0)) 366*2264Sjacobs status = papiJobStreamWrite(svc, stream, buf, rc); 367*2264Sjacobs 368*2264Sjacobs if (status == PAPI_OK) 369*2264Sjacobs status = papiJobStreamClose(svc, stream, job); 370*2264Sjacobs 371*2264Sjacobs return (status); 372*2264Sjacobs } 373*2264Sjacobs 374*2264Sjacobs static char ** 375*2264Sjacobs all_list(papi_service_t svc) 376*2264Sjacobs { 377*2264Sjacobs papi_status_t status; 378*2264Sjacobs papi_printer_t printer = NULL; 379*2264Sjacobs char *list[] = { "member-names", NULL }; 380*2264Sjacobs char **result = NULL; 381*2264Sjacobs 382*2264Sjacobs status = papiPrinterQuery(svc, "_all", list, NULL, &printer); 383*2264Sjacobs if ((status == PAPI_OK) && (printer != NULL)) { 384*2264Sjacobs papi_attribute_t **attributes = 385*2264Sjacobs papiPrinterGetAttributeList(printer); 386*2264Sjacobs if (attributes != NULL) { 387*2264Sjacobs void *iter = NULL; 388*2264Sjacobs char *value = NULL; 389*2264Sjacobs 390*2264Sjacobs for (status = papiAttributeListGetString(attributes, 391*2264Sjacobs &iter, "member-names", &value); 392*2264Sjacobs status == PAPI_OK; 393*2264Sjacobs status = papiAttributeListGetString(attributes, 394*2264Sjacobs &iter, NULL, &value)) 395*2264Sjacobs list_append(&result, strdup(value)); 396*2264Sjacobs } 397*2264Sjacobs papiPrinterFree(printer); 398*2264Sjacobs } 399*2264Sjacobs 400*2264Sjacobs return (result); 401*2264Sjacobs } 402*2264Sjacobs 403*2264Sjacobs static char ** 404*2264Sjacobs printers_list(papi_service_t svc) 405*2264Sjacobs { 406*2264Sjacobs papi_status_t status; 407*2264Sjacobs papi_printer_t *printers = NULL; 408*2264Sjacobs char *keys[] = { "printer-name", NULL }; 409*2264Sjacobs char **result = NULL; 410*2264Sjacobs 411*2264Sjacobs status = papiPrintersList(svc, keys, NULL, &printers); 412*2264Sjacobs if ((status == PAPI_OK) && (printers != NULL)) { 413*2264Sjacobs int i; 414*2264Sjacobs 415*2264Sjacobs for (i = 0; printers[i] != NULL; i++) { 416*2264Sjacobs papi_attribute_t **attributes = 417*2264Sjacobs papiPrinterGetAttributeList(printers[i]); 418*2264Sjacobs char *name = NULL; 419*2264Sjacobs 420*2264Sjacobs (void) papiAttributeListGetString(attributes, NULL, 421*2264Sjacobs "printer-name", &name); 422*2264Sjacobs if ((name != NULL) && (strcmp(name, "_default") != 0)) 423*2264Sjacobs list_append(&result, strdup(name)); 424*2264Sjacobs } 425*2264Sjacobs papiPrinterListFree(printers); 426*2264Sjacobs } 427*2264Sjacobs 428*2264Sjacobs return (result); 429*2264Sjacobs } 430*2264Sjacobs 431*2264Sjacobs char ** 432*2264Sjacobs interest_list(papi_service_t svc) 433*2264Sjacobs { 434*2264Sjacobs static char been_here; 435*2264Sjacobs static char **result; 436*2264Sjacobs 437*2264Sjacobs if (been_here == 0) { /* only do this once */ 438*2264Sjacobs been_here = 1; 439*2264Sjacobs 440*2264Sjacobs if ((result = all_list(svc)) == NULL) 441*2264Sjacobs result = printers_list(svc); 442*2264Sjacobs } 443*2264Sjacobs 444*2264Sjacobs return (result); 445*2264Sjacobs } 446*2264Sjacobs 447*2264Sjacobs char * 448*2264Sjacobs localhostname() 449*2264Sjacobs { 450*2264Sjacobs static char *result; 451*2264Sjacobs 452*2264Sjacobs if (result == NULL) { 453*2264Sjacobs static char buf[256]; 454*2264Sjacobs 455*2264Sjacobs if (gethostname(buf, sizeof (buf)) == 0) 456*2264Sjacobs result = buf; 457*2264Sjacobs } 458*2264Sjacobs 459*2264Sjacobs return (result); 460*2264Sjacobs } 461*2264Sjacobs 462*2264Sjacobs int 463*2264Sjacobs cli_auth_callback(papi_service_t svc, void *app_data) 464*2264Sjacobs { 465*2264Sjacobs char prompt[BUFSIZ]; 466*2264Sjacobs char *user, *svc_name, *passphrase; 467*2264Sjacobs 468*2264Sjacobs /* get the name of the service we are contacting */ 469*2264Sjacobs if ((svc_name = papiServiceGetServiceName(svc)) == NULL) 470*2264Sjacobs return (-1); 471*2264Sjacobs 472*2264Sjacobs /* find our who we are supposed to be */ 473*2264Sjacobs if ((user = papiServiceGetUserName(svc)) == NULL) { 474*2264Sjacobs struct passwd *pw; 475*2264Sjacobs 476*2264Sjacobs if ((pw = getpwuid(getuid())) != NULL) 477*2264Sjacobs user = pw->pw_name; 478*2264Sjacobs else 479*2264Sjacobs user = "nobody"; 480*2264Sjacobs } 481*2264Sjacobs 482*2264Sjacobs /* build the prompt string */ 483*2264Sjacobs snprintf(prompt, sizeof (prompt), 484*2264Sjacobs gettext("passphrase for %s to access %s: "), user, svc_name); 485*2264Sjacobs 486*2264Sjacobs /* ask for the passphrase */ 487*2264Sjacobs if ((passphrase = getpassphrase(prompt)) != NULL) 488*2264Sjacobs papiServiceSetPassword(svc, passphrase); 489*2264Sjacobs 490*2264Sjacobs return (0); 491*2264Sjacobs } 492