Commit | Line | Data |
---|---|---|
1cc2dcec EA |
1 | # include <stdio.h> |
2 | # include <sys/types.h> | |
3 | # include <sys/stat.h> | |
399058e3 | 4 | # include <sys/dir.h> |
1cc2dcec | 5 | # include <sysexits.h> |
7a8ba074 | 6 | # include <whoami.h> |
1cc2dcec | 7 | |
61886f2a | 8 | static char SccsId[] = "@(#)sccs.c 1.17 %G%"; |
2a0234bf EA |
9 | |
10 | # define bitset(bit, word) ((bit) & (word)) | |
11 | ||
12 | typedef char bool; | |
a97ecd0d EA |
13 | # define TRUE 1 |
14 | # define FALSE 0 | |
81dc6403 | 15 | |
1cc2dcec EA |
16 | struct sccsprog |
17 | { | |
18 | char *sccsname; /* name of SCCS routine */ | |
a97ecd0d EA |
19 | short sccsoper; /* opcode, see below */ |
20 | short sccsflags; /* flags, see below */ | |
1cc2dcec EA |
21 | char *sccspath; /* pathname of binary implementing */ |
22 | }; | |
23 | ||
a97ecd0d EA |
24 | /* values for sccsoper */ |
25 | # define PROG 0 /* call a program */ | |
e672376b | 26 | # define CMACRO 1 /* command substitution macro */ |
4535945e | 27 | # define FIX 2 /* fix a delta */ |
399058e3 | 28 | # define CLEAN 3 /* clean out recreatable files */ |
61886f2a | 29 | # define UNEDIT 4 /* unedit a file */ |
a97ecd0d | 30 | |
2a0234bf | 31 | /* bits for sccsflags */ |
a97ecd0d EA |
32 | # define NO_SDOT 0001 /* no s. on front of args */ |
33 | # define REALUSER 0002 /* protected (e.g., admin) */ | |
1cc2dcec | 34 | |
7a8ba074 EA |
35 | # ifdef CSVAX |
36 | # define PROGPATH(name) "/usr/local/name" | |
37 | # endif CSVAX | |
38 | ||
39 | # ifndef PROGPATH | |
40 | # define PROGPATH(name) "/usr/sccs/name" | |
41 | # endif PROGPATH | |
42 | ||
1cc2dcec EA |
43 | struct sccsprog SccsProg[] = |
44 | { | |
7a8ba074 EA |
45 | "admin", PROG, REALUSER, PROGPATH(admin), |
46 | "chghist", PROG, 0, PROGPATH(rmdel), | |
47 | "comb", PROG, 0, PROGPATH(comb), | |
48 | "delta", PROG, 0, PROGPATH(delta), | |
49 | "get", PROG, 0, PROGPATH(get), | |
50 | "help", PROG, NO_SDOT, PROGPATH(help), | |
51 | "prt", PROG, 0, PROGPATH(prt), | |
52 | "rmdel", PROG, REALUSER, PROGPATH(rmdel), | |
53 | "what", PROG, NO_SDOT, PROGPATH(what), | |
27e36d02 EA |
54 | "edit", CMACRO, 0, "get -e", |
55 | "delget", CMACRO, 0, "delta/get", | |
56 | "deled", CMACRO, 0, "delta/get -e", | |
e672376b | 57 | "del", CMACRO, 0, "delta/get", |
4535945e EA |
58 | "delt", CMACRO, 0, "delta/get", |
59 | "fix", FIX, 0, NULL, | |
db4904e0 EA |
60 | "clean", CLEAN, REALUSER, (char *) TRUE, |
61 | "info", CLEAN, REALUSER, (char *) FALSE, | |
61886f2a | 62 | "unedit", UNEDIT, 0, NULL, |
a97ecd0d | 63 | NULL, -1, 0, NULL |
1cc2dcec EA |
64 | }; |
65 | ||
61886f2a EA |
66 | struct pfile |
67 | { | |
68 | char *p_osid; /* old SID */ | |
69 | char *p_nsid; /* new SID */ | |
70 | char *p_user; /* user who did edit */ | |
71 | char *p_date; /* date of get */ | |
72 | char *p_time; /* time of get */ | |
73 | }; | |
74 | ||
2a0234bf | 75 | char *SccsPath = "SCCS"; /* pathname of SCCS files */ |
2a0234bf | 76 | bool RealUser; /* if set, running as real user */ |
27e36d02 EA |
77 | # ifdef DEBUG |
78 | bool Debug; /* turn on tracing */ | |
79 | # endif | |
1cc2dcec EA |
80 | |
81 | main(argc, argv) | |
82 | int argc; | |
83 | char **argv; | |
84 | { | |
85 | register char *p; | |
1045e3ba | 86 | extern struct sccsprog *lookup(); |
1cc2dcec EA |
87 | |
88 | /* | |
89 | ** Detect and decode flags intended for this program. | |
90 | */ | |
91 | ||
a97ecd0d EA |
92 | if (argc < 2) |
93 | { | |
94 | fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); | |
95 | exit(EX_USAGE); | |
96 | } | |
97 | argv[argc] = NULL; | |
98 | ||
1045e3ba | 99 | if (lookup(argv[0]) == NULL) |
1cc2dcec | 100 | { |
1045e3ba | 101 | while ((p = *++argv) != NULL) |
1cc2dcec | 102 | { |
1045e3ba EA |
103 | if (*p != '-') |
104 | break; | |
105 | switch (*++p) | |
106 | { | |
107 | case 'r': /* run as real user */ | |
108 | setuid(getuid()); | |
109 | RealUser++; | |
110 | break; | |
111 | ||
112 | case 'p': /* path of sccs files */ | |
113 | SccsPath = ++p; | |
114 | break; | |
115 | ||
27e36d02 EA |
116 | # ifdef DEBUG |
117 | case 'T': /* trace */ | |
118 | Debug++; | |
119 | break; | |
120 | # endif | |
121 | ||
1045e3ba EA |
122 | default: |
123 | fprintf(stderr, "Sccs: unknown option -%s\n", p); | |
124 | break; | |
125 | } | |
1cc2dcec | 126 | } |
1045e3ba EA |
127 | if (SccsPath[0] == '\0') |
128 | SccsPath = "."; | |
1cc2dcec EA |
129 | } |
130 | ||
e672376b | 131 | command(argv, FALSE); |
a97ecd0d EA |
132 | exit(EX_OK); |
133 | } | |
134 | ||
e672376b | 135 | command(argv, forkflag) |
a97ecd0d | 136 | char **argv; |
e672376b | 137 | bool forkflag; |
a97ecd0d EA |
138 | { |
139 | register struct sccsprog *cmd; | |
140 | register char *p; | |
e672376b EA |
141 | register char *q; |
142 | char buf[40]; | |
1045e3ba | 143 | extern struct sccsprog *lookup(); |
27e36d02 EA |
144 | char *nav[7]; |
145 | char **avp; | |
146 | ||
147 | # ifdef DEBUG | |
148 | if (Debug) | |
149 | { | |
150 | printf("command:\n"); | |
151 | for (avp = argv; *avp != NULL; avp++) | |
152 | printf(" \"%s\"\n", *avp); | |
153 | } | |
154 | # endif | |
2a0234bf | 155 | |
1cc2dcec EA |
156 | /* |
157 | ** Look up command. | |
a97ecd0d | 158 | ** At this point, argv points to the command name. |
1cc2dcec EA |
159 | */ |
160 | ||
61886f2a | 161 | cmd = lookup(argv[0]); |
1045e3ba | 162 | if (cmd == NULL) |
1cc2dcec | 163 | { |
61886f2a | 164 | fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]); |
1cc2dcec EA |
165 | exit(EX_USAGE); |
166 | } | |
167 | ||
2a0234bf | 168 | /* |
a97ecd0d | 169 | ** Interpret operation associated with this command. |
2a0234bf EA |
170 | */ |
171 | ||
a97ecd0d EA |
172 | switch (cmd->sccsoper) |
173 | { | |
174 | case PROG: /* call an sccs prog */ | |
e672376b EA |
175 | callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); |
176 | break; | |
177 | ||
178 | case CMACRO: /* command macro */ | |
179 | for (p = cmd->sccspath; *p != '\0'; p++) | |
180 | { | |
27e36d02 EA |
181 | avp = nav; |
182 | *avp++ = buf; | |
e672376b | 183 | for (q = buf; *p != '/' && *p != '\0'; p++, q++) |
27e36d02 EA |
184 | { |
185 | if (*p == ' ') | |
186 | { | |
187 | *q = '\0'; | |
188 | *avp++ = &q[1]; | |
189 | } | |
190 | else | |
191 | *q = *p; | |
192 | } | |
e672376b | 193 | *q = '\0'; |
27e36d02 EA |
194 | *avp = NULL; |
195 | xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], | |
196 | nav[3], nav[4], nav[5], nav[6]); | |
e672376b EA |
197 | } |
198 | fprintf(stderr, "Sccs internal error: CMACRO\n"); | |
a97ecd0d EA |
199 | exit(EX_SOFTWARE); |
200 | ||
4535945e | 201 | case FIX: /* fix a delta */ |
399058e3 | 202 | if (strcmpn(argv[1], "-r", 2) != 0) |
4535945e EA |
203 | { |
204 | fprintf(stderr, "Sccs: -r flag needed for fix command\n"); | |
205 | break; | |
206 | } | |
207 | xcommand(&argv[1], TRUE, "get", "-k", NULL); | |
208 | xcommand(&argv[1], TRUE, "rmdel", NULL); | |
209 | xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); | |
210 | fprintf(stderr, "Sccs internal error: FIX\n"); | |
211 | exit(EX_SOFTWARE); | |
212 | ||
399058e3 | 213 | case CLEAN: |
db4904e0 | 214 | clean((bool) cmd->sccspath); |
399058e3 EA |
215 | break; |
216 | ||
61886f2a EA |
217 | case UNEDIT: |
218 | for (avp = &argv[1]; *avp != NULL; avp++) | |
219 | unedit(*avp); | |
220 | break; | |
221 | ||
a97ecd0d EA |
222 | default: |
223 | fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); | |
224 | exit(EX_SOFTWARE); | |
225 | } | |
226 | } | |
1045e3ba EA |
227 | \f/* |
228 | ** LOOKUP -- look up an SCCS command name. | |
229 | ** | |
230 | ** Parameters: | |
231 | ** name -- the name of the command to look up. | |
232 | ** | |
233 | ** Returns: | |
234 | ** ptr to command descriptor for this command. | |
235 | ** NULL if no such entry. | |
236 | ** | |
237 | ** Side Effects: | |
238 | ** none. | |
239 | */ | |
240 | ||
241 | struct sccsprog * | |
242 | lookup(name) | |
243 | char *name; | |
244 | { | |
245 | register struct sccsprog *cmd; | |
246 | ||
247 | for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) | |
248 | { | |
249 | if (strcmp(cmd->sccsname, name) == 0) | |
250 | return (cmd); | |
251 | } | |
252 | return (NULL); | |
253 | } | |
a97ecd0d | 254 | |
4535945e EA |
255 | |
256 | xcommand(argv, forkflag, arg0) | |
257 | char **argv; | |
258 | bool forkflag; | |
259 | char *arg0; | |
260 | { | |
261 | register char **av; | |
262 | char *newargv[1000]; | |
263 | register char **np; | |
264 | ||
265 | np = newargv; | |
266 | for (av = &arg0; *av != NULL; av++) | |
267 | *np++ = *av; | |
268 | for (av = argv; *av != NULL; av++) | |
269 | *np++ = *av; | |
270 | *np = NULL; | |
271 | command(newargv, forkflag); | |
272 | } | |
273 | ||
a97ecd0d EA |
274 | callprog(progpath, flags, argv, forkflag) |
275 | char *progpath; | |
276 | short flags; | |
277 | char **argv; | |
278 | bool forkflag; | |
279 | { | |
280 | register char *p; | |
281 | register char **av; | |
a97ecd0d EA |
282 | extern char *makefile(); |
283 | register int i; | |
e672376b | 284 | auto int st; |
a97ecd0d EA |
285 | |
286 | if (*argv == NULL) | |
287 | return (-1); | |
2a0234bf | 288 | |
1cc2dcec | 289 | /* |
4535945e | 290 | ** Fork if appropriate. |
1cc2dcec EA |
291 | */ |
292 | ||
a97ecd0d EA |
293 | if (forkflag) |
294 | { | |
27e36d02 EA |
295 | # ifdef DEBUG |
296 | if (Debug) | |
297 | printf("Forking\n"); | |
298 | # endif | |
a97ecd0d EA |
299 | i = fork(); |
300 | if (i < 0) | |
301 | { | |
302 | fprintf(stderr, "Sccs: cannot fork"); | |
303 | exit(EX_OSERR); | |
304 | } | |
305 | else if (i > 0) | |
e672376b EA |
306 | { |
307 | wait(&st); | |
308 | return (st); | |
309 | } | |
a97ecd0d EA |
310 | } |
311 | ||
4535945e EA |
312 | /* |
313 | ** Build new argument vector. | |
314 | */ | |
315 | ||
316 | /* copy program filename arguments and flags */ | |
317 | av = argv; | |
318 | while ((p = *++av) != NULL) | |
319 | { | |
320 | if (!bitset(NO_SDOT, flags) && *p != '-') | |
321 | *av = makefile(p); | |
322 | } | |
323 | ||
a97ecd0d EA |
324 | /* |
325 | ** Set protection as appropriate. | |
326 | */ | |
327 | ||
328 | if (bitset(REALUSER, flags)) | |
329 | setuid(getuid()); | |
4535945e | 330 | |
a97ecd0d | 331 | /* |
4535945e | 332 | ** Call real SCCS program. |
a97ecd0d EA |
333 | */ |
334 | ||
4535945e | 335 | execv(progpath, argv); |
1cc2dcec | 336 | fprintf(stderr, "Sccs: cannot execute "); |
a97ecd0d | 337 | perror(progpath); |
1cc2dcec EA |
338 | exit(EX_UNAVAILABLE); |
339 | } | |
340 | ||
341 | ||
342 | char * | |
343 | makefile(name) | |
344 | char *name; | |
345 | { | |
346 | register char *p; | |
347 | register char c; | |
348 | char buf[512]; | |
349 | struct stat stbuf; | |
350 | extern char *malloc(); | |
351 | ||
352 | /* | |
353 | ** See if this filename should be used as-is. | |
354 | ** There are three conditions where this can occur. | |
355 | ** 1. The name already begins with "s.". | |
356 | ** 2. The name has a "/" in it somewhere. | |
357 | ** 3. The name references a directory. | |
358 | */ | |
359 | ||
399058e3 | 360 | if (strcmpn(name, "s.", 2) == 0) |
1cc2dcec EA |
361 | return (name); |
362 | for (p = name; (c = *p) != '\0'; p++) | |
363 | { | |
364 | if (c == '/') | |
365 | return (name); | |
366 | } | |
367 | if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) | |
368 | return (name); | |
369 | ||
370 | /* | |
371 | ** Prepend the path of the sccs file. | |
372 | */ | |
373 | ||
374 | strcpy(buf, SccsPath); | |
2a0234bf | 375 | strcat(buf, "/s."); |
1cc2dcec EA |
376 | strcat(buf, name); |
377 | p = malloc(strlen(buf) + 1); | |
378 | if (p == NULL) | |
379 | { | |
380 | perror("Sccs: no mem"); | |
381 | exit(EX_OSERR); | |
382 | } | |
383 | strcpy(p, buf); | |
384 | return (p); | |
385 | } | |
399058e3 EA |
386 | \f/* |
387 | ** CLEAN -- clean out recreatable files | |
388 | ** | |
389 | ** Any file for which an "s." file exists but no "p." file | |
390 | ** exists in the current directory is purged. | |
391 | ** | |
392 | ** Parameters: | |
db4904e0 EA |
393 | ** really -- if TRUE, remove everything. |
394 | ** else, just report status. | |
399058e3 EA |
395 | ** |
396 | ** Returns: | |
397 | ** none. | |
398 | ** | |
399 | ** Side Effects: | |
400 | ** removes files in the current directory. | |
401 | */ | |
402 | ||
db4904e0 EA |
403 | clean(really) |
404 | bool really; | |
399058e3 EA |
405 | { |
406 | struct direct dir; | |
407 | struct stat stbuf; | |
408 | char buf[100]; | |
bb9b2f29 | 409 | char pline[120]; |
db4904e0 EA |
410 | register FILE *dirfd; |
411 | register char *basefile; | |
d7f27f60 | 412 | bool gotedit; |
bb9b2f29 | 413 | FILE *pfp; |
399058e3 EA |
414 | |
415 | dirfd = fopen(SccsPath, "r"); | |
416 | if (dirfd == NULL) | |
417 | { | |
418 | fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); | |
419 | return; | |
420 | } | |
421 | ||
422 | /* | |
423 | ** Scan the SCCS directory looking for s. files. | |
424 | */ | |
425 | ||
d7f27f60 | 426 | gotedit = FALSE; |
399058e3 EA |
427 | while (fread(&dir, sizeof dir, 1, dirfd) != NULL) |
428 | { | |
429 | if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) | |
430 | continue; | |
431 | ||
432 | /* got an s. file -- see if the p. file exists */ | |
433 | strcpy(buf, SccsPath); | |
434 | strcat(buf, "/p."); | |
db4904e0 | 435 | basefile = &buf[strlen(buf)]; |
db4904e0 | 436 | strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2); |
bb9b2f29 EA |
437 | basefile[sizeof dir.d_name - 2] = '\0'; |
438 | pfp = fopen(buf, "r"); | |
439 | if (pfp != NULL) | |
db4904e0 | 440 | { |
bb9b2f29 EA |
441 | while (fgets(pline, sizeof pline, pfp) != NULL) |
442 | printf("%12s: being editted: %s", basefile, pline); | |
443 | fclose(pfp); | |
d7f27f60 | 444 | gotedit = TRUE; |
399058e3 | 445 | continue; |
db4904e0 | 446 | } |
399058e3 EA |
447 | |
448 | /* the s. file exists and no p. file exists -- unlink the g-file */ | |
db4904e0 EA |
449 | if (really) |
450 | { | |
451 | strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); | |
452 | buf[sizeof dir.d_name - 2] = '\0'; | |
453 | unlink(buf); | |
454 | } | |
399058e3 EA |
455 | } |
456 | ||
457 | fclose(dirfd); | |
d7f27f60 EA |
458 | if (!gotedit && !really) |
459 | printf("Nothing being editted\n"); | |
399058e3 | 460 | } |
61886f2a EA |
461 | \f/* |
462 | ** UNEDIT -- unedit a file | |
463 | ** | |
464 | ** Checks to see that the current user is actually editting | |
465 | ** the file and arranges that s/he is not editting it. | |
466 | ** | |
467 | ** Parameters: | |
468 | ** fn -- the name of the file to be uneditted. | |
469 | ** | |
470 | ** Returns: | |
471 | ** none. | |
472 | ** | |
473 | ** Side Effects: | |
474 | ** fn is removed | |
475 | ** entries are removed from pfile. | |
476 | */ | |
477 | ||
478 | unedit(fn) | |
479 | char *fn; | |
480 | { | |
481 | register FILE *pfp; | |
482 | char *pfn; | |
483 | static char tfn[] = "/tmp/sccsXXXXX"; | |
484 | FILE *tfp; | |
485 | register char *p; | |
486 | register char *q; | |
487 | bool delete = FALSE; | |
488 | bool others = FALSE; | |
489 | char *myname; | |
490 | extern char *getlogin(); | |
491 | struct pfile *pent; | |
492 | extern struct pfile *getpfile(); | |
493 | char buf[120]; | |
494 | ||
495 | /* make "s." filename & find the trailing component */ | |
496 | pfn = makefile(fn); | |
497 | q = &pfn[strlen(pfn) - 1]; | |
498 | while (q > pfn && *q != '/') | |
499 | q--; | |
500 | if (q <= pfn && (q[0] != 's' || q[1] != '.')) | |
501 | { | |
502 | fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn); | |
503 | return; | |
504 | } | |
505 | ||
506 | /* turn "s." into "p." */ | |
507 | *++q = 'p'; | |
508 | ||
509 | pfp = fopen(pfn, "r"); | |
510 | if (pfp == NULL) | |
511 | { | |
512 | printf("%12s: not being editted\n", fn); | |
513 | return; | |
514 | } | |
515 | ||
516 | /* | |
517 | ** Copy p-file to temp file, doing deletions as needed. | |
518 | */ | |
519 | ||
520 | mktemp(tfn); | |
521 | tfp = fopen(tfn, "w"); | |
522 | if (tfp == NULL) | |
523 | { | |
524 | fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn); | |
525 | exit(EX_OSERR); | |
526 | } | |
527 | ||
528 | myname = getlogin(); | |
529 | while ((pent = getpfile(pfp)) != NULL) | |
530 | { | |
531 | if (strcmp(pent->p_user, myname) == 0) | |
532 | { | |
533 | /* a match */ | |
534 | delete++; | |
535 | } | |
536 | else | |
537 | { | |
538 | fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, | |
539 | pent->p_nsid, pent->p_user, pent->p_date, | |
540 | pent->p_time); | |
541 | others++; | |
542 | } | |
543 | } | |
544 | ||
545 | /* do final cleanup */ | |
546 | if (others) | |
547 | { | |
548 | if (freopen(tfn, "r", tfp) == NULL) | |
549 | { | |
550 | fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn); | |
551 | exit(EX_OSERR); | |
552 | } | |
553 | if (freopen(pfn, "w", pfp) == NULL) | |
554 | { | |
555 | fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn); | |
556 | return; | |
557 | } | |
558 | while (fgets(buf, sizeof buf, tfp) != NULL) | |
559 | fputs(buf, pfp); | |
560 | } | |
561 | else | |
562 | { | |
563 | unlink(pfn); | |
564 | } | |
565 | fclose(tfp); | |
566 | fclose(pfp); | |
567 | unlink(tfn); | |
568 | ||
569 | if (delete) | |
570 | { | |
571 | unlink(fn); | |
572 | printf("%12s: removed\n", fn); | |
573 | } | |
574 | else | |
575 | { | |
576 | printf("%12s: not being editted by you\n", fn); | |
577 | } | |
578 | } | |
579 | \f/* | |
580 | ** GETPFILE -- get an entry from the p-file | |
581 | ** | |
582 | ** Parameters: | |
583 | ** pfp -- p-file file pointer | |
584 | ** | |
585 | ** Returns: | |
586 | ** pointer to p-file struct for next entry | |
587 | ** NULL on EOF or error | |
588 | ** | |
589 | ** Side Effects: | |
590 | ** Each call wipes out results of previous call. | |
591 | */ | |
592 | ||
593 | struct pfile * | |
594 | getpfile(pfp) | |
595 | FILE *pfp; | |
596 | { | |
597 | static struct pfile ent; | |
598 | static char buf[120]; | |
599 | register char *p; | |
600 | extern char *nextfield(); | |
601 | ||
602 | if (fgets(buf, sizeof buf, pfp) == NULL) | |
603 | return (NULL); | |
604 | ||
605 | ent.p_osid = p = buf; | |
606 | ent.p_nsid = p = nextfield(p); | |
607 | ent.p_user = p = nextfield(p); | |
608 | ent.p_date = p = nextfield(p); | |
609 | ent.p_time = p = nextfield(p); | |
610 | if (p == NULL || nextfield(p) != NULL) | |
611 | return (NULL); | |
612 | ||
613 | return (&ent); | |
614 | } | |
615 | ||
616 | ||
617 | char * | |
618 | nextfield(p) | |
619 | register char *p; | |
620 | { | |
621 | if (p == NULL || *p == '\0') | |
622 | return (NULL); | |
623 | while (*p != ' ' && *p != '\n' && *p != '\0') | |
624 | p++; | |
625 | if (*p == '\n' || *p == '\0') | |
626 | { | |
627 | *p = '\0'; | |
628 | return (NULL); | |
629 | } | |
630 | *p++ = '\0'; | |
631 | return (p); | |
632 | } |