Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtfile / fsDialog.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 /* $XConsortium: fsDialog.c /main/6 1996/10/08 11:58:08 mustafa $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           fsDialog.c
28  *
29  *   COMPONENT_NAME: Desktop File Manager (dtfile)
30  *
31  *   Description:    Containes routines to handle contents of dtfile.config
32  *
33  *   FUNCTIONS: configFileName
34  *              configFileOK
35  *              fsDialogAvailable
36  *              getFSType
37  *              readConfigFile
38  *              readDialogData
39  *              readFSID
40  *              readLine
41  *              stricmp
42  *              strip
43  *
44  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
45  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
46  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
47  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
48  *
49  ****************************************************************************
50  ************************************<+>*************************************/
51
52 /*---------------------------------------------------------------------------------
53  *
54  *   fsDialogAvailable (String path, dtFSData *fsDialogData)
55  *
56  *   For the file specified by path, determine if there is a properties
57  *      dialog specific to the file system that the file is on.
58  *      For example, if the file is in AFS there might be a dialog to edit
59  *      the Access Control List. fsDialogData is filled if there is a dialog.
60  *   path must not include host:
61  *
62  *   If any errors are encountered, FALSE is returned and no other action is taken
63  *      or message displayed.
64  *
65  *--------------------------------------------------------------------------------*/
66
67 #include <string.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <ctype.h>
71 #include <errno.h>
72 #include <sys/stat.h>
73
74 #include <Dt/UserMsg.h>   /* for DtSimpleError */
75
76 #include <Xm/Xm.h>
77 #include "Encaps.h"
78 #include "ModAttr.h"
79 #include "FileMgr.h"
80 #include "Desktop.h"
81 #include "Main.h"
82
83 /*-----------------------------------------------------
84  * Configuration file search order is:
85  *
86  *       $DTFSCONFIG   (meant to be used for debugging)
87  *       $HOME/CONFIGFILENAME
88  *       SYSCONFIGFILEDIR/CONFIGFILENAME
89  *       DEFCONFIGFILEDIR/$LANG/CONFIGFILENAME
90  *       DEFCONFIGFILEDIR/C/CONFIGFILENAME
91  *
92  * The function configFileName() determines the correct
93  *    name. It will apply $LANG where appropriate.
94  *
95  *-----------------------------------------------------*/
96
97 #define SYSCONFIGFILEDIR  CDE_CONFIGURATION_TOP "/config"
98 #define DEFCONFIGFILEDIR  CDE_INSTALLATION_TOP  "/config"
99 #define CONFIGFILENAME "dtfile.config"
100 #define DEFCONFIGFILENAME  CDE_INSTALLATION_TOP  "/config/C/dtfile.config"
101
102 /*--------------------------------------------------------
103      Platform-specific includes required for getFSType()
104  *--------------------------------------------------------*/
105
106 #ifdef __aix
107 #endif
108
109 #if defined(sun) || defined(__osf__)
110 #include <sys/types.h>
111 #include <sys/statvfs.h>
112 #endif
113
114 #ifdef __hpux
115 #include <sys/vfs.h>
116 #endif
117
118 /*----------------------
119  *   global variables
120  *----------------------*/
121
122 #define EMSGMAX 1024
123 char g_errorMessage[EMSGMAX];
124
125
126 /*--------------------
127      Local functions
128  *--------------------*/
129
130 static void    getFSType      (const String      path,
131                                      String      fsType,
132                                      String      platform);
133 static void    readConfigFile (const String      fsType,
134                                const String      platform,
135                                      dtFSData  * fsDialogData,
136                                      Boolean   * dialogAvailable);
137 static String  configFileName (void);
138 static void    readFSID       (FILE          * fptr,
139                                const String    fsType,
140                                const String    platform,
141                                String          fsID);
142 static void    readDialogData (FILE          * fptr,
143                                const String    fsID,
144                                Boolean       * dialogAvailable,
145                                dtFSData      * fsDialogData);
146 static int     readLine        (FILE  * fptr, String line);
147 static int     stricmp         (const String  s1,  const String  s2);
148 static String  strip           (String  s);
149 static Boolean configFileOK(void);
150
151
152 /*----------------------
153  *   fsDialogAvailable
154  *----------------------*/
155
156 Boolean
157 fsDialogAvailable (const String path, dtFSData *fsDialogData)
158
159 {
160    char     fsType[MAXLINELENGTH], platform[MAXLINELENGTH];
161    Boolean  dialogAvailable;
162
163    DPRINTF(("fsDialogAvailble: checking path \"%s\"\n",path));
164    if ( ! configFileOK())
165       return FALSE;
166
167    getFSType(path, fsType, platform);
168    DPRINTF(("   fsType=\"%s\"\n   platform=\"%s\"\n",fsType,platform));
169
170    if (strlen(fsType) == 0)
171       return FALSE;
172    else
173    {
174       readConfigFile(fsType, platform, fsDialogData, &dialogAvailable);
175       if (dialogAvailable)
176       {
177          strcpy (fsDialogData->path,path);
178       }
179       return dialogAvailable;
180    }
181
182 }
183
184
185 /*--------------------------------------------------------------------------------
186  *
187  *   configFileOK
188  *
189  *   is the configuration file accessible?
190  *
191  *------------------------------------------------------------------------------*/
192
193 static Boolean
194 configFileOK(void)
195
196 {
197    struct stat  buf;
198    int          err;
199    String       fname;
200    String       msg1, msg2;
201
202    fname = configFileName();
203    err   = stat(fname, &buf);
204    DPRINTF(("   config file \"%s\"  stat ret=%i\n",fname,err));
205
206    if (err == 0)
207       return TRUE;
208    else
209    {
210       msg1 = GETMESSAGE(21, 22, "Cannot open file manager configuration file: ");
211       msg2 = strerror(errno);
212       sprintf(g_errorMessage,"%s%s\n   %s\n",msg1,fname,msg2);
213       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
214       return FALSE;
215    }
216
217 }  /* end configFileOK */
218
219
220 /*--------------------------------------------------------------------------------
221  *
222  *   getFSType
223  *
224  *   Do the platform-specific work required to determine what type of file system
225  *   the file defined by path is on. Return strings defining the file system type
226  *   and the platform. Any errors result in a fsType set to the null string.
227  *
228  *   Both fsType and platform are allocated by the caller
229  *
230  *   The strings used in platform and fsType must match those used in
231  *      the configuration file.
232  *
233  *--------------------------------------------------------------------------------*/
234
235 static void getFSType(const String  path,
236                             String  fsType,
237                             String  platform)
238
239 {
240 #ifdef __aix
241 #define GETFSTYPE
242    struct stat  buf;
243
244    strncpy(platform,"aix",MAXLINELENGTH);
245    if (lstat(path, &buf) == 0)
246       sprintf(fsType,"%i",buf.st_vfstype);
247    else
248       strncpy(fsType,"",MAXLINELENGTH);
249 #endif /* __aix */
250
251 #ifdef sun
252 #define GETFSTYPE
253    struct statvfs buf;
254
255    strncpy(platform,"sunos",MAXLINELENGTH);
256    if (statvfs(path, &buf) == 0)
257       strncpy(fsType,buf.f_basetype,MAXLINELENGTH);
258    else
259       strncpy(fsType,"",MAXLINELENGTH);
260 #endif /* sun */
261
262 #ifdef __hpux
263 #define GETFSTYPE
264    struct statfs buf;
265
266    strncpy(platform,"hpux",MAXLINELENGTH);
267    if (statfs(path, &buf) == 0)
268       sprintf(fsType,"%li",buf.f_fsid[1]);
269    else
270       strncpy(fsType,"",MAXLINELENGTH);
271 #endif /* __hpux */
272
273  #ifdef __osf__
274 #define GETFSTYPE
275    struct statvfs buf;
276
277    strncpy(platform,"decunix",MAXLINELENGTH);
278    if (statvfs(path, &buf) == 0)
279        strncpy(fsType,buf.f_basetype,MAXLINELENGTH);
280    else
281        strncpy(fsType,"",MAXLINELENGTH);
282 #endif /* DEC Unix */
283
284 #ifndef GETFSTYPE
285    strncpy(platform,"unknown",MAXLINELENGTH);
286    strncpy(fsType,  "",MAXLINELENGTH);
287 #endif  /* unknown platform */
288
289    return;
290
291 }    /* end getFSType */
292
293
294 /*--------------------------------------------------------------------------------
295  *
296  *   readConfigFile
297  *
298  *   Given the platform and type of file system, read the configuration file to
299  *   determine if there is a file-system specific dialog. If there is, fill
300  *   fsDialogData. If there is no file or if the file has no entry for
301  *   platform:fsType, dialogAvailable is set to FALSE.
302  *
303  *   The configuration file consists of two parts. The first is a list of entries
304  *   (ending with "end") of the form
305  *
306  *   platform:file-system-type=file-system-id
307  *
308  *   where platform is one of the strings from getFSType(), file-system-type is
309  *   a (platform-dependent) string identifying the type of file system, and
310  *   file-system-id is a string used in the configuration file to identify the
311  *   file system. The second part of the file consists of lists of attributes
312  *   (in the form name = value, one per line) for each of the file system id's.
313  *   Blank lines and lines beginning with * are ignored as is case. A example
314  *   is given below.
315  *
316  *   -------------------------------------------------
317  *   * sample dtfs configuration file
318  *
319  *   aix:4=afs
320  *   hpux:8=afs
321  *   sunos:nfs=nfs
322  *   end
323  *
324  *   afs:     buttonlabel = Change AFS ACL ...
325  *            fsDialog    = modAttrAFS
326  *            warning     = File system access may be further restriced by AFS ACLs
327  *
328  *   nfs:     buttonLabel = Change NFS attributes ...
329  *            fsDialog    = <configuration-location>/bin/modNFSAttr
330  *   -------------------------------------------------
331  *
332  *--------------------------------------------------------------------------------*/
333
334 static void
335 readConfigFile(const String      fsType,
336                const String      platform,
337                      dtFSData  * fsDialogData,
338                      Boolean   * dialogAvailable)
339 {
340    char      fsID[MAXLINELENGTH];
341    FILE    * fptr;
342    String    fname, msg1, msg2;
343
344
345    *dialogAvailable = FALSE;
346
347    fname = configFileName();
348    fptr = fopen(fname,"r");
349    if (fptr == NULL)
350    {
351       msg1 = GETMESSAGE(21, 22, "Cannot open file manager configuration file: ");
352       msg2 = strerror(errno);
353       sprintf(g_errorMessage,"%s%s\n   %s\n",msg1,fname,msg2);
354       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
355       return;
356    }
357
358    readFSID(fptr, fsType, platform, fsID);
359    DPRINTF(("   fsID=\"%s\"\n",fsID));
360
361    readDialogData(fptr, fsID, dialogAvailable, fsDialogData);
362
363    /* make sure that a dialog program has been specified or execl will do unfortunate things */
364    if (strlen(fsDialogData->fsDialogProgram) == 0)
365    {
366       *dialogAvailable = FALSE;
367       if (strlen(fsDialogData->buttonLabel) != 0)
368       {
369          msg1 = XtNewString(GETMESSAGE(21, 29, "No value was provided for the fsDialog field in dtfile's configuration file"));
370          _DtSimpleError (application_name, DtError, NULL, msg1, NULL);
371          XtFree(msg1);
372       }
373    }
374
375    fclose(fptr);
376    return;
377
378 }  /* end readConfigFile */
379
380
381 /*--------------------------------------------------------------------------------
382  *
383  *   configFileName
384  *
385  *   return the name of the configuration file which defines file-system
386  *   specific dialogs ... a hierarchy of names is searched until a file
387  *   is found ... no checking is done to see if it can be read
388  *
389  *------------------------------------------------------------------------------*/
390
391 static String
392 configFileName(void)
393
394 {
395           int          i, err;
396           struct stat  buf;
397           String       s;
398    static char         fn[MAX_PATH]="";   /* remember name once its been determined */
399
400
401    if (strlen(fn) != 0) return fn;   /* has filename been determined already? */
402
403    for (i=0; i < 4; i++)
404    {
405       switch (i)
406       {
407          case 0: /* file name $DTFSCONFIG */
408             if ( (s=getenv("DTFSCONFIG")) != NULL)        /* a convenience for debugging */
409                strncpy(fn,s,MAX_PATH);
410             else  /* DTFSCONFIG not defined */
411                continue;
412             break;
413
414          case 1: /* $HOME/dtfile.config */
415             if ( (s=getenv("HOME")) != NULL)
416             {
417                strncpy(fn,s,MAX_PATH);
418                strcat (fn,"/");
419                strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
420             }
421             else  /* $HOME not defined */
422                continue;
423             break;
424
425          case 2: /* SYSCONFIGFILEDIR, e.g. /etc/dt/config/dtfile.config */
426             strncpy(fn,SYSCONFIGFILEDIR,MAX_PATH);
427             strcat (fn,"/");
428             strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
429             break;
430
431          case 3: /* DEFCONFIGFILEDIR, e.g. /usr/dt/config/C/dtfile.config */
432             strncpy(fn,DEFCONFIGFILEDIR,MAX_PATH);
433             strcat (fn,"/");
434             if ( (s=getenv("LANG")) != NULL)
435             {
436                strncat(fn,s,MAX_PATH-strlen(fn)-1);
437                strcat (fn,"/");
438             }
439             else
440             {
441                strncat(fn,"C",MAX_PATH-strlen(fn)-1);
442                strcat (fn,"/");
443             }
444             strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
445             break;
446       }  /* end switch */
447
448       err   = stat(fn, &buf);
449       DPRINTF(("   config file \"%s\"  stat ret=%i\n",fn,err));
450
451       if (err == 0)  /* file is found */
452          return fn;
453
454    }  /* end loop over possible config files */
455
456    /* didn't find any of the files (this should never, ever happen) */
457    /* return the name of the factory default (case 3 above) which should always be there */
458
459    strncpy(fn,DEFCONFIGFILENAME,MAX_PATH);
460    return fn;
461
462
463 }  /* end configFileName */
464
465
466 /*--------------------------------------------------------------------------------
467  *
468  *   readFSID
469  *
470  *   get the file-system identifier from the configuration file
471  *
472  *   fptr refers to an open configuration file
473  *   fsType and platform identify the platfrom (aix, hpux, sunos, etc)
474  *      and file-system type (afs, nfs, dfs, etc.)
475  *   fsID is filled by matching platform:fsType with with information in the
476  *      file and reading the id; the string comparisons are case insensitive
477  *      if there is more than one match, the last fsID found will be returned;
478  *      if no match is found, fsID is returned as ""
479  *   upon conclusion, the file is positioned past the "end" record
480  *
481  *------------------------------------------------------------------------------*/
482
483 static void
484 readFSID (      FILE    * fptr,
485           const String    fsType,
486           const String    platform,
487                 String    fsID)
488
489 {
490    char    line[MAXLINELENGTH];
491    String  pform, type, id;
492    int     lineLength;
493    String  lineIn, lineOut;
494
495
496    fsID[0] = '\0';
497
498    while ( (lineLength=readLine(fptr,line)) != EOF &&
499             stricmp(line,"end")             != 0 )
500    {
501       /* readLine has changed all white space to blanks ... now remove
502          the blanks as they are not significant here */
503       for (lineIn = lineOut = line; *lineIn != '\0'; lineIn++)
504          if (*lineIn != ' ')
505          {
506             *lineOut = *lineIn;
507             lineOut++;
508          }
509       *lineOut = '\0';
510
511       pform = strtok(line,":");
512       type  = strtok(NULL,"=");
513       id    = strtok(NULL," ");
514       if (stricmp(pform,platform)==0 && stricmp(type,fsType)==0)
515          strncpy(fsID,id,MAXLINELENGTH);
516    }
517
518    return;
519
520 }  /* end readFSID */
521
522
523 /*--------------------------------------------------------------------------------
524  *
525  *   readDialogData
526  *
527  *   fill dtFSData with information from the configuration file; set
528  *      dialogAvailable
529  *
530  *   fsID is a file-system identifier defined, in the configuration file,
531  *      by a platform:file-system-type pair. The configuration file is
532  *      searched from its current position until a definition section
533  *      for fsID is found. The string comparison is case insensitive; if more
534  *      than one match is found, the last in the file is used. The fields in
535  *      dtFSData are initialized (e.g. string fields are set to "") if no
536  *      matches are found or if no match is found for a particular field.
537  *   Upon conclusion, the file is positioned at EOF.
538  *
539  *------------------------------------------------------------------------------*/
540
541 static void
542 readDialogData(      FILE      * fptr,
543                const String      fsID,
544                      Boolean   * dialogAvailable,
545                      dtFSData  * fsDialogData)
546
547
548 {
549    int     lineLength;
550    String  s, id, token1, token2;
551    String  msg1;
552    char    line[MAXLINELENGTH];
553
554
555    *dialogAvailable = FALSE;
556
557    /* initialize fields in fsDialogData */
558    strcpy(fsDialogData->buttonLabel,"");
559    strcpy(fsDialogData->fsDialogProgram,"");
560    strcpy(fsDialogData->warningMessage,"");
561    fsDialogData->dismissStdPermissionDialog = FALSE;
562
563    if (strlen(fsID) == 0)
564       return;
565
566    /* loop over lines in the file; note that readLine is called
567       from within the loop except for the first pass */
568    for (lineLength=readLine(fptr,line); lineLength != EOF; )
569    {
570       id = strtok(line,":");
571       if (stricmp(id,fsID) == 0)
572       {
573          /* a section matching the input fsID has been found */
574          *dialogAvailable = TRUE;
575          token1 = strtok(NULL," =");
576          s = strchr(token1,'\0') + 1;   /* first character after token1 */
577          token2 = &s[strspn(s," =")];   /* first non-blank, non-= after token1 */
578          /* loop to get data for fields in fsDialogData */
579          while(strchr(token1,':') == NULL  && lineLength != EOF)
580          {
581             if (stricmp(token1,"buttonlabel") == 0)
582                strncpy(fsDialogData->buttonLabel,token2,MAXLINELENGTH);
583             else if (stricmp(token1,"warning") == 0)
584             {
585                strncpy(fsDialogData->warningMessage,token2,MAXLINELENGTH);
586             }
587             else if (stricmp(token1,"fsdialog") == 0)
588                strncpy(fsDialogData->fsDialogProgram,token2,MAXLINELENGTH);
589             else if (stricmp(token1,"dismiss") == 0)
590                fsDialogData->dismissStdPermissionDialog = (stricmp(token2,"yes") == 0);
591             else
592             {
593                msg1 = GETMESSAGE(21, 24, "Unknown field label in file manager configuration file: ");
594                sprintf(g_errorMessage,"%s\"%s\"\n",msg1,token1);
595                _DtSimpleError (application_name, DtWarning, NULL, g_errorMessage, NULL);
596             }
597             lineLength = readLine(fptr,line);
598             if (lineLength != EOF)
599             {
600                token1 = strtok(line," =");
601                s = strchr(token1,'\0') + 1;
602                token2 = &s[strspn(s," =")];
603             }
604          } /* end while */
605       }  /* end if */
606       else
607       {
608          /* the current line does not match fsID ... get next line */
609          lineLength = readLine(fptr,line);
610       }
611    }  /* end for */
612
613    if ( ! *dialogAvailable )
614    {
615       msg1 = GETMESSAGE(21, 25, "No information found in file manager configuration file for file-system identifier");
616       sprintf(g_errorMessage,"%s \"%s\"\n",msg1,fsID);
617       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
618    }
619
620 }  /* end readDialogData */
621
622
623 /*--------------------------------------------------------------------------------
624  *
625  *   readLine
626  *
627  *   read a line from the stream pointed to by fptr and return it in line
628  *   line is allocated by the caller
629  *
630  *   blank lines and lines starting with * are skipped
631  *   leading and trailing white space is removed
632  *   imbedded white space characters are changed to blanks
633  *   imbedded "\n" is changed to space, newline
634  *   the length of the line or EOF (on end-of-file or error) is returned
635  *
636  *------------------------------------------------------------------------------*/
637
638 static int
639 readLine(FILE  * fptr, String  line)
640
641 {
642    const char    commentChar = '#';
643          char    myLine[MAXLINELENGTH];
644          String  s, t;
645          int     i, len;
646
647    while (TRUE)
648    {
649       if ( fgets(myLine,MAXLINELENGTH,fptr) == NULL)
650          return EOF;
651       else
652       {
653          s = strip(myLine);  /* remove leading & trailing whitespace */
654          if ((len=strlen(s)) != 0  &&  s[0] != commentChar)
655          {
656             /* change imbedded white space characters to spaces */
657             for (i=0; i<len; i++)
658                if (isspace(s[i]))
659                   s[i] = ' ';
660
661             /* change any imbedded "\n" to space followed by newline */
662             while ( (t=strstr(s,"\\n")) != NULL )
663             {
664                *t     = ' ';      /* space */
665                *(t+1) = '\n';     /* new line */
666             }
667
668             strncpy(line,s,MAXLINELENGTH);
669             return len;
670          }
671       }
672    }
673
674 }  /* end readLine */
675
676
677 /*--------------------------------------------------------------------------------
678  *
679  *  stricmp
680  *
681  *  compare strings ignoring case, return value as in strcmp
682  *
683  *------------------------------------------------------------------------------*/
684
685 static int
686 stricmp(const String  s1, const String  s2)
687
688 {
689    int i;
690
691    for (i=0; tolower(s1[i]) == tolower(s2[i]); i++)
692       if (s1[i] == '\0')
693          return 0;
694
695    return (tolower(s1[i]) > tolower(s2[i])) ? +1 : -1;
696
697 }  /* end stricmp */
698
699
700 /*--------------------------------------------------------------------------------
701  *
702  *   strip
703  *
704  *   remove trailing white space by inserting \0
705  *   return a pointer to the first non-white space character
706  *------------------------------------------------------------------------------*/
707
708 static String
709 strip(String  s)
710
711 {
712    int i;
713
714    for (i=(strlen(s)-1); isspace(s[i]) && i>=0; i--)
715       ;
716    s[i+1] = '\0';
717
718    for (i=0; isspace(s[i]); i++)
719       ;
720
721    return &s[i];
722
723 }  /* end strip */
724
725
726 /*----------------
727  *   for testing
728  *----------------*/
729
730  /*
731 main(int argc, char **argv)
732 {
733     char        pwd[100];
734     int         err = 0;
735     Boolean     test;
736     dtFSData    data;
737
738     if(argc > 1)
739     {
740         strcpy(pwd, argv[1]);
741     }
742     else
743     {
744         getcwd(pwd,sizeof(pwd));
745
746     }
747     printf("testing file \"%s\"\n",pwd);
748     test = fsDialogAvailable(pwd, &data);
749     if (test)
750     {
751         printf("dialog is available\n");
752         printf("   buttonLabel=\"%s\"\n",data.buttonLabel);
753         printf("   fsDialogProgram=\"%s\"\n",data.fsDialogProgram);
754     }
755     else
756     {
757         printf("dialog not available\n");
758     }
759 }
760 */
761