Appendix B. ladsh Source Code

   


Appendix B. ladsh Source Code

   1: /* ladsh4.c */   2:   3: #define _GNU_SOURCE   4:   5: #include <ctype.h>   6: #include <errno.h>   7: #include <fcntl.h>   8: #include <glob.h>   9: #include <signal.h>  10: #include <stdio.h>  11: #include <stdlib.h>  12: #include <string.h>  13: #include <sys/ioctl.h>  14: #include <sys/wait.h>  15: #include <unistd.h>  16:  17: #define MAX_COMMAND_LEN 250     /* max length of a single command  18:                                    string */  19: #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"  20:  21: struct jobSet {  22:     struct job * head;      /* head of list of running jobs */  23:     struct job * fg;        /* current foreground job */  24: };  25:  26: enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,  27:                        REDIRECT_APPEND };  28:  29: struct redirectionSpecifier {  30:     enum redirectionType type;  /* type of redirection */  31:     int fd;                 /* fd being redirected */  32:     char * filename;        /* file to redirect fd to */  33: };  34:  35: struct childProgram {  36:     pid_t pid;              /* 0 if exited */  37:     char ** argv;           /* program name and arguments */  38:     int numRedirections;    /* elements in redirection array */  39:     struct redirectionSpecifier * redirections; /* I/O redirs */  40:     glob_t globResult;      /* result of parameter globbing */  41:     int freeGlob;           /* should we free globResult? */  42:     int isStopped;          /* is the program currently running? */  43: };  44:  45: struct job {  46:     int jobId;              /* job number */  47:     int numProgs;           /* number of programs in job */  48:     int runningProgs;       /* number of programs running */  49:     char * text;            /* name of job */  50:     char * cmdBuf;          /* buffer various argv's point to */  51:     pid_t pgrp;             /* process group ID for the job */  52:     struct childProgram * progs; /* array of programs in job */  53:     struct job * next;      /* to track background commands */  54:     int stoppedProgs;       /* num of programs alive, but stopped */  55: };  56:  57: void freeJob(struct job * cmd) {  58:     int i;  59:  60:     for (i = 0; i < cmd->numProgs; i++) {  61:         free(cmd->progs[i].argv);  62:         if (cmd->progs[i].redirections)  63:             free(cmd->progs[i].redirections);  64:         if (cmd->progs[i].freeGlob)  65:             globfree(&cmd->progs[i].globResult);  66:     }  67:     free(cmd->progs);  68:     if (cmd->text) free(cmd->text);  69:     free(cmd->cmdBuf);  70: }  71:  72: int getCommand(FILE * source, char * command) {  73:     if (source == stdin) {  74:         printf("# ");  75:         fflush(stdout);  76:     }  77:  78:     if (!fgets(command, MAX_COMMAND_LEN, source)) {  79:         if (source == stdin) printf("\n");  80:         return 1;  81:     }  82:  83:     /* remove trailing newline */  84:     command[strlen(command) - 1] = '\0';  85:  86:     return 0;  87: }  88:  89: void globLastArgument(struct childProgram * prog, int * argcPtr,  90:                         int * argcAllocedPtr) {  91:     int argc = *argcPtr;  92:     int argcAlloced = *argcAllocedPtr;  93:     int rc;  94:     int flags;  95:     int i;  96:     char * src, * dst;  97:  98:     if (argc > 1) {       /* cmd->globResult already initialized */  99:         flags = GLOB_APPEND; 100:         i = prog->globResult.gl_pathc; 101:     } else { 102:         prog->freeGlob = 1; 103:         flags = 0; 104:         i = 0; 105:     } 106: 107:     rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); 108:     if (rc == GLOB_NOSPACE) { 109:         fprintf(stderr, "out of space during glob operation\n"); 110:         return; 111:     } else if (rc == GLOB_NOMATCH || 112:                (!rc && (prog->globResult.gl_pathc - i) == 1 && 113:                 !strcmp(prog->argv[argc - 1], 114:                         prog->globResult.gl_pathv[i]))) { 115:         /* we need to remove whatever \ quoting is still present */ 116:         src = dst = prog->argv[argc - 1]; 117:         while (*src) { 118:             if (*src != '\\') *dst++ = *src; 119:             src++; 120:         } 121:         *dst = '\0'; 122:     } else if (!rc) { 123:         argcAlloced += (prog->globResult.gl_pathc - i); 124:         prog->argv = realloc(prog->argv, 125:                              argcAlloced * sizeof(*prog->argv)); 126:         memcpy(prog->argv + (argc - 1), 127:                prog->globResult.gl_pathv + i, 128:                sizeof(*(prog->argv)) * 129:                       (prog->globResult.gl_pathc - i)); 130:         argc += (prog->globResult.gl_pathc - i - 1); 131:     } 132: 133:     *argcAllocedPtr = argcAlloced; 134:     *argcPtr = argc; 135: } 136: 137: /* Return cmd->numProgs as 0 if no command is present (e.g. an empty 138:    line). If a valid command is found, commandPtr is set to point to 139:    the beginning of the next command (if the original command had 140:    more than one job associated with it) or NULL if no more 141:    commands are present. */ 142: int parseCommand(char ** commandPtr, struct job * job, int * isBg) { 143:     char * command; 144:     char * returnCommand = NULL; 145:     char * src, * buf, * chptr; 146:     int argc = 0; 147:     int done = 0; 148:     int argvAlloced; 149:     int i; 150:     char quote = '\0'; 151:     int count; 152:     struct childProgram * prog; 153: 154:     /* skip leading white space */ 155:     while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++; 156: 157:     /* this handles empty lines and leading '#' characters */ 158:         if (!**commandPtr || (**commandPtr=='#')) { 159:         job->numProgs = 0; 160:         *commandPtr = NULL; 161:         return 0; 162:     } 163: 164:     *isBg = 0; 165:     job->numProgs = 1; 166:     job->progs = malloc(sizeof(*job->progs)); 167: 168:     /* We set the argv elements to point inside of this string. The 169:        memory is freed by freeJob(). 170: 171:        Getting clean memory relieves us of the task of NULL 172:        terminating things and makes the rest of this look a bit 173:        cleaner (though it is, admittedly, a tad less efficient) */ 174:     job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); 175:     job->text = NULL; 176: 177:     prog = job->progs; 178:     prog->numRedirections = 0; 179:     prog->redirections = NULL; 180:     prog->freeGlob = 0; 181:     prog->isStopped = 0; 182: 183:     argvAlloced = 5; 184:     prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); 185:     prog->argv[0] = job->cmdBuf; 186: 187:     buf = command; 188:     src = *commandPtr; 189:     while (*src && !done) { 190:         if (quote == *src) { 191:             quote = '\0'; 192:         } else if (quote) { 193:             if (*src == '\\') { 194:                 src++; 195:                 if (!*src) { 196:                     fprintf(stderr, 197:                         "character expected after \\\n"); 198:                     freeJob(job); 199:                     return 1; 200:                 } 201: 202:                 /* in shell, "\'" should yield \' */ 203:                 if (*src != quote) *buf++ = '\\'; 204:             } else if (*src == '*' || *src == '?' || *src == '[' || 205:                        *src == ']') 206:                 *buf++ = '\\'; 207:             *buf++ = *src; 208:         } else if (isspace(*src)) { 209:             if (*prog->argv[argc]) { 210:                 buf++, argc++; 211:                 /* +1 here leaves room for the NULL which 212:                    ends argv */ 213:                 if ((argc + 1) == argvAlloced) { 214:                     argvAlloced += 5; 215:                     prog->argv = realloc(prog->argv, 216:                                 sizeof(*prog->argv) * argvAlloced); 217:                 } 218:                 prog->argv[argc] = buf; 219: 220:                 globLastArgument(prog, &argc, &argvAlloced); 221:             } 222:         } else switch (*src) { 223:         case '"': 224:         case '\'': 225:             quote = *src; 226:             break; 227: 228:         case '#':                         /* comment */ 229:             done = 1; 230:             break; 231: 232:         case '>':                         /* redirections */ 233:         case '<': 234:             i = prog->numRedirections++; 235:             prog->redirections = realloc(prog->redirections, 236:                         sizeof(*prog->redirections) * (i + 1)); 237: 238:             prog->redirections[i].fd = -1; 239:             if (buf != prog->argv[argc]) { 240:                 /* the stuff before this character may be 241:                    the file number being redirected */ 242:                 prog->redirections[i].fd = 243:                     strtol(prog->argv[argc], &chptr, 10); 244: 245:                 if (*chptr && *prog->argv[argc]) { 246:                     buf++, argc++; 247:                     globLastArgument(prog, &argc, &argvAlloced); 248:                 } 249:             } 250: 251:             if (prog->redirections[i].fd == -1) { 252:                 if (*src == '>') 253:                     prog->redirections[i].fd = 1; 254:                 else 255:                     prog->redirections[i].fd = 0; 256:             } 257: 258:             if (*src++ == '>') { 259:                 if (*src == '>') { 260:                     prog->redirections[i].type = REDIRECT_APPEND; 261:                     src++; 262:                 } else { 263:                    prog->redirections[i].type = REDIRECT_OVERWRITE; 264:                 } 265:             } else { 266:                 prog->redirections[i].type = REDIRECT_INPUT; 267:             } 268: 269:             /* This isn't POSIX sh compliant. Oh well. */ 270:             chptr = src; 271:             while (isspace(*chptr)) chptr++; 272: 273:             if (!*chptr) { 274:                 fprintf(stderr, "file name expected after %c\n", 275:                         *src); 276:                 freeJob(job); 277:                 return 1; 278:             } 279: 280:             prog->redirections[i].filename = buf; 281:             while (*chptr && !isspace(*chptr)) 282:                 *buf++ = *chptr++; 283: 284:             src = chptr - 1;               /* we src++ later */ 285:             prog->argv[argc] = ++buf; 286:             break; 287: 288:         case '|':                         /* pipe */ 289:             /* finish this command */ 290:             if (*prog->argv[argc]) argc++; 291:             if (!argc) { 292:                 fprintf(stderr, "empty command in pipe\n"); 293:                 freeJob(job); 294:                 return 1; 295:             } 296:             prog->argv[argc] = NULL; 297: 298:             /* and start the next */ 299:             job->numProgs++; 300:             job->progs = realloc(job->progs, 301:                                  sizeof(*job->progs) * 302:                                      job->numProgs); 303:             prog = job->progs + (job->numProgs - 1); 304:             prog->numRedirections = 0; 305:             prog->redirections = NULL; 306:             prog->freeGlob = 0; 307:             argc = 0; 308: 309:             argvAlloced = 5; 310:             prog->argv = malloc(sizeof(*prog->argv) * 311:                                     argvAlloced); 312:             prog->argv[0] = ++buf; 313: 314:             src++; 315:             while (*src && isspace(*src)) src++; 316: 317:             if (!*src) { 318:                 fprintf(stderr, "empty command in pipe\n"); 319:                 return 1; 320:             } 321:             src--;     /* we'll ++ it at the end of the loop */ 322: 323:             break; 324: 325:         case '&':                         /* background */ 326:             *isBg = 1; 327:         case ';':                         /* multiple commands */ 328:             done = 1; 329:             returnCommand = *commandPtr + (src - *commandPtr) + 1; 330:             break; 331: 332:         case '\\': 333:             src++; 334:             if (!*src) { 335:                 freeJob(job); 336:                 fprintf(stderr, "character expected after \\\n"); 337:                 return 1; 338:             } 339:             if (*src == '*' || *src == '[' || *src == ']' 340:                             || *src == '?') 341:                 *buf++ = '\\'; 342:             /* fallthrough */ 343:         default: 344:             *buf++ = *src; 345:         } 346: 347:         src++; 348:     } 349: 350:     if (*prog->argv[argc]) { 351:         argc++; 352:         globLastArgument(prog, &argc, &argvAlloced); 353:     } 354:     if (!argc) { 355:         freeJob(job); 356:         return 0; 357:     } 358:     prog->argv[argc] = NULL; 359: 360:     if (!returnCommand) { 361:         job->text = malloc(strlen(*commandPtr) + 1); 362:         strcpy(job->text, *commandPtr); 363:     } else { 364:         /* This leaves any trailing spaces, which is a bit sloppy */ 365: 366:         count = returnCommand - *commandPtr; 367:         job->text = malloc(count + 1); 368:         strncpy(job->text, *commandPtr, count); 369:         job->text[count] = '\0'; 370:     } 371: 372:     *commandPtr = returnCommand; 373: 374:     return 0; 375: } 376: 377: int setupRedirections(struct childProgram * prog) { 378:     int i; 379:     int openfd; 380:     int mode; 381:     struct redirectionSpecifier * redir = prog->redirections; 382: 383:     for (i = 0; i < prog->numRedirections; i++, redir++) { 384:         switch (redir->type) { 385:         case REDIRECT_INPUT: 386:             mode = O_RDONLY; 387:             break; 388:         case REDIRECT_OVERWRITE: 389:             mode = O_RDWR | O_CREAT | O_TRUNC; 390:             break; 391:         case REDIRECT_APPEND: 392:             mode = O_RDWR | O_CREAT | O_APPEND; 393:             break; 394:         } 395: 396:         openfd = open(redir->filename, mode, 0666); 397:         if (openfd < 0) { 398:             /* this could get lost if stderr has been redirected, 399:                but bash and ash both lose it as well (though zsh 400:                doesn't!) */ 401:             fprintf(stderr, "error opening %s: %s\n", 402:                     redir->filename, strerror(errno)); 403:             return 1; 404:         } 405: 406:         if (openfd != redir->fd) { 407:             dup2(openfd, redir->fd); 408:             close(openfd); 409:         } 410:     } 411: 412:     return 0; 413: } 414: 415: int runCommand(struct job newJob, struct jobSet * jobList, 416:                int inBg) { 417:     struct job * job; 418:     char * newdir, * buf; 419:     int i, len; 420:     int nextin, nextout; 421:     int pipefds[2];            /* pipefd[0] is for reading */ 422:     char * statusString; 423:     int jobNum; 424:     int controlfds[2];         /* a pipe to make the child pause */ 425: 426:     /* handle built-ins here -- we don't fork() so we 427:        can't background these very easily */ 428:     if (!strcmp(newJob.progs[0].argv[0], "exit")) { 429:         /* this should return a real exit code */ 430:         exit(0); 431:     } else if (!strcmp(newJob.progs[0].argv[0], "pwd")) { 432:         len = 50; 433:         buf = malloc(len); 434:         while (!getcwd(buf, len) && errno == ERANGE) { 435:             len += 50; 436:             buf = realloc(buf, len); 437:         } 438:         printf("%s\n", buf); 439:         free(buf); 440:         return 0; 441:     } else if (!strcmp(newJob.progs[0].argv[0], "cd")) { 442:         if (!newJob.progs[0].argv[1] == 1) 443:             newdir = getenv("HOME"); 444:         else 445:             newdir = newJob.progs[0].argv[1]; 446:         if (chdir(newdir)) 447:             printf("failed to change current directory: %s\n", 448:                     strerror(errno)); 449:         return 0; 450:     } else if (!strcmp(newJob.progs[0].argv[0], "jobs")) { 451:         for (job = jobList->head; job; job = job->next) { 452:             if (job->runningProgs == job->stoppedProgs) 453:                 statusString = "Stopped"; 454:             else 455:                 statusString = "Running"; 456: 457:             printf(JOB_STATUS_FORMAT, job->jobId, statusString, 458:                     job->text); 459:         } 460:         return 0; 461:     } else if (!strcmp(newJob.progs[0].argv[0], "fg") || 462:                !strcmp(newJob.progs[0].argv[0], "bg")) { 463:         if (!newJob.progs[0].argv[1] || newJob.progs[0].argv[2]) { 464:             fprintf(stderr, 465:                     "%s: exactly one argument is expected\n", 466:                     newJob.progs[0].argv[0]); 467:             return 1; 468:         } 469: 470:        if (sscanf(newJob.progs[0].argv[1], "%%%d", &jobNum) != 1) { 471:             fprintf(stderr, "%s: bad argument '%s'\n", 472:                     newJob.progs[0].argv[0], 473:                     newJob.progs[0].argv[1]); 474:             return 1; 475:         } 476: 477:         for (job = jobList->head; job; job = job->next) 478:             if (job->jobId == jobNum) break; 479: 480:         if (!job) { 481:             fprintf(stderr, "%s: unknown job %d\n", 482:                     newJob.progs[0].argv[0], jobNum); 483:             return 1; 484:         } 485: 486:         if (*newJob.progs[0].argv[0] == 'f') { 487:             /* Make this job the foreground job */ 488: 489:             if (tcsetpgrp(0, job->pgrp)) 490:                 perror("tcsetpgrp"); 491:             jobList->fg = job; 492:         } 493: 494:         /* Restart the processes in the job */ 495:         for (i = 0; i < job->numProgs; i++) 496:             job->progs[i].isStopped = 0; 497: 498:         kill(-job->pgrp, SIGCONT); 499: 500:         job->stoppedProgs = 0; 501: 502:         return 0; 503:     } 504: 505:     nextin = 0, nextout = 1; 506:     for (i = 0; i < newJob.numProgs; i++) { 507:         if ((i + 1) < newJob.numProgs) { 508:             pipe(pipefds); 509:             nextout = pipefds[1]; 510:         } else { 511:             nextout = 1; 512:         } 513: 514:         pipe(controlfds); 515: 516:         if (!(newJob.progs[i].pid = fork())) { 517:             signal(SIGTTOU, SIG_DFL); 518: 519:             close(controlfds[1]); 520:            /* this read will return 0 when the write side closes */ 521:             read(controlfds[0], &len, 1); 522:             close(controlfds[0]); 523: 524:             if (nextin != 0) { 525:                 dup2(nextin, 0); 526:                 close(nextin); 527:             } 528: 529:             if (nextout != 1) { 530:                 dup2(nextout, 1); 531:                 close(nextout); 532:             } 533: 534:             /* explicit redirections override pipes */ 535:             setupRedirections(newJob.progs + i); 536: 537:             execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); 538:             fprintf(stderr, "exec() of %s failed: %s\n", 539:                     newJob.progs[i].argv[0], 540:                     strerror(errno)); 541:             exit(1); 542:         } 543: 544:         /* put our child in the process group whose leader is the 545:            first process in this pipe */ 546:         setpgid(newJob.progs[i].pid, newJob.progs[0].pid); 547: 548:         /* close the control pipe so the child can continue */ 549:         close(controlfds[0]); 550:         close(controlfds[1]); 551: 552:         if (nextin != 0) close(nextin); 553:         if (nextout != 1) close(nextout); 554: 555:         /* If there isn't another process, nextin is garbage 556:            but it doesn't matter */ 557:         nextin = pipefds[0]; 558:     } 559: 560:     newJob.pgrp = newJob.progs[0].pid; 561: 562:     /* find the ID for the job to use */ 563:     newJob.jobId = 1; 564:     for (job = jobList->head; job; job = job->next) 565:         if (job->jobId >= newJob.jobId) 566:             newJob.jobId = job->jobId + 1; 567: 568:     /* add the job to the list of running jobs */ 569:     if (!jobList->head) { 570:         job = jobList->head = malloc(sizeof(*job)); 571:     } else { 572:         for (job = jobList->head; job->next; job = job->next); 573:         job->next = malloc(sizeof(*job)); 574:         job = job->next; 575:     } 576: 577:     *job = newJob; 578:     job->next = NULL; 579:     job->runningProgs = job->numProgs; 580:     job->stoppedProgs = 0; 581: 582:     if (inBg) { 583:         /* we don't wait for background jobs to return -- append it 584:            to the list of backgrounded jobs and leave it alone */ 585: 586:         printf("[%d] %d\n", job->jobId, 587:                newJob.progs[newJob.numProgs - 1].pid); 588:     } else { 589:         jobList->fg = job; 590: 591:         /* move the new process group into the foreground */ 592: 593:         if (tcsetpgrp(0, newJob.pgrp)) 594:             perror("tcsetpgrp"); 595:     } 596: 597:     return 0; 598: } 599: 600: void removeJob(struct jobSet * jobList, struct job * job) { 601:     struct job * prevJob; 602: 603:     freeJob(job); 604:     if (job == jobList->head) { 605:         jobList->head = job->next; 606:     } else { 607:         prevJob = jobList->head; 608:         while (prevJob->next != job) prevJob = prevJob->next; 609:         prevJob->next = job->next; 610:     } 611: 612:     free(job); 613: } 614: 615: /* Checks to see if any background processes have exited -- if they 616:    have, figure out why and see if a job has completed */ 617: void checkJobs(struct jobSet * jobList) { 618:     struct job * job; 619:     pid_t childpid; 620:     int status; 621:     int progNum; 622:     char * msg; 623: 624:     while ((childpid = waitpid(-1, &status, 625:                                WNOHANG | WUNTRACED)) > 0) { 626:         for (job = jobList->head; job; job = job->next) { 627:             progNum = 0; 628:             while (progNum < job->numProgs && 629:                         job->progs[progNum].pid != childpid) 630:                 progNum++; 631:             if (progNum < job->numProgs) break; 632:         } 633: 634:         if (WIFEXITED(status) || WIFSIGNALED(status)) { 635:             /* child exited */ 636:             job->runningProgs--; 637:             job->progs[progNum].pid = 0; 638: 639:             if (!WIFSIGNALED(status)) 640:                 msg = "Done"; 641:             else 642:                 msg = strsignal(WTERMSIG(status)); 643: 644:             if (!job->runningProgs) { 645:                 printf(JOB_STATUS_FORMAT, job->jobId, 646:                        msg, job->text); 647:                 removeJob(jobList, job); 648:             } 649:         } else { 650:             /* child stopped */ 651:             job->stoppedProgs++; 652:             job->progs[progNum].isStopped = 1; 653: 654:             if (job->stoppedProgs == job->numProgs) { 655:                 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", 656:                        job->text); 657:             } 658:         } 659:     } 660: 661:     if (childpid == -1 && errno != ECHILD) 662:         perror("waitpid"); 663: } 664: 665: int main(int argc, const char ** argv) { 666:     char command[MAX_COMMAND_LEN + 1]; 667:     char * nextCommand = NULL; 668:     struct jobSet jobList = { NULL, NULL }; 669:     struct job newJob; 670:     FILE * input = stdin; 671:     int i; 672:     int status; 673:     int inBg; 674: 675:     if (argc > 2) { 676:         fprintf(stderr, "unexpected arguments; usage: ladsh1 " 677:                         "<commands>\n"); 678:         exit(1); 679:     } else if (argc == 2) { 680:         input = fopen(argv[1], "r"); 681:         if (!input) { 682:             perror("fopen"); 683:             exit(1); 684:         } 685:     } 686: 687:     /* don't pay any attention to this signal; it just confuses 688:        things and isn't really meant for shells anyway */ 689:     signal(SIGTTOU, SIG_IGN); 690: 691:     while (1) { 692:         if (!jobList.fg) { 693:             /* no job is in the foreground */ 694: 695:             /* see if any background processes have exited */ 696:             checkJobs(&jobList); 697: 698:             if (!nextCommand) { 699:                 if (getCommand(input, command)) break; 700:                 nextCommand = command; 701:             } 702: 703:             if (!parseCommand(&nextCommand, &newJob, &inBg) && 704:                               newJob.numProgs) { 705:                 runCommand(newJob, &jobList, inBg); 706:             } 707:         } else { 708:             /* a job is running in the foreground; wait for it */ 709:             i = 0; 710:             while (!jobList.fg->progs[i].pid || 711:                    jobList.fg->progs[i].isStopped) i++; 712: 713:             waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); 714: 715:             if (WIFSIGNALED(status) && 716:                     (WTERMSIG(status) != SIGINT)) { 717:                 printf("%s\n", strsignal(status)); 718:             } 719: 720:             if (WIFEXITED(status) || WIFSIGNALED(status)) { 721:                 /* the child exited */ 722:                 jobList.fg->runningProgs--; 723:                 jobList.fg->progs[i].pid = 0; 724: 725:                 if (!jobList.fg->runningProgs) { 726:                     /* child exited */ 727: 728:                     removeJob(&jobList, jobList.fg); 729:                     jobList.fg = NULL; 730: 731:                     /* move the shell to the foreground */ 732:                     if (tcsetpgrp(0, getpid())) 733:                         perror("tcsetpgrp"); 734:                 } 735:             } else { 736:                 /* the child was stopped */ 737:                 jobList.fg->stoppedProgs++; 738:                 jobList.fg->progs[i].isStopped = 1; 739: 740:                 if (jobList.fg->stoppedProgs == 741:                                     jobList.fg->runningProgs) { 742:                     printf("\n" JOB_STATUS_FORMAT, 743:                            jobList.fg->jobId, 744:                            "Stopped", jobList.fg->text); 745:                     jobList.fg = NULL; 746:                 } 747:             } 748: 749:             if (!jobList.fg) { 750:                 /* move the shell to the foreground */ 751:                 if (tcsetpgrp(0, getpid())) 752:                     perror("tcsetpgrp"); 753:             } 754:         } 755:     } 756: 757:     return 0; 758: } 



       
    top
     


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168

    Similar book on Amazon

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net