Section 14.6. Adding Directories and Globbing to ladsh

   


14.6. Adding Directories and Globbing to ladsh

The evolution of ladsh continues here by adding four new features to ladsh3.c.

  1. The cd built-in, to change directories.

  2. The pwd built-in, to display the current directory.

  3. File name globbing.

  4. Some new messages are displayed to take advantage of strsignal(). These changes are discussed on page 222 of Chapter 12.

14.6.1. Adding cd and pwd

Adding the built-in commands is a straightforward application of chdir() and getcwd(). The code fits into runProgram() right where all the other built-in commands are handled. Here is how the built-in command-handling section looks in ladsh3.c:

 422:     if (!strcmp(newJob.progs[0].argv[0], "exit")) { 423:         /* this should return a real exit code */ 424:         exit(0); 425:     } else if (!strcmp(newJob.progs[0].argv[0], "pwd")) { 426:         len = 50; 427:         buf = malloc(len); 428:         while (!getcwd(buf, len) && errno == ERANGE) { 429:             len += 50; 430:             buf = realloc(buf, len); 431:         } 432:         printf("%s\n", buf); 433:         free(buf); 434:         return 0; 435:     } else if (!strcmp(newJob.progs[0].argv[0], "cd")) { 436:         if (!newJob.progs[0].argv[1] == 1) 437:             newdir = getenv("HOME"); 438:         else 439:             newdir = newJob.progs[0].argv[1]; 440:         if (chdir(newdir)) 441:             printf("failed to change current directory: %s\n", 442:                     strerror(errno)); 443:         return 0; 444:     } else if (!strcmp(newJob.progs[0].argv[0], "jobs")) { 445:         for (job = jobList->head; job; job = job->next) 446:             printf(JOB_STATUS_FORMAT, job->jobId, "Running", 447:                     job->text); 448:         return 0; 449:     } 


14.6.2. Adding File Name Globbing

File name globbing, in which the shell expands the *, [], and ? characters into matching file names, is a bit tricky to implement because of the various quoting methods. The first modification is to build up each argument as a string suitable for passing to glob(). If a globbing character is quoted by a shell quoting sequence (enclosed in double quotes, for example), then the globbing character is prefixed by a \ to prevent glob() from expanding it. Although this sounds tricky, it is easy to do.

Two parts of parseCommand()'s command parsing need to be slightly modified. The " and ' sequences are handled near the top of the loop, which splits a command string into arguments. If we are in the middle of a quoted string and we encounter a globbing character, we quote the globbing character with a \ while parsing it, which looks like this:

 189:         } else if (quote) { 190:             if (*src == '\\') { 191:                 src++; 192:                 if (!*src) { 193:                     fprintf(stderr, 194:                             "character expected after \\\n"); 195:                     freeJob(job); 196:                     return 1; 197:                 } 198: 199:                 /* in shell, "\'" should yield \' */ 200:                 if (*src != quote) *buf++ = '\\'; 201:             } else if (*src == '*' || *src == '?' || *src == '[' || 202:                        *src == ']') 203:                 *buf++ = '\\'; 204:             *buf++ = *src; 205:         } else if (isspace(*src)) { 


Only the middle else if and the assignment statement in its body were added to the code. Similar code needs to be added to the handling of \ characters that occur outside quoted strings. This case is handled at the end of parseCommand()'s main loop. Here is the modified code:

 329:         case '\\': 330:             src++; 331:             if (!*src) { 332:                 freeJob(job); 333:                 fprintf(stderr, "character expected after \\\n"); 334:                 return 1; 335:             } 336:             if (*src == '*' || *src == '[' || *src == ']' 337:                             || *src == '?') 338:                 *buf++ = '\\'; 339:             /* fallthrough */ 340:         default: 341:             *buf++ = *src; 


The same code was added here to quote the globbing characters.

Those two sequences of code ensure that each argument may be passed to glob() without finding unintended matches. Now we add a function, globLastArgument(), which globs the most recently found argument for a child program and replaces it with whatever matches it finds.

To help ease the memory management, a glob_t called globResult, which is used to hold the results of all glob operations, has been added to struct childProgram. We also added an integer, freeGlob, which is nonzero if freeJob() should free the globResult contained in the structure. Here is the complete definition for struct childProgram in ladsh3.c:

 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: }; 


The first time globLastArgument() is run for a command string (when argc for the current child is 1), it initializes globResult. For the rest of the arguments, it takes advantage of GLOB_APPEND to add new matches to the end of the existing matches. This prevents us from having to allocate our own memory for globbing because our single glob_t is automatically expanded as necessary.

If globLastArgument() does not find any matches, the quoting \ characters are removed from the argument. Otherwise, all the new matches are copied into the list of arguments being constructed for the child program.

Here is the complete implementation of globLastArgument(). All the tricky parts are related to memory management; the actual globbing is similar to the globit.c sample program presented earlier in this chapter.

  87: void globLastArgument(struct childProgram * prog, int * argcPtr,  88:                         int * argcAllocedPtr) {  89:     int argc = *argcPtr;  90:     int argcAlloced = *argcAllocedPtr;  91:     int rc;  92:     int flags;  93:     int i;  94:     char * src, * dst;  95:  96:     if (argc > 1) {        /* cmd->globResult already initialized */  97:         flags = GLOB_APPEND;  98:         i = prog->globResult.gl_pathc;  99:     } else { 100:         prog->freeGlob = 1; 101:         flags = 0; 102:         i = 0; 103:     } 104: 105:    rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); 106:     if (rc == GLOB_NOSPACE) { 107:         fprintf(stderr, "out of space during glob operation\n"); 108:         return; 109:     } else if (rc == GLOB_NOMATCH || 110:                (!rc && (prog->globResult.gl_pathc - i) == 1 && 111:                 !strcmp(prog->argv[argc - 1], 112:                         prog->globResult.gl_pathv[i]))) { 113:         /* we need to remove whatever \ quoting is still present */ 114:         src = dst = prog->argv[argc - 1]; 115:         while (*src) { 116:             if (*src != '\\') *dst++ = *src; 117:             src++; 118:         } 119:         *dst = '\0'; 120:     } else if (!rc) { 121:         argcAlloced += (prog->globResult.gl_pathc - i); 122:         prog->argv = realloc(prog->argv, 123:                              argcAlloced * sizeof(*prog->argv)); 124:         memcpy(prog->argv + (argc - 1), 125:                prog->globResult.gl_pathv + i, 126:                sizeof(*(prog->argv)) * 127:                       (prog->globResult.gl_pathc - i)); 128:         argc += (prog->globResult.gl_pathc - i - 1); 129:     } 130: 131:     *argcAllocedPtr = argcAlloced; 132:     *argcPtr = argc; 133: } 


The final changes are the calls to globLastArgument() that need to be made once a new argument has been parsed. The calls are added in two places: when white space is found outside a quoted string and when the entire command string has been parsed. Both of the calls look like this:

 globLastArgument(prog, &argc, &argvAlloced); 


The complete source code for ladsh3.c is available on the LAD Web site at http://ladweb.net/lad/src/.


       
    top
     


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

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