home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume4 / xf / part02 / dir.c next >
Encoding:
C/C++ Source or Header  |  1989-09-07  |  25.4 KB  |  1,168 lines

  1. /* 
  2.  * dir.c --
  3.  *
  4.  *    Routines used to acquire file names and traverse the
  5.  *    directory tree.
  6.  *
  7.  * Copyright 1989 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  *
  16.  * Author: Gary Shea, UC Berkeley, Dept. of Chemistry
  17.  */
  18.  
  19. static char rcsid[] = "$Header: dir.c,v 1.3 89/07/18 00:04:01 garys Exp $";
  20.  
  21. #include <ctype.h>
  22. #include "xf.h"
  23.  
  24. /* #define DEBUG */
  25.  
  26.  
  27. /*
  28.  *----------------------------------------------------------------------
  29.  *
  30.  * DecomposePath --
  31.  *
  32.  *    Accept a path, return a list of pointers to char
  33.  *    with the various parts of the path in it.  This should
  34.  *    work for both relative and absolute pathnames.
  35.  *
  36.  * Results:
  37.  *    A pointer to an array of pointers to char is returned.
  38.  *    The array is null-terminated.
  39.  *
  40.  * Side effects:
  41.  *    Space is allocated for the array.
  42.  *
  43.  *  Arguments:
  44.  *    path - The path to take apart.
  45.  *    kind - By-reference parameter flags relative or absolute paths.
  46.  *    parts - Number of components in the path ('/' is a component).
  47.  *
  48.  *----------------------------------------------------------------------
  49.  */
  50.  
  51. #define VECTOR_INC    50
  52. #define START        0
  53. #define IN_DIR        1
  54. #define SEP        2
  55.  
  56. char **
  57. DecomposePath( char *path, PathKind *kind, int *parts )
  58. {
  59.     char    errLine[ ERRLINE_LEN ] ;
  60.     char    *pLocName = "DecomposePath()" ;
  61.  
  62.     char    pTempString[ MAXPATHLEN + 1 ] ;
  63.     char    *pCurString = pTempString ;
  64.     int        state ;
  65.  
  66.     char    **pVector ;
  67.     int        vecLen ;
  68.  
  69. #ifdef DEBUG
  70.     fprintf( pErrFp, "\n%s:\n", pLocName );
  71.     fprintf( pErrFp, "\tpath=<%s>\n", path ) ;
  72. #endif
  73.  
  74.     /* Skip leading white space. */
  75.  
  76.     while ( *path != '\0' && isspace( *path ) ) ++path ;
  77.  
  78. #ifdef DEBUG
  79.     fprintf( pErrFp, "\tPost-compression:\n" );
  80.     fprintf( pErrFp, "\tpath=<%s>\n", path ) ;
  81. #endif
  82.  
  83.     /* Take it apart, mon...
  84.     *  The only important things to look for are "/" and '\0' .
  85.     *
  86.     *  This thing will be a state-machine.
  87.     *        START : Nothing seen yet.
  88.     *        IN_DIR : Reading the name of a dir.
  89.     *        SEP : Just saw a separator.
  90.     */
  91.  
  92.     *parts = 0 ;
  93.     state = START ;
  94.     pVector = (char **)0 ;
  95.     vecLen = 0 ;
  96.  
  97.     for ( ; ; )
  98.     {
  99.  
  100. #ifdef DEBUG
  101.     fprintf( pErrFp,
  102.         "\tTop: strlen=%d, state=%d\n",
  103.         pCurString - pTempString, state ) ;
  104.     fprintf( pErrFp,
  105.         "\tparts=%d, vecLen=%d\n",
  106.         *parts, vecLen ) ;
  107. #endif
  108.  
  109.     switch( state )
  110.     {
  111.     case START :
  112.         if ( *path == '/' )
  113.         {
  114.         char    *sp ;
  115.  
  116.         /* It's an absolute path. */
  117.  
  118.         *kind = AbsPath ;
  119.  
  120.         /* Create the vector of string pointers. */
  121.  
  122.         vecLen = VECTOR_INC ;
  123.         TestAndGrow( pVector, (*parts), vecLen, VECTOR_INC, char * ) ;
  124.         sp = malloc( 2 ) ; sp[0] = '/' ; sp[1] = '\0' ;
  125.         pVector[ (*parts)++ ] = sp ;
  126.         pVector[ *parts ] = (char *) 0 ;
  127.  
  128. #ifdef DEBUG
  129.         fprintf( pErrFp,
  130.             "\t\tabsPath: Fake string <%s>\n",
  131.             pVector[ *parts - 1 ] ) ;
  132. #endif
  133.         state = SEP ;
  134.         ++path ;
  135.         }
  136.         else if ( *path == '\0' )
  137.         {
  138.         /* A null path. */
  139.  
  140.         UserError( "No path specified" );
  141.         *kind = NoPath ;
  142.         return ( NULL ) ;
  143.         }
  144.         else
  145.         {
  146.         /* A relative path. */
  147.  
  148.         *kind = RelPath ;
  149.         state = IN_DIR ;
  150.  
  151.         /* Save this char. */
  152.  
  153.         pCurString = pTempString ;
  154.         *(pCurString++) = *(path++) ;
  155.         *pCurString = '\0' ;
  156.  
  157. #ifdef DEBUG
  158.         fprintf( pErrFp, "\t\tSTART: Initialized temp string ptr.\n" );
  159. #endif
  160.  
  161.         }
  162.         break ;
  163.  
  164.     case IN_DIR :
  165.         if ( *path == '/' || *path == '\0' )
  166.         {
  167.         char *sp ;
  168.  
  169.         /* We just completed a dir name.  Allocate
  170.         *  a string for the currently completing dir
  171.         *  name, attach it to the list of names.
  172.         */
  173.  
  174.         TestAndGrow( pVector, (*parts), vecLen, VECTOR_INC, char * ) ;
  175.         pVector[ *parts ] =
  176.             malloc( (unsigned)(pCurString - pTempString + 1) ) ;
  177.         strcpy( pVector[ (*parts)++ ], pTempString ) ;
  178.         TestAndGrow( pVector, (*parts), vecLen, VECTOR_INC, char * ) ;
  179.         pVector[ *parts ] = (char *) 0 ;
  180.  
  181. #ifdef DEBUG
  182.         fprintf( pErrFp,
  183.             "\t\tstrLen=%d, tempStr:<%s>, newStr:<%s>\n",
  184.             pCurString - pTempString,
  185.             pTempString,
  186.             pVector[ *parts - 1 ] ) ;
  187. #endif
  188.  
  189.         if ( *path == '/' )
  190.         {
  191.             state = SEP ;
  192.             ++path ;
  193.         }
  194.         else if ( *path == '\0' )
  195.             return( pVector ) ;
  196.         }
  197.         else
  198.         {
  199.         /* More characters for this dir name. */
  200.  
  201.         if ( pCurString - pTempString + 2 >= MAXPATHLEN )
  202.         {
  203.             sprintf( errLine, "String too long (> %d)\n",
  204.             MAXPATHLEN ) ;
  205.             FatalError( pLocName, errLine );
  206.         }
  207.     
  208.         *(pCurString++) = *(path++) ;
  209.         *pCurString = '\0' ;
  210.  
  211. #ifdef DEBUG
  212.         /* I wanna look at the string, mon. */
  213.     
  214.         fprintf( pErrFp,
  215.             "\t\tcurStr:<%s>, strLen:%d\n",
  216.             pTempString, pCurString - pTempString + 1 ) ;
  217. #endif
  218.         }
  219.         break ;
  220.  
  221.     case SEP :
  222.         if ( *path == '/' )
  223.         {
  224.         /* Just ignore this... */
  225.  
  226. #ifdef DEBUG
  227.         fprintf( pErrFp, "\t\tSEP: Ignoring a '/'.\n" );
  228. #endif
  229.  
  230.         ++path ;
  231.  
  232.         }
  233.         else if ( *path == '\0' )
  234.         {
  235.         /* Now either the pathname is just '/',
  236.         *  or we're ending on a '/'.  Ending on a '/'
  237.         *  is no big deal - just ignore it.
  238.         *  If the pathname is /, then let the calling
  239.         *  program figure out that nothing's coming.
  240.         */
  241.  
  242. #ifdef DEBUG
  243.         fprintf( pErrFp, "\t\tSEP: End of string.\n" );
  244. #endif
  245.  
  246.         return( pVector ) ;
  247.         }
  248.         else
  249.         {
  250.         /* Starting a new dir name. */
  251.  
  252.         pCurString = pTempString ;
  253.         *(pCurString++) = *(path++) ;
  254.         *pCurString = '\0' ;
  255.         state = IN_DIR ;
  256.  
  257. #ifdef DEBUG
  258.         fprintf( pErrFp, "\t\tSEP: Initialized temp string ptr.\n" );
  259. #endif
  260.         }
  261.         break ;
  262.  
  263.     }
  264.     }
  265.  
  266. #ifdef DEBUG
  267.     fprintf( pErrFp, "\tEND: Shouldn't be here...\n" ) ;
  268. #endif
  269.  
  270.     FatalError ( pLocName, "Fell through loop" );
  271. }
  272.  
  273.  
  274. /*
  275.  *----------------------------------------------------------------------
  276.  *
  277.  * DirTree_AddBranch --
  278.  *
  279.  *    Given a valid subdirectory name and a DirTree,
  280.  *    add the subdirectory to the DirTree and fill out
  281.  *    all its stuff, and make the DirTree its root.
  282.  *    If the subdir exists, return a pointer to it.
  283.  *    In this sense, both '.' and '..' are subdir's.
  284.  *
  285.  * Results:
  286.  *    A pointer to the new DirTree is returned, or NULL
  287.  *    if it doesn't make sense.
  288.  *
  289.  * Side effects:
  290.  *    A new DirTree may be allocated and filled out,
  291.  *    also the branch list in the original DirTree may be
  292.  *    extended if necessary.
  293.  *
  294.  *  Arguments:
  295.  *    trvPtr - DirTree struct to add name below.
  296.  *    index - Index to the name in the files[] array.
  297.  *
  298.  *----------------------------------------------------------------------
  299.  */
  300.  
  301. DirTree    *
  302. DirTree_AddBranch( DirTree *trvPtr, int index )
  303. {
  304.     char    errLine[ ERRLINE_LEN ] ;
  305.     char    *pLocName = "DirTree_AddBranch()" ;
  306.  
  307.     int        branchIdx ;
  308.  
  309.  
  310. #ifdef DEBUG
  311.     fprintf ( pErrFp, "\n%s:\n", pLocName ) ;
  312. #endif
  313.  
  314.     /*
  315.     *  Is it really a subdirectory?
  316.     */
  317.  
  318.     if ( ( trvPtr->files[ index ].statMode & S_GFMT) != S_GFDIR )
  319.     {
  320.     int    err = 0 ;
  321.     char    *errString ;
  322.  
  323.     if ( (trvPtr->files[ index ].statMode & S_GFMT) == S_GFLNK )
  324.     {
  325.         char    path[ MAXPATHLEN + 1 ] ;
  326.         struct stat    statBuf ;
  327.  
  328.         strcpy ( path, trvPtr->fullName ) ;
  329.         strcat ( path, trvPtr->files[index].file ) ;
  330.         if ( stat ( path, &statBuf ) != 0 )
  331.         OSFatalError ( pLocName, "stat() failed" ) ;
  332.  
  333.         if ( (statBuf.st_mode & S_GFMT) != S_GFDIR )
  334.         {
  335.         errString = "Link to non-directory" ;
  336.         ++err ;
  337.         }
  338.     }
  339.     else
  340.     {
  341.         errString = "Not a directory" ;
  342.         ++err ;
  343.     }
  344.  
  345.     if ( err )
  346.     {
  347.         sprintf ( errLine,
  348.         "%s%s: %s",
  349.         trvPtr->relName != NULL ? trvPtr->relName : trvPtr->fullName,
  350.         trvPtr->files[index].file,
  351.         errString ) ;
  352.         UserError ( errLine ) ;
  353.         return ( NULL ) ;
  354.     }
  355.     }
  356.  
  357.     /*
  358.     *  Is it one of . or .. ?
  359.     */
  360.  
  361.     if ( (trvPtr->files[ index ].file)[0] == '.' )
  362.     {
  363.     if ( (trvPtr->files[ index ].file)[1] == '\0' )
  364.         return ( trvPtr ) ;
  365.     else if ( (trvPtr->files[ index ].file)[1] == '.'
  366.         && (trvPtr->files[ index ].file)[2] == '\0' )
  367.         return ( trvPtr->rootPtr ) ;
  368.     }
  369.  
  370.     /*
  371.     *  Has it ever been opened before?
  372.     */
  373.  
  374.     for ( branchIdx = 0 ; branchIdx < trvPtr->branchCnt ; ++branchIdx )
  375.     if ( trvPtr->branches[ branchIdx ].index == index )
  376.         break ;
  377.  
  378. #ifdef DEBUG
  379.     fprintf ( pErrFp, "\tbranchIdx=%d\n", branchIdx ) ;
  380. #endif
  381.  
  382.     if ( branchIdx >= trvPtr->branchCnt )
  383.     {
  384.     DirTree    *newDirPtr ;
  385.     char    path[ MAXPATHLEN + 1 ] ;
  386.  
  387.     /*
  388.     *  It hasn't been opened yet.
  389.     *  Make a DirTree struct for this subdir and
  390.     *  try to get it filled out.
  391.     */
  392.  
  393.     strcpy ( path, trvPtr->fullName ) ;
  394.     strcat ( path, trvPtr->files[index].file ) ;
  395.     strcat ( path, "/" ) ;
  396.     newDirPtr = DirTree_PresetNew ( StrSave( trvPtr->files[index].file ),
  397.             StrSave( path ), TRUE, trvPtr->dirLevel + 1 ) ;
  398.     FillFilesList ( newDirPtr ) ;
  399.  
  400.     if ( newDirPtr->fileCnt <= 0 )
  401.     {
  402.         sprintf ( errLine,
  403.         "%s%s: Directory is empty",
  404.         trvPtr->relName != NULL ? trvPtr->relName : trvPtr->fullName,
  405.         trvPtr->files[index].file ) ;
  406.         UserError ( errLine ) ;
  407.         return ( NULL ) ;
  408.     }
  409.  
  410. #ifdef DEBUG
  411.     fprintf ( pErrFp, "\tnewDirPtr:\n" ) ;
  412.     DirTree_Dump( newDirPtr ) ;
  413. #endif
  414.  
  415.     /* Now add the new branch to the list. */
  416.  
  417.     TestAndGrow( trvPtr->branches, trvPtr->branchCnt,
  418.         trvPtr->branchSlots, BRANCH_SLOT_INCREMENT, BranchData ) ;
  419.     trvPtr->branches[ trvPtr->branchCnt ].index = index ;
  420.     trvPtr->branches[ trvPtr->branchCnt ].branchPtr = newDirPtr ;
  421.     newDirPtr->rootPtr = trvPtr ;
  422.  
  423. #ifdef DEBUG1
  424.     fprintf ( pErrFp, "\tdirRootPtr:\n" ) ;
  425.     DirTree_TreeDump( dirRootPtr ) ;
  426. #endif
  427.  
  428.     branchIdx = trvPtr->branchCnt++ ;
  429.     }
  430.  
  431.     return ( trvPtr->branches[ branchIdx ].branchPtr ) ;
  432. }
  433.  
  434.  
  435. /*
  436.  *----------------------------------------------------------------------
  437.  *
  438.  * DirTree_Alloc --
  439.  *
  440.  *    Allocate and return a pointer to a DirTree struct.
  441.  *
  442.  * Results:
  443.  *    A pointer to freshly-allocated memory is returned.
  444.  *
  445.  * Side effects:
  446.  *    The contents of the structure are initialized.
  447.  *    See DirTree_Init().
  448.  *
  449.  *  Arguments:
  450.  *    None.
  451.  *
  452.  *----------------------------------------------------------------------
  453.  */
  454.  
  455. DirTree *
  456. DirTree_Alloc( void )
  457. {
  458.     DirTree    *dp ;
  459.  
  460.     char    errLine[ ERRLINE_LEN ] ;
  461.     char    *pLocName = "DirTree_Alloc()" ;
  462.  
  463.  
  464. #ifdef DEBUG
  465.     fprintf( pErrFp, "\n%s:\n", pLocName );
  466. #endif
  467.  
  468.  
  469.     if ( ( dp = (DirTree *) malloc( sizeof( DirTree ) ) ) == (DirTree *)0 )
  470.     FatalError( pLocName, "Out of Memory" ) ;
  471.     DirTree_Init( dp ) ;
  472.     return( dp ) ;
  473. }
  474.  
  475.  
  476. /*
  477.  *----------------------------------------------------------------------
  478.  *
  479.  * DirTree_CWD --
  480.  *
  481.  *    Build a DirTree that is the current working directory.
  482.  *
  483.  * Results:
  484.  *    A pointer to the new DirTree is returned.
  485.  *
  486.  * Side effects:
  487.  *    Lots.
  488.  *
  489.  *----------------------------------------------------------------------
  490.  */
  491.  
  492. void
  493. DirTree_CWD( void )
  494. {
  495.     char    errLine[ ERRLINE_LEN ] ;
  496.     char    *pLocName = "DirTree_CWD()" ;
  497.  
  498.     char    *getwd() ;
  499.     char    path[ MAXPATHLEN + 1 ] ;
  500.  
  501.  
  502. #ifdef DEBUG
  503.     fprintf ( pErrFp, "\n%s:\n", pLocName ) ;
  504. #endif
  505.  
  506.     /* Get the current direcory. */
  507.  
  508.     if ( getwd( path ) == NULL )
  509.     {
  510.     perror ( "getwd" ) ;
  511.     exit ( 1 ) ;
  512.     }
  513.  
  514.     /* Now build the tree... and set the global cwdPtr in the process. */
  515.  
  516.     if ( ( cwdPtr = DirTree_Find ( path )) == NULL )
  517.     exit ( 1 ) ;
  518.  
  519.     /* Set up the initial relative path stuff. */
  520.  
  521.     cwdPtr->relName = StrSave( "" ) ;
  522. }
  523.  
  524.  
  525. /*
  526.  *----------------------------------------------------------------------
  527.  *
  528.  * DirTree_ClientDump --
  529.  *
  530.  *    DirTree-tree node-dump routine for use as a callup by
  531.  *    DirTree_TreeTrav().
  532.  *
  533.  * Results:
  534.  *    None.
  535.  *
  536.  * Side effects:
  537.  *    Some i/o is done.
  538.  *
  539.  *  Arguments:
  540.  *    dirTreePtr - DirTree struct to dump.
  541.  *    clientData - Anonymous pointer to data that will be ignored.
  542.  *
  543.  *----------------------------------------------------------------------
  544.  */
  545.  
  546. void
  547. DirTree_ClientDump( DirTree *dirTreePtr, void *clientData )
  548. {
  549.     DirTree_Dump( dirTreePtr ) ;
  550. }
  551.  
  552.  
  553. /*
  554.  *----------------------------------------------------------------------
  555.  *
  556.  * DirTree_Dump --
  557.  *
  558.  *    Dump the contents of a DirTree struct to the err file.
  559.  *
  560.  * Results:
  561.  *    None.
  562.  *
  563.  * Side effects:
  564.  *    The err file fills with garbage.
  565.  *
  566.  *  Arguments:
  567.  *    dirTreePtr - Pointer to a DirTree struct.
  568.  *
  569.  *----------------------------------------------------------------------
  570.  */
  571.  
  572. void
  573. DirTree_Dump( DirTree *dirTreePtr )
  574. {
  575.     int        i, j ;
  576.  
  577.  
  578.     if ( dirTreePtr == (DirTree *)0 )
  579.     {
  580.     fprintf( pErrFp, "\nDirTree_Dump: null dirTreePtr\n" ) ;
  581.     return ;
  582.     }
  583.  
  584.     fprintf( pErrFp, "\nDirTree_Dump: <%s> at 0x%x\n",
  585.     dirTreePtr->name, dirTreePtr ) ;
  586.     fprintf( pErrFp, "\tnormal=%s, fullName=<%s>\n",
  587.     dirTreePtr ? "true" : "false",
  588.     dirTreePtr->fullName ) ;
  589.  
  590.     fprintf( pErrFp, "\tfiles list: fileCnt=%d, fileSlots=%d\n",
  591.     dirTreePtr->fileCnt, dirTreePtr->fileSlots ) ;
  592.     for ( i = 0 ; i < dirTreePtr->fileCnt ; ++i )
  593.     fprintf( pErrFp, "\t\t<%s>\n", dirTreePtr->files[ i ].file ) ;
  594.  
  595.     fprintf( pErrFp, "\trootPtr=0x%x, branchCnt=%d, branches=0x%x\n",
  596.     dirTreePtr->rootPtr, dirTreePtr->branchCnt, dirTreePtr->branches ) ;
  597. }
  598.  
  599.  
  600. /*
  601.  *----------------------------------------------------------------------
  602.  *
  603.  * DirTree_Find --
  604.  *
  605.  *    Given a (possibly invalid) directory name,
  606.  *    attempt to find it in the tree.  If it doesn't exist,
  607.  *    nodes are constructed as needed.
  608.  *
  609.  * Results:
  610.  *    A pointer to the DirTree which matches the path is returned.
  611.  *
  612.  * Side effects:
  613.  *    Lots.
  614.  *
  615.  *  Arguments:
  616.  *    path - The argument should be a path.
  617.  *
  618.  *----------------------------------------------------------------------
  619.  */
  620.  
  621. DirTree    *
  622. DirTree_Find( char *path )
  623. {
  624.     char    errLine[ ERRLINE_LEN ] ;
  625.     char    *pLocName = "DirTree_Find()" ;
  626.  
  627.     PathKind    kind = NoPath ;
  628.     char    **strVec ;
  629.     int        isRoot = FALSE ;
  630.  
  631.     int        parts ;
  632.     int        curPart ;
  633.  
  634.     DirTree    *trvPtr = NULL ;
  635.  
  636.  
  637. #ifdef DEBUG
  638.     fprintf( pErrFp, "\n%s:\n", pLocName );
  639. #endif
  640.  
  641.     if ( ( strVec = DecomposePath ( path, &kind, &parts ) ) == (char **) 0 )
  642.     return ( NULL ) ;
  643.  
  644. #ifdef DEBUG
  645.     fprintf( pErrFp, "\t%s:\n",
  646.     kind == AbsPath
  647.         ? "Absolute"
  648.         : kind == RelPath ? "Relative" : "NoPath" ) ;
  649. #endif
  650.  
  651.     /* If it's an absolute path, make sure the tree exists,
  652.     *  set the first part as the next one after the '/',
  653.     *  and set the traversal to begin from the DirTree root.
  654.     *  If a relative path, set the traversal to begin from
  655.     *  the cwd.
  656.     */
  657.  
  658.     if ( kind == AbsPath )
  659.     {
  660.     /* First make sure the tree exists. */
  661.  
  662.     if ( dirRootPtr == NULL )
  663.     {
  664.         dirRootPtr = DirTree_PresetNew ( "/", "/", TRUE, 0 ) ;
  665.         FillFilesList ( dirRootPtr ) ;
  666.     }
  667.  
  668.     /* Now step through the remaining fields, if any. */
  669.  
  670.     trvPtr = dirRootPtr ;
  671.     curPart = 1 ;
  672.     }
  673.     else
  674.     {
  675.     if ( cwdPtr == NULL )
  676.         InternalError ( pLocName, "cwdPtr not initialized" ) ;
  677.     else
  678.     {
  679.         trvPtr = cwdPtr ;
  680.         curPart = 0 ;
  681.     }
  682.     }
  683.  
  684.     for ( ; curPart < parts ; ++curPart )
  685.     {
  686.     DirTree    *newDirPtr ;
  687.     int    fileIdx ;
  688.     int    branchIdx ;
  689.  
  690.  
  691. #ifdef DEBUG
  692.     fprintf ( pErrFp, "\tTop of loop: curPart=%d\n", curPart ) ;
  693.     if ( trvPtr != NULL )
  694.     {
  695.         fprintf ( pErrFp, "\ttrvPtr:\n" ) ;
  696.         DirTree_Dump( trvPtr ) ;
  697.     }
  698.     else
  699.         fprintf ( pErrFp, "\ttrvPtr is NULL\n" ) ;
  700. #endif
  701.  
  702.     /* Do we have the files for this dir yet? */
  703.  
  704.     if ( trvPtr->fileCnt <= 0 )
  705.         InternalError ( pLocName, "No files" ) ;
  706.  
  707.     /* Is the sub-dir we're looking for in here?
  708.     *  I agree that this is tacky, but it's how
  709.     *  i'm doing it anyway.
  710.     */
  711.  
  712.     if ( strcmp ( strVec[curPart], "." ) == 0 )
  713.         newDirPtr = trvPtr ;
  714.     else if ( strcmp ( strVec[curPart], ".." ) == 0 )
  715.         newDirPtr = trvPtr->rootPtr ;
  716.     else
  717.     {
  718.         for ( fileIdx = 0 ; fileIdx < trvPtr->fileCnt ; ++fileIdx )
  719.         {
  720.         if ( strcmp ( strVec[curPart],
  721.             trvPtr->files[fileIdx].file ) == 0 )
  722.             break ;
  723.         }
  724.  
  725.         if ( fileIdx >= trvPtr->fileCnt )
  726.         {
  727.         sprintf ( errLine, "%s: No such file or directory", path ) ;
  728.         UserError ( errLine ) ;
  729.         return ( NULL ) ;
  730.         }
  731.  
  732. #ifdef DEBUG
  733.         fprintf ( pErrFp, "\tfileIdx=%d\n", fileIdx ) ;
  734. #endif
  735.  
  736.         if ( ( newDirPtr = DirTree_AddBranch( trvPtr, fileIdx )) == NULL )
  737.         return ( NULL ) ;
  738.     }
  739.  
  740.     /* If a relative path was specified, then propagate
  741.     *  the relative path into the new directory.  This
  742.     *  may well be duplicated effort, but the check for
  743.     *  length means you can't really mess things up
  744.     *  (i hope).
  745.     */
  746.  
  747.     if ( kind == RelPath )
  748.     {
  749.         char    path[ MAXPATHLEN + 1 ] ;
  750.  
  751.  
  752.         if ( trvPtr->relName == NULL )
  753.         InternalError ( pLocName, "relName NULL" ) ;
  754.  
  755.         strcpy ( path, trvPtr->relName ) ;
  756.         strcat ( path, strVec[curPart] ) ;
  757.         strcat ( path, "/" ) ;
  758.         if ( newDirPtr->relName == NULL
  759.         || ( newDirPtr->relName != NULL
  760.             && strlen ( path ) < strlen ( newDirPtr->relName ) ) )
  761.             newDirPtr->relName = StrSave( path ) ;
  762.     }
  763.  
  764.     trvPtr = newDirPtr ;
  765.     }
  766.  
  767.     return ( trvPtr ) ;
  768. }
  769.  
  770.  
  771. /*
  772.  *----------------------------------------------------------------------
  773.  *
  774.  * DirTree_FindWidget --
  775.  *
  776.  *    Search a tree of widgets for a particular one.  Any
  777.  *    of the command widgets may be searched for by Widget id,
  778.  *    by specifying the appropriate member of WidgetToFind.
  779.  *
  780.  * Results:
  781.  *    A pointer to the DirTree struct holding the widget is
  782.  *    returned if the search succeeds, otherwise NULL.
  783.  *
  784.  * Side effects:
  785.  *    None.
  786.  *
  787.  *----------------------------------------------------------------------
  788.  */
  789.  
  790. DirTree    *
  791. DirTree_FindWidget( DirTree *dirTreePtr, WidgetToFind kind, Widget w )
  792. {
  793.     char    errLine[ ERRLINE_LEN ] ;
  794.     char    *pLocName = "DirTree_FindWidget()" ;
  795.  
  796.     int        i ;
  797.  
  798.  
  799. #ifdef DEBUG
  800.     fprintf ( pErrFp, "\n%s:\n", pLocName ) ;
  801. #endif
  802.  
  803.     if ( dirTreePtr == (DirTree *)0 ) return ( NULL ) ;
  804.  
  805.     if ( dirTreePtr->isDisplayed )
  806.     {
  807.     Widget    tw ;
  808.  
  809.     switch ( kind )
  810.     {
  811.         case SetW : tw = dirTreePtr->setW ; break ;
  812.         case UpW : tw = dirTreePtr->upComW ; break ;
  813.         case QuitW : tw = dirTreePtr->quitComW ; break ;
  814.         case DoneW : tw = dirTreePtr->doneComW ; break ;
  815.     }
  816.  
  817.     if ( tw == w )
  818.         return ( dirTreePtr ) ;
  819.     }
  820.  
  821.     /* Recursively search each of the sub-trees. */
  822.  
  823.     for ( i = 0 ; i < dirTreePtr->branchCnt ; ++i )
  824.     {
  825.     if ( dirTreePtr->branches[ i ].branchPtr != NULL )
  826.     {
  827.         DirTree    *dtPtr ;
  828.  
  829.         if ( ( dtPtr =
  830.         DirTree_FindWidget( dirTreePtr->branches[ i ].branchPtr,
  831.             kind, w )) != NULL )
  832.             return ( dtPtr ) ;
  833.     }
  834.     }
  835.  
  836.     return ( NULL ) ;
  837. }
  838.  
  839.  
  840. /*
  841.  *----------------------------------------------------------------------
  842.  *
  843.  * DirTree_Init --
  844.  *
  845.  *    Initialize a DirTree struct.
  846.  *
  847.  * Results:
  848.  *    None.
  849.  *
  850.  * Side effects:
  851.  *    The contents of the struct are modified.
  852.  *
  853.  *  Arguments:
  854.  *    dp - Pointer to a DirTree struct.
  855.  *
  856.  *----------------------------------------------------------------------
  857.  */
  858.  
  859. void
  860. DirTree_Init( DirTree *dp )
  861. {
  862.     char    errLine[ ERRLINE_LEN ] ;
  863.     char    *pLocName = "DirTree_Init()" ;
  864.  
  865.  
  866. #ifdef DEBUG
  867.     fprintf( pErrFp, "\n%s:\n", pLocName );
  868. #endif
  869.  
  870.     dp->name = NULL ;
  871.     dp->fullName = NULL ;
  872.     dp->relName = NULL ;
  873.     dp->normal = FALSE ;
  874.  
  875.     dp->fileCnt = 0 ;
  876.     dp->fileSlots = 0 ;
  877.     dp->files = NULL ;
  878.  
  879.     dp->rootPtr = NULL ;
  880.     dp->isDisplayed = FALSE ;
  881.  
  882.     dp->branchCnt = 0 ;
  883.     dp->branchSlots = 0 ;
  884.     dp->branches = NULL ;
  885. }
  886.  
  887.  
  888. /*
  889.  *----------------------------------------------------------------------
  890.  *
  891.  * DirTree_PresetNew --
  892.  *
  893.  *    A convenience routine to create, initialize and fill in
  894.  *    some of the more commonly used fields of a DirTree.
  895.  *
  896.  * Results:
  897.  *    Pointer to the newly allocated DirTree is returned.
  898.  *
  899.  * Side effects:
  900.  *    An DirTree is allocated, DirTree_Init is called on it, some of
  901.  *    its fields are modified based on the input parameters.
  902.  *
  903.  *  Arguments:
  904.  *    namePtr - The name of the dirTree.
  905.  *    addr - Address in memory which the dirTree refers to.
  906.  *    status - Has the address been determined yet?
  907.  *
  908.  *----------------------------------------------------------------------
  909.  */
  910.  
  911. DirTree *
  912. DirTree_PresetNew( char *name, char *fullName, int normal, int dirLevel )
  913. {
  914.     char    errLine[ ERRLINE_LEN ] ;
  915.     char    *pLocName = "DirTree_PresetNew()" ;
  916.  
  917.     DirTree    *dp ;
  918.  
  919. #ifdef DEBUG
  920.     fprintf( pErrFp, "\n%s:\n", pLocName );
  921.     fprintf( pErrFp, "\tname=<%s>, fullName=<%s>, normal=%d\n",
  922.     name, fullName, normal ) ;
  923.  
  924.     /* Test to see if the conditions on fullName are met. */
  925.     {    int len = strlen ( fullName ) ;
  926.     if ( fullName [ len - 1 ] != '/' )
  927.         InternalError ( pLocName, "fullName not '/'-terminated" ) ;
  928.     }
  929. #endif
  930.  
  931.     dp = DirTree_Alloc() ;
  932.     dp->name = name ;
  933.     dp->fullName = fullName ;
  934.     dp->normal = normal ;
  935.     dp->dirLevel = dirLevel ;
  936.  
  937.     return( dp ) ;
  938. }
  939.  
  940.  
  941. /*
  942.  *----------------------------------------------------------------------
  943.  *
  944.  * DirTree_TreeDump --
  945.  *
  946.  *    Dump the contents of the DirTree tree with the
  947.  *    given root.
  948.  *
  949.  * Results:
  950.  *    None.
  951.  *
  952.  * Side effects:
  953.  *    More garbage in the err file.
  954.  *
  955.  *  Arguments:
  956.  *    rootPtr - Root of a DirTree tree.
  957.  *
  958.  *----------------------------------------------------------------------
  959.  */
  960.  
  961. void
  962. DirTree_TreeDump( DirTree *rootPtr )
  963. {
  964.     char    errLine[ ERRLINE_LEN ] ;
  965.     char    *pLocName = "DirTree_TreeDump()" ;
  966.  
  967.  
  968.     fprintf( pErrFp, "\n%s:\n", pLocName );
  969.     DirTree_TreeTrav( rootPtr, PreOrder, DirTree_ClientDump, (void *)0 ) ;
  970.     fprintf( pErrFp, "END DirTree_TreeDump()\n" ) ;
  971. }
  972.  
  973.  
  974. /*
  975.  *----------------------------------------------------------------------
  976.  *
  977.  * DirTree_TreeTrav --
  978.  *
  979.  *    Traverse the DirTree_Tree,
  980.  *    calling the passed in function on each node as it is
  981.  *    visited.  This is a structure-hiding routine - i hope
  982.  *    it's useful!!!.
  983.  *
  984.  * Results:
  985.  *    None.
  986.  *
  987.  * Side effects:
  988.  *    The passed in fcn gets called on every node of the tree.
  989.  *
  990.  *  Arguments:
  991.  *    dirTreePtr - Pointer to the root of the tree.
  992.  *    travMode - Whether the traversal is InOrder, PreOrder, or PostOrder.
  993.  *    NodeOp - Function that will operate on the nodes.
  994.  *    clientData - Anonymous pointer to data used by NodeOp.
  995.  *
  996.  *----------------------------------------------------------------------
  997.  */
  998.  
  999. void
  1000. DirTree_TreeTrav( DirTree *dirTreePtr, TreeOrder travOrder,
  1001.     void NodeOp( DirTree *nodePtr, void *clientData ),
  1002.     void *clientData )
  1003. {
  1004.     char    errLine[ ERRLINE_LEN ] ;
  1005.     char    *pLocName = "DirTree_TreeTrav()" ;
  1006.  
  1007.     int        i ;
  1008.  
  1009.  
  1010. #ifdef DEBUG
  1011.     fprintf( pErrFp, "\n%s:\n", pLocName );
  1012. #endif
  1013.  
  1014.  
  1015.     if ( dirTreePtr == (DirTree *)0 ) return ;
  1016.  
  1017.     if ( travOrder == PreOrder )
  1018.     NodeOp( dirTreePtr, clientData ) ;
  1019.  
  1020.     for ( i = 0 ; i < dirTreePtr->branchCnt ; ++i )
  1021.     if ( dirTreePtr->branches[ i ].branchPtr != (DirTree *)0 )
  1022.         DirTree_TreeTrav( dirTreePtr->branches[ i ].branchPtr,
  1023.         travOrder, NodeOp, clientData ) ;
  1024.  
  1025.     if ( travOrder == PostOrder )
  1026.     NodeOp( dirTreePtr, clientData ) ;
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  *----------------------------------------------------------------------
  1032.  *
  1033.  * FileData_Compare --
  1034.  *
  1035.  *    Compare two FileData structs by strcmp()-ing the file
  1036.  *    fields.  This is a qsort()-callable routine and is used
  1037.  *    to sort the list of files.
  1038.  *
  1039.  * Results:
  1040.  *    An integer less than zero is returned if leftPtr < rightPtr,
  1041.  *    0 if they are equal,
  1042.  *    an integer greater than zero is returned if leftPtr > rightPtr.
  1043.  *
  1044.  * Side effects:
  1045.  *    None.
  1046.  *
  1047.  *  Arguments:
  1048.  *    leftPtr - Pointer to a pointer to a FileData struct.
  1049.  *    rightPtr - Pointer to a pointer to a FileData struct.
  1050.  *
  1051.  *----------------------------------------------------------------------
  1052.  */
  1053.  
  1054. static int
  1055. FileData_Compare( FileData *leftPtr, FileData *rightPtr )
  1056. {
  1057.     char    errLine[ ERRLINE_LEN ] ;
  1058.     char    *pLocName = "FileData_Compare()" ;
  1059.  
  1060. # ifdef DEBUG
  1061.     fprintf ( pErrFp, "\n%s:\n", pLocName ) ;
  1062. # endif
  1063.  
  1064.     return ( strcmp ( leftPtr->file, rightPtr->file ) ) ;
  1065. }
  1066.  
  1067.  
  1068. /*
  1069.  *----------------------------------------------------------------------
  1070.  *
  1071.  * FillFilesList --
  1072.  *
  1073.  *    Given a DirTree with its fullName filled in,
  1074.  *    fill in its files[] list.
  1075.  *
  1076.  * Results:
  1077.  *    Currently returns True, or dies if there's an error.
  1078.  *
  1079.  * Side effects:
  1080.  *    Mucho space is allocated for the filenames.
  1081.  *
  1082.  *  Arguments:
  1083.  *    trvPtr - The DirTree to fill in.
  1084.  *
  1085.  *----------------------------------------------------------------------
  1086.  */
  1087.  
  1088. int
  1089. FillFilesList ( DirTree *trvPtr )
  1090. {
  1091.     char    errLine[ ERRLINE_LEN ] ;
  1092.     char    *pLocName = "FillFilesList()" ;
  1093.  
  1094.     DIR        *dirFp ;
  1095.     DirEnt    *entPtr ;
  1096.     char    *sp ;
  1097.  
  1098.     char    path[ MAXPATHLEN ] ;
  1099.     int        pathLen ;
  1100.  
  1101.     struct stat    statBuf ;
  1102.     
  1103.     static int    FileData_Compare( FileData **leftPtr, FileData **rightPtr ) ;
  1104.  
  1105.  
  1106. #ifdef DEBUG
  1107.     fprintf( pErrFp, "\n%s:\n", pLocName );
  1108. #endif
  1109.  
  1110.     /* First try to open the directory in which we are sitting. */
  1111.  
  1112.     if ( ( dirFp = opendir ( trvPtr->fullName ) ) == NULL )
  1113.     InternalError ( pLocName, "opendir() failed" ) ;
  1114.  
  1115.     /* Get ready for calling lsat by making a string to tack
  1116.     *  file names onto the end of.
  1117.     */
  1118.  
  1119.     strcpy ( path, trvPtr->fullName ) ;
  1120.     pathLen = strlen ( path ) ;
  1121.  
  1122.     /* Now traverse the directory getting the files. */
  1123.  
  1124.     for ( entPtr = readdir( dirFp )
  1125.     ; entPtr != (DirEnt *)0
  1126.     ; entPtr = readdir( dirFp ) )
  1127.     {
  1128.     if ( !ls_mode_a && entPtr->d_name[ 0 ] == '.' ) continue ;
  1129.  
  1130.     /* Construct a path so that lstat() may be called. */
  1131.  
  1132.     path[ pathLen ] = '\0' ;
  1133.     strcat ( &path[pathLen], entPtr->d_name ) ;
  1134.     if ( lstat ( path, &statBuf ) != 0 )
  1135.         OSFatalError ( pLocName, "lstat() failed" ) ;
  1136.  
  1137.     /* Make sure there's someplace to put the (local) name. */
  1138.  
  1139.     TestAndGrow ( trvPtr->files, trvPtr->fileCnt,
  1140.         trvPtr->fileSlots, FILE_SLOT_INCREMENT, FileData ) ;
  1141.  
  1142.     trvPtr->files[ trvPtr->fileCnt ].file = StrSave ( entPtr->d_name ) ;
  1143.     trvPtr->files[ trvPtr->fileCnt ].statMode = statBuf.st_mode ;
  1144.  
  1145. #ifdef DEBUG1
  1146.     fprintf ( pErrFp, "\t\t%d: <%s>, statMode=0%o\n",
  1147.         trvPtr->fileCnt,
  1148.         trvPtr->files[ trvPtr->fileCnt ].file,
  1149.         trvPtr->files[ trvPtr->fileCnt ].statMode ) ;
  1150. #endif
  1151.  
  1152.     trvPtr->fileCnt++ ;
  1153.     }
  1154.  
  1155. #ifdef DEBUG
  1156.     fprintf ( pErrFp, "\t%s: DONE\n", pLocName ) ;
  1157. #endif
  1158.  
  1159.     /* Sort the files, if there are any... */
  1160.  
  1161.     if ( trvPtr->fileCnt > 0 )
  1162.     qsort ( (char *) trvPtr->files, trvPtr->fileCnt,
  1163.         sizeof(FileData), FileData_Compare ) ;
  1164.  
  1165.     return( TRUE ) ;
  1166. }
  1167.  
  1168.