Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtappbuilder / src / libAButil / util_file.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23
24 /*
25  *      $XConsortium: util_file.c /main/4 1995/11/06 18:54:05 rswiston $
26  *
27  * @(#)util_file.c      1.24 19 Apr 1995        cde_app_builder/src/libAButil
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
31  *      The information in this document is subject to special
32  *      restrictions in a confidential disclosure agreement between
33  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
34  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
35  *      Sun's specific written approval.  This document and all copies
36  *      and derivative works thereof must be returned or destroyed at
37  *      Sun's request.
38  *
39  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
40  *
41  */
42
43
44 /*
45  * File: util_file.c
46  */
47
48 #include <fcntl.h>
49 #include <string.h>
50 #include <sys/param.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <errno.h>
54 #include <ab_private/abio.h>
55 #include "utilP.h"
56
57 /*************************************************************************
58 **                                                                      **
59 **       Private Function Declarations                                  **
60 **                                                                      **
61 **************************************************************************/
62
63 /*************************************************************************
64 **                                                                      **
65 **       Data                                                           **
66 **                                                                      **
67 **************************************************************************/
68 #define AB_EXT_LENGTH 3
69
70 /*************************************************************************
71 **                                                                      **
72 **       Function Definitions                                           **
73 **                                                                      **
74 **************************************************************************/
75
76 /*
77  * removes all buffering from a file.  This is useful for debugging
78  * to avoid losing data, and to keep stdout and stderr messages from
79  * getting jumbled (ordered is basically guaranteed if both streams
80  * are unbuffered).  
81  *
82  * This *is* a performance hit, so don't use this except when debugging.
83  * Note that stderr is always unbuffered.
84  */
85 int
86 util_unbuffer_file(FILE *fp)
87 {
88     int         iReturn= 0;
89     int         iRC= 0;         /* return code */
90     int         fileMode= 0;
91
92     setbuf(fp, NULL);
93     /* this is too much - actually waits for sync to disk, and runs
94      * RIDICULOUSLY SLOW.
95      */
96     /*
97     if ((iRC= fileMode= fcntl(fileno(fp), F_GETFL)) >= 0)
98     {
99         fileMode |= O_DSYNC;
100         iRC= fcntl(fileno(fp), F_SETFL, fileMode);
101     }
102     */
103     iReturn= iRC;
104
105     return iReturn;
106 }
107
108
109 BOOL
110 util_file_exists(STRING fileName)
111 {
112     struct stat fileInfo;
113     return (stat(fileName, &fileInfo) == 0);
114 }
115
116
117 /* Returns the file size, or  ERR_ value (<0) on error
118  *
119  */
120 long
121 util_file_size(STRING fileName)
122 {
123     struct stat fileInfo;
124     if (stat(fileName, &fileInfo) != 0)
125     {
126         return ERR_FILE_NOT_FOUND;
127     }
128     return (long)fileInfo.st_size;
129 }
130
131
132 /*
133  * Extension should not contain it's leading "."
134  *
135  * if extension is NULL or the empty string, returns TRUE if the name
136  * has no extension
137  */
138 BOOL
139 util_file_name_has_extension(STRING fileName, STRING extension)
140 {
141     BOOL        hasExtension= FALSE;
142
143     if (extension == NULL)
144     {
145         hasExtension = ( (fileName == NULL) || (strlen(fileName) == 0) );
146     }
147     else
148     {
149         if (fileName == NULL)
150         {
151             hasExtension= FALSE;
152         }
153         else
154         {
155             char *dotPtr= strrchr(fileName, '.');
156             if (dotPtr == NULL)
157             {
158                 hasExtension= FALSE;
159             }
160             else
161             {
162                 hasExtension= util_streq(dotPtr+1, extension);
163             }
164         }
165     }
166
167     return hasExtension;
168 }
169
170
171 BOOL
172 util_file_name_has_ab_extension(STRING fileName)
173 {
174     BOOL        hasExtension= FALSE;
175     char        *dotPtr= strrchr(fileName, '.');
176
177     if (dotPtr != NULL)
178     {
179         hasExtension= (strncmp(dotPtr, ".bi", 3) == 0);
180     }
181     return hasExtension;
182 }
183
184
185 STRING
186 util_get_file_name_from_path(
187                 STRING  path,
188                 STRING  fileNameBuf,
189                 int     fileNameBufSize
190 )
191 {
192     char        *slashPtr= strrchr(path, '/');
193     if (slashPtr == NULL)
194     {
195         strncpy(fileNameBuf, path, fileNameBufSize);
196     }
197     else
198     {
199         strncpy(fileNameBuf, slashPtr+1, fileNameBufSize);
200     }
201     fileNameBuf[fileNameBufSize-1]= 0;
202     return fileNameBuf;
203 }
204
205
206 /*
207  * If path is not absolute, directory is "."
208  *
209  * The directory name returned does NOT have a trailing '/'.  (The
210  * only exception being the directory "/" .
211  */
212 STRING
213 util_get_dir_name_from_path(
214                 STRING  path,
215                 STRING  dirNameBuf,
216                 int     dirNameBufSize
217 )
218 {
219     char        *slashPtr= strrchr(path, '/');
220     assert(dirNameBufSize > 1);
221     if (slashPtr == NULL)
222     {
223         /* no directory name */
224         strcpy(dirNameBuf, ".");
225     }
226     else if (slashPtr == path)
227     {
228         /* file in / */
229         strcpy(dirNameBuf, "/");
230     }
231     else
232     {
233         /* directory and file name */
234         int     copyCount= 
235                     util_min(dirNameBufSize, (((int)(slashPtr - path))+1));
236         strncpy(dirNameBuf, path, copyCount);
237         dirNameBuf[dirNameBufSize-1]= 0;
238         while ((copyCount > 1) && (dirNameBuf[copyCount-1] == '/'))
239         {
240             --copyCount;
241         }
242         dirNameBuf[copyCount]= 0;
243     }
244
245     return dirNameBuf;
246 }
247
248
249 /*
250  * Truncates the open file to length bytes.
251  *
252  * ftruncate() is not defined in POSIX, so the header files don't define
253  * it when _POSIX_SOURCE is not defined. We're going to use it, anyway,
254  * and this prototype is identical in the header files for all the
255  * platforms (Sun/HP/IBM).
256  */
257 #ifdef __cplusplus
258 extern "C" {
259 #endif
260     extern ftruncate(int filedes, off_t length);
261 #ifdef __cplusplus
262 } // extern "C"
263 #endif
264 int
265 util_fdtruncate(int filedes, off_t length)
266 {
267     return ftruncate(filedes, length);
268 }
269
270
271 BOOL
272 util_directory_exists(STRING dir_name)
273 {
274     BOOL        exists = FALSE;
275     struct stat dir_info;
276
277     if (stat(dir_name, &dir_info) != 0)
278     {
279         goto epilogue;
280     }
281     if (S_ISDIR(dir_info.st_mode))
282     {
283         exists = TRUE;
284     }
285
286 epilogue:
287     return exists;
288 }
289
290
291 BOOL
292 util_paths_are_same_file(STRING path1, STRING path2)
293 {
294     BOOL        same_file = FALSE;
295     struct stat file_info1;
296     struct stat file_info2;
297
298     if (stat(path1, &file_info1) != 0)
299     {
300         goto epilogue;
301     }
302     if (stat(path2, &file_info2) != 0)
303     {
304         goto epilogue;
305     }
306
307     same_file = (   (file_info1.st_dev == file_info2.st_dev)
308                  && (file_info1.st_ino == file_info2.st_ino) );
309
310 epilogue:
311     return same_file;
312 }
313
314
315 /*
316  * Converts the path to a relative path from from_dir.  
317  *
318  * for path and from_dir, NULL, "", or "."  = current directory
319  *
320  * Returns "." if path and from_dir reference the same directory
321  */
322 int
323 util_cvt_path_to_relative(
324                 STRING  path_in,
325                 STRING  from_dir,
326                 char    *buf,
327                 int     buf_size
328 )
329 {
330 #define path_is_dot(arg_path) \
331             (util_strempty(arg_path) || util_streq(arg_path, "."))
332         
333     int         return_value = 0;
334     BOOL        found_relative= FALSE;
335     STRING      path = NULL;
336     BOOL        more_path = TRUE;
337     char        cwd[MAXPATHLEN]= "";
338     STRING      from= NULL;
339     char        *rightmost_slash= NULL;
340     char        *last_rightmost_slash= NULL;
341
342     if (   (path_is_dot(from_dir) && path_is_dot(path_in))
343         || (util_streq(from_dir, path_in))
344        )
345     {
346         /* the strings are equivalent! */
347         strcpy(buf, ".");
348         return return_value;
349     }
350
351     /*
352      * Determine "from" dir.
353      */
354     if (path_is_dot(from_dir))
355     {
356         from = ".";
357     }
358     else
359     {
360         from = from_dir;
361     }
362
363     /*
364      * Determine the directory we are trying to convert.
365      */
366     if (path_is_dot(path_in))
367     {
368         if (getcwd(cwd, MAXPATHLEN) == NULL)
369         {
370             return_value = ERR;
371             goto epilogue;
372         }
373         path = cwd;
374     }
375     else
376     {
377         path = path_in;
378     }
379
380     if (!util_directory_exists(from))
381     {
382         return_value = ERR;
383         goto epilogue;
384     }
385
386     rightmost_slash = NULL;
387     last_rightmost_slash = NULL;
388     more_path = TRUE;
389     while ((!found_relative) && more_path)
390     {
391         if (util_paths_are_same_file(from, path))
392         {
393             found_relative = TRUE;
394             break;
395         }
396
397         /*
398          * Get the name of the next dir up
399          */
400         rightmost_slash= strrchr(path, '/');
401         if (last_rightmost_slash != NULL)
402         {
403             *last_rightmost_slash = '/';
404         }
405         last_rightmost_slash= rightmost_slash;
406
407         /*
408          * Put in a NULL, so that util_paths_are_same_file is happy
409          */
410         if (rightmost_slash == NULL)
411         {
412             more_path = FALSE;
413         }
414         else
415         {
416             (*rightmost_slash) = 0;
417         }
418     }
419
420     if (rightmost_slash != NULL)
421     {
422         *rightmost_slash = '/';
423     }
424
425     if (found_relative)
426     {
427         if (rightmost_slash == NULL)
428         {
429             /* they are the same damn file (directory)! */
430             strncpy(buf, ".", buf_size);
431             buf[buf_size-1]= 0;
432         }
433         else
434         {
435             while ((*rightmost_slash == '/') && (*rightmost_slash != NULL))
436             {
437                 ++rightmost_slash;
438             }
439             strncpy(buf, rightmost_slash, buf_size);
440             buf[buf_size-1]= 0;
441         }
442     }
443     else
444     {
445         strncpy(buf, path, buf_size);
446         buf[buf_size-1]= 0;
447     }
448
449 epilogue:
450     return return_value;
451 #undef path_is_dot
452 }
453
454 /* This routine will create a directory hierarchy in the
455  * current working directory if the hierarchy does not 
456  * already exist.  If mkdir fails, errno will be set and
457  * a negative value will be returned.
458  */  
459 int     
460 util_mkdir_hier(
461     STRING path 
462
463 {
464     STRING      slash_ptr = NULL;
465     int         ret = 0;        
466
467     /* As an example: path -> "x/y/z", so slash_ptr points
468      * to the same string.
469      */
470     slash_ptr = path;
471     while (slash_ptr != NULL)
472     {
473         /* 1) x/y/z     2) x/y/z        3) x/y/z
474          *     ^              ^                ^        
475          *     |              |                |
476          *  slash_ptr      slash_ptr    slash_ptr == NULL
477          */
478         slash_ptr = strchr(slash_ptr, '/');
479
480         /* 1) x NULL y/z  2) x/y NULL z
481          *       ^                ^
482          *       |                |
483          *    slash_ptr         slash_ptr
484          */ 
485         if (slash_ptr != NULL) 
486         {
487            *slash_ptr = 0; 
488         }
489
490         /* Make the directory named: 
491          *      1) "x"    2) "x/y"    3) "x/y/z"
492          * in the cwd 
493          */
494         ret = mkdir(path, 0777);
495
496         /* If there was an error return -1 */
497         if ( ret != 0 && errno != EEXIST )
498         {
499             return ret;
500         }
501         
502         if ( slash_ptr != NULL )
503         {
504             /* 1) x/y/z      2) x/y/z      3) slash_ptr == NULL
505              *     ^               ^
506              *     |               |
507              *   slash_ptr      slash_ptr
508              */  
509             *slash_ptr = '/';
510
511             /* If there are multiple slashes, skip them. 
512              * 1) x/y/z       2) x/y/z
513              *      ^                ^             
514              *      |                | 
515              *   slash_ptr      slash_ptr
516              */   
517             while (*slash_ptr == '/')
518             {
519                 ++slash_ptr;
520             }
521         }
522     }
523         return 0;
524 }       
525
526 BOOL
527 util_path_is_absolute(
528     STRING      dir
529 )
530 {
531     BOOL        val = FALSE;
532
533     if ( dir[0] == '/' )
534         val = TRUE;
535
536     return val;
537 }
538
539 /* This routine is passed in an absolute path name (from the file
540  * chooser) and derives the module or project name for the ABObj
541  * struct. The obj name is passed back in the objname parameter.
542  * This routine assumes that objname already has allocated space.
543  */
544 int
545 util_derive_name_from_path(
546     char    *fullpath,
547     char    *objname
548 )
549 {
550     char        *filename, *name;
551     int         len = 0;
552
553     if ( util_file_name_has_ab_extension(fullpath) )
554     {
555         /*
556          * Check return value of strrchr before adding 1 to it
557          */
558         if (filename = strrchr(fullpath, '/'))
559             name = (STRING)strdup(filename + 1);        
560         else
561             name = (STRING)strdup(fullpath);
562  
563         len = strlen(name) - (AB_EXT_LENGTH + 1);
564         strncpy(objname, name, len);
565         objname[len] = '\0';
566     }
567     else
568     {
569         /*
570          * Check return value of strrchr before adding 1 to it
571          */
572         if (filename = strrchr(fullpath, '/'))
573             strcpy(objname, filename + 1);
574         else
575             strcpy(objname, fullpath);
576     }
577     return 0;
578 }
579
580 /* This routine is passed in a name (from the project or module 
581  * name dialog).  It checks the name for the ".bil" or ".bip"
582  * extension and strips it off if the name has it.  The project 
583  * or module name is passed back in the new_name parameter. This 
584  * routine assumes that new_name already has allocated space.
585  */
586 int
587 util_check_name(
588     STRING      name,
589     STRING      new_name
590 )
591 {
592     int         len = 0;
593     
594     if ( util_file_name_has_ab_extension(name) )
595     {
596         len = strlen(name) - (AB_EXT_LENGTH + 1);
597         strncpy(new_name, name, len);
598         new_name[len] = '\0'; 
599     }
600     else
601     {
602         strcpy(new_name, name);
603     }
604     return 0;
605 }
606
607
608 BOOL
609 util_file_is_regular_file(STRING filename)
610 {
611     BOOL        IsRegFile = FALSE;
612     struct stat file_info;
613
614     if (stat(filename, &file_info) != 0)
615     {
616         goto epilogue;
617     }
618     if (S_ISREG(file_info.st_mode))
619     {
620         IsRegFile = TRUE;
621     }
622
623 epilogue:
624     return IsRegFile;
625 }
626
627 BOOL
628 util_file_is_directory(STRING filename)
629 {
630     BOOL        IsDir = FALSE;
631     struct stat file_info;
632
633     if (stat(filename, &file_info) != 0)
634     {
635         goto epilogue;
636     }
637     if (S_ISDIR(file_info.st_mode))
638     {
639         IsDir = TRUE;
640     }
641
642 epilogue:
643     return IsDir;
644 }
645
646
647 /*
648  * from fopen() man page: legal types are:
649  *
650  *      r, rb, w, wb, a, ab, 
651  *      r+, r+b, rb+, w+, w+b, wb+, a+, a+b, ab+
652  *
653  * The 'b' option is ignored.
654  *
655  */
656 FILE *
657 util_fopen_locked(const char *filename, const char *accessType)
658 {
659     FILE        *file = NULL;
660     char        char1 = accessType[0];
661     BOOL        charPlus = 
662                     (   (accessType[1] != 0) 
663                      && ((accessType[1] == '+') || (accessType[2] == '+')));
664     BOOL        truncateFile = FALSE;
665     int         lockType = -1;
666
667     /*
668      * Open the file
669      * If a truncated open, open the existing file, first. That way, we
670      * can get a write lock before actually doing the truncate.
671      */
672     switch (accessType[0])
673     {
674         case 'r':
675             file = fopen(filename, accessType);
676             if (charPlus)
677             {
678                 lockType = F_WRLCK;
679             }
680             else
681             {
682                 lockType = F_RDLCK;
683             }
684         break;
685
686         case 'w':
687             errno = 0;
688             file = fopen(filename, "r+");       /* use existing, first! */
689             if ((file == NULL) && (errno == ENOENT))
690             {
691                 file = fopen(filename, accessType);
692             }
693             lockType = F_WRLCK;
694             truncateFile = TRUE;
695         break;
696
697         case 'a':
698             file = fopen(filename, accessType);
699             lockType = F_WRLCK;
700         break;
701
702         default:
703             errno = 0;          /* file is NULL */
704         break;
705     }
706
707     /*
708      * Get the appropriate lock on the file.
709      * Truncate the file, if necessary.
710      */
711     if ((file != NULL) && (lockType != -1))
712     {
713         if (   (util_flock(file, TRUE, lockType, 0, 0) >= 0)
714             && truncateFile)
715         {
716             util_ftruncate(file, 0, accessType);
717         }
718     }
719
720     return file;
721 }
722
723
724 int
725 util_flock(FILE *file, BOOL wait, int lockType, off_t offset, off_t length)
726 {
727     struct flock        lock;
728     int                 fcntlParam = (wait? F_SETLKW:F_SETLK);
729
730     lock.l_type = lockType;
731     lock.l_whence = SEEK_SET;
732     lock.l_start = offset;
733     lock.l_len = length;
734     lock.l_pid = (pid_t)-1;
735
736 #ifdef DEBUG
737     if (debugging() && wait)
738     {
739         if (fcntl(fileno(file), F_SETLK, (void*)&lock) == 0)
740         {
741             /* got the lock */
742             return 0;
743         }
744         else
745         {
746             /* didn't get the lock - we're going to block */
747             util_dprintf(1, "Waiting for lock [%s]...\n",
748                 (fcntlParam == F_RDLCK? "READ":
749                     (fcntlParam == F_WRLCK? "WRITE":
750                         "BAD TYPE"))
751             );
752         }
753     }
754 #endif /* DEBUG */
755
756     if (fcntl(fileno(file), fcntlParam, (void*)&lock) != 0)
757     {
758         return -1;
759     }
760
761     return 0;
762 }
763
764
765 int
766 util_funlock(FILE *file, off_t offset, off_t length)
767 {
768     struct flock        lock;
769
770     lock.l_type = F_UNLCK;
771     lock.l_whence = SEEK_SET;
772     lock.l_start = offset;
773     lock.l_len = length;
774     lock.l_pid = (pid_t)-1;
775
776     if (fcntl(fileno(file), F_SETLKW, (void*)&lock) != 0)
777     {
778         return -1;
779     }
780
781     return 0;
782 }
783
784
785 int
786 util_ftruncate(FILE *file, off_t length, const char *accessType)
787 {
788     int         fd = fileno(file);
789     off_t       offset = ftell(file);
790
791     util_fdtruncate(fd, length);
792
793     /*
794      *  Associate the stream with the file descriptor fildes.
795      */
796     fdopen(fd, accessType);
797
798     /*
799      * Perform a seek on the stream, just to make sure it's in sync.
800      */
801     if (offset > length)
802     {
803         fseek(file, 0, SEEK_END);
804     }
805     else
806     {
807         fseek(file, offset, SEEK_SET);
808     }
809
810     return 0;
811 }
812