some minor modifications to loadparm.c to support the necessary
[kai/samba.git] / source3 / param / loadparm.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Parameter loading functions
5    Copyright (C) Karl Auer 1993,1997
6
7    Largely re-written by Andrew Tridgell, September 1994
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /*
25  *  Load parameters.
26  *
27  *  This module provides suitable callback functions for the params
28  *  module. It builds the internal table of service details which is
29  *  then used by the rest of the server.
30  *
31  * To add a parameter:
32  *
33  * 1) add it to the global or service structure definition
34  * 2) add it to the parm_table
35  * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
36  * 4) If it's a global then initialise it in init_globals. If a local
37  *    (ie. service) parameter then initialise it in the sDefault structure
38  *  
39  *
40  * Notes:
41  *   The configuration file is processed sequentially for speed. It is NOT
42  *   accessed randomly as happens in 'real' Windows. For this reason, there
43  *   is a fair bit of sequence-dependent code here - ie., code which assumes
44  *   that certain things happen before others. In particular, the code which
45  *   happens at the boundary between sections is delicately poised, so be
46  *   careful!
47  *
48  */
49
50 #include "includes.h"
51
52 BOOL bLoaded = False;
53
54 extern int DEBUGLEVEL;
55 extern pstring user_socket_options;
56 extern pstring myname;
57
58 #ifndef GLOBAL_NAME
59 #define GLOBAL_NAME "global"
60 #endif
61
62 #ifndef PRINTCAP_NAME
63 #ifdef AIX
64 #define PRINTCAP_NAME "/etc/qconfig"
65 #else
66 #define PRINTCAP_NAME "/etc/printcap"
67 #endif
68 #endif
69
70 #ifndef PRINTERS_NAME
71 #define PRINTERS_NAME "printers"
72 #endif
73
74 #ifndef HOMES_NAME
75 #define HOMES_NAME "homes"
76 #endif
77
78 /* some helpful bits */
79 #define pSERVICE(i) ServicePtrs[i]
80 #define iSERVICE(i) (*pSERVICE(i))
81 #define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices) && iSERVICE(iService).valid)
82 #define VALID(i) iSERVICE(i).valid
83
84 /* these are the types of parameter we have */
85 typedef enum
86 {
87   P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,
88   P_STRING,P_USTRING,P_GSTRING,P_UGSTRING
89 } parm_type;
90
91 typedef enum
92 {
93   P_LOCAL,P_GLOBAL,P_NONE
94 } parm_class;
95
96 int keepalive=0;
97 extern BOOL use_getwd_cache;
98
99 extern int extra_time_offset;
100 #ifdef KANJI
101 extern int coding_system;
102 #endif
103
104 /* 
105  * This structure describes global (ie., server-wide) parameters.
106  */
107 typedef struct
108 {
109   char *szPrintcapname;
110   char *szLockDir;
111   char *szRootdir;
112   char *szDefaultService;
113   char *szDfree;
114   char *szMsgCommand;
115   char *szHostsEquiv;
116   char *szServerString;
117   char *szAutoServices;
118   char *szPasswdProgram;
119   char *szPasswdChat;
120   char *szLogFile;
121   char *szConfigFile;
122   char *szSMBPasswdFile;
123   char *szPasswordServer;
124   char *szSocketOptions;
125   char *szValidChars;
126   char *szWorkGroup;
127   char *szDomainController;
128   char *szUsernameMap;
129   char *szCharacterSet;
130   char *szLogonScript;
131   char *szLogonPath;
132   char *szSmbrun;
133   char *szWINSserver;
134   char *szInterfaces;
135   char *szRemoteAnnounce;
136   char *szSocketAddress;
137   char *szNISHomeMapName;
138   char *szAnnounceVersion; /* This is initialised in init_globals */
139   char *szNetbiosAliases;
140   int max_log_size;
141   int mangled_stack;
142   int max_xmit;
143   int max_mux;
144   int max_packet;
145   int pwordlevel;
146   int deadtime;
147   int maxprotocol;
148   int security;
149   int printing;
150   int maxdisksize;
151   int lpqcachetime;
152   int syslog;
153   int os_level;
154   int max_ttl;
155   int ReadSize;
156   int shmem_size;
157   int shmem_hash_size;
158   int client_code_page;
159   int announce_as;   /* This is initialised in init_globals */
160   BOOL bDNSproxy;
161   BOOL bWINSsupport;
162   BOOL bWINSproxy;
163   BOOL bLocalMaster;
164   BOOL bPreferredMaster;
165   BOOL bDomainMaster;
166   BOOL bDomainLogons;
167   BOOL bEncryptPasswords;
168   BOOL bStripDot;
169   BOOL bNullPasswords;
170   BOOL bLoadPrinters;
171   BOOL bUseRhosts;
172   BOOL bReadRaw;
173   BOOL bWriteRaw;
174   BOOL bReadPrediction;
175   BOOL bReadbmpx;
176   BOOL bSyslogOnly;
177   BOOL bBrowseList;
178   BOOL bUnixRealname;
179   BOOL bNISHomeMap;
180   BOOL bTimeServer;
181 } global;
182
183 static global Globals;
184
185
186
187 /* 
188  * This structure describes a single service. 
189  */
190 typedef struct
191 {
192   BOOL valid;
193   char *szService;
194   char *szPath;
195   char *szUsername;
196   char *szGuestaccount;
197   char *szInvalidUsers;
198   char *szValidUsers;
199   char *szAdminUsers;
200   char *szCopy;
201   char *szInclude;
202   char *szPreExec;
203   char *szPostExec;
204   char *szRootPreExec;
205   char *szRootPostExec;
206   char *szPrintcommand;
207   char *szLpqcommand;
208   char *szLprmcommand;
209   char *szLppausecommand;
210   char *szLpresumecommand;
211   char *szPrintername;
212   char *szPrinterDriver;
213   char *szDontdescend;
214   char *szHostsallow;
215   char *szHostsdeny;
216   char *szMagicScript;
217   char *szMagicOutput;
218   char *szMangledMap;
219   char *szVetoFiles;
220   char *szHideFiles;
221   char *comment;
222   char *force_user;
223   char *force_group;
224   char *readlist;
225   char *writelist;
226   char *volume;
227   int  iMinPrintSpace;
228   int  iCreate_mask;
229   int  iCreate_force_mode;
230   int  iDir_mask;
231   int  iDir_force_mode;
232   int  iMaxConnections;
233   int  iDefaultCase;
234   BOOL bAlternatePerm;
235   BOOL bRevalidate;
236   BOOL bCaseSensitive;
237   BOOL bCasePreserve;
238   BOOL bShortCasePreserve;
239   BOOL bCaseMangle;
240   BOOL status;
241   BOOL bHideDotFiles;
242   BOOL bBrowseable;
243   BOOL bAvailable;
244   BOOL bRead_only;
245   BOOL bNo_set_dir;
246   BOOL bGuest_only;
247   BOOL bGuest_ok;
248   BOOL bPrint_ok;
249   BOOL bPostscript;
250   BOOL bMap_system;
251   BOOL bMap_hidden;
252   BOOL bMap_archive;
253   BOOL bLocking;
254   BOOL bStrictLocking;
255   BOOL bShareModes;
256   BOOL bOnlyUser;
257   BOOL bMangledNames;
258   BOOL bWidelinks;
259   BOOL bSymlinks;
260   BOOL bSyncAlways;
261   char magic_char;
262   BOOL *copymap;
263   BOOL bDeleteReadonly;
264   BOOL bFakeOplocks;
265   char dummy[3]; /* for alignment */
266 } service;
267
268
269 /* This is a default service used to prime a services structure */
270 static service sDefault = 
271 {
272   True,   /* valid */
273   NULL,    /* szService */
274   NULL,    /* szPath */
275   NULL,    /* szUsername */
276   NULL,    /* szGuestAccount  - this is set in init_globals() */
277   NULL,    /* szInvalidUsers */
278   NULL,    /* szValidUsers */
279   NULL,    /* szAdminUsers */
280   NULL,    /* szCopy */
281   NULL,    /* szInclude */
282   NULL,    /* szPreExec */
283   NULL,    /* szPostExec */
284   NULL,    /* szRootPreExec */
285   NULL,    /* szRootPostExec */
286   NULL,    /* szPrintcommand */
287   NULL,    /* szLpqcommand */
288   NULL,    /* szLprmcommand */
289   NULL,    /* szLppausecommand */
290   NULL,    /* szLpresumecommand */
291   NULL,    /* szPrintername */
292   NULL,    /* szPrinterDriver - this is set in init_globals() */
293   NULL,    /* szDontdescend */
294   NULL,    /* szHostsallow */
295   NULL,    /* szHostsdeny */
296   NULL,    /* szMagicScript */
297   NULL,    /* szMagicOutput */
298   NULL,    /* szMangledMap */
299   NULL,    /* szVetoFiles */
300   NULL,    /* szHideFiles */
301   NULL,    /* comment */
302   NULL,    /* force user */
303   NULL,    /* force group */
304   NULL,    /* readlist */
305   NULL,    /* writelist */
306   NULL,    /* volume */
307   0,       /* iMinPrintSpace */
308   0744,    /* iCreate_mask */
309   0000,    /* iCreate_force_mode */
310   0755,    /* iDir_mask */
311   0000,    /* iDir_force_mode */
312   0,       /* iMaxConnections */
313   CASE_LOWER, /* iDefaultCase */
314   False,   /* bAlternatePerm */
315   False,   /* revalidate */
316   False,   /* case sensitive */
317   False,   /* case preserve */
318   False,   /* short case preserve */
319   False,  /* case mangle */
320   True,  /* status */
321   True,  /* bHideDotFiles */
322   True,  /* bBrowseable */
323   True,  /* bAvailable */
324   True,  /* bRead_only */
325   True,  /* bNo_set_dir */
326   False, /* bGuest_only */
327   False, /* bGuest_ok */
328   False, /* bPrint_ok */
329   False, /* bPostscript */
330   False, /* bMap_system */
331   False, /* bMap_hidden */
332   True,  /* bMap_archive */
333   True,  /* bLocking */
334   False,  /* bStrictLocking */
335   True,  /* bShareModes */
336   False, /* bOnlyUser */
337   True,  /* bMangledNames */
338   True,  /* bWidelinks */
339   True,  /* bSymlinks */
340   False, /* bSyncAlways */
341   '~',   /* magic char */
342   NULL,  /* copymap */
343   False, /* bDeleteReadonly */
344   False, /* bFakeOplocks */
345   ""     /* dummy */
346 };
347
348
349
350 /* local variables */
351 static service **ServicePtrs = NULL;
352 static int iNumServices = 0;
353 static int iServiceIndex = 0;
354 static BOOL bInGlobalSection = True;
355 static BOOL bGlobalOnly = False;
356 static int default_server_announce;
357
358 #define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
359
360 /* prototypes for the special type handlers */
361 static BOOL handle_valid_chars(char *pszParmValue, char **ptr);
362 static BOOL handle_include(char *pszParmValue, char **ptr);
363 static BOOL handle_copy(char *pszParmValue, char **ptr);
364 static BOOL handle_protocol(char *pszParmValue,int *val);
365 static BOOL handle_security(char *pszParmValue,int *val);
366 static BOOL handle_case(char *pszParmValue,int *val);
367 static BOOL handle_printing(char *pszParmValue,int *val);
368 static BOOL handle_character_set(char *pszParmValue,int *val);
369 static BOOL handle_announce_as(char *pszParmValue, int *val);
370 #ifdef KANJI
371 static BOOL handle_coding_system(char *pszParmValue,int *val);
372 #endif /* KANJI */
373
374 static void set_default_server_announce_type(void);
375
376 struct parm_struct
377 {
378   char *label;
379   parm_type type;
380   parm_class class;
381   void *ptr;
382   BOOL (*special)();
383 } parm_table[] =
384 {
385   {"debuglevel",       P_INTEGER, P_GLOBAL, &DEBUGLEVEL,                NULL},
386   {"log level",        P_INTEGER, P_GLOBAL, &DEBUGLEVEL,                NULL},
387   {"syslog",           P_INTEGER, P_GLOBAL, &Globals.syslog,            NULL},
388   {"syslog only",      P_BOOL,    P_GLOBAL, &Globals.bSyslogOnly,       NULL},
389   {"protocol",         P_INTEGER, P_GLOBAL, &Globals.maxprotocol,handle_protocol},
390   {"security",         P_INTEGER, P_GLOBAL, &Globals.security,handle_security},
391   {"printing",         P_INTEGER, P_GLOBAL, &Globals.printing,handle_printing},
392   {"max disk size",    P_INTEGER, P_GLOBAL, &Globals.maxdisksize,       NULL},
393   {"lpq cache time",   P_INTEGER, P_GLOBAL, &Globals.lpqcachetime,      NULL},
394   {"announce as",      P_INTEGER, P_GLOBAL, &Globals.announce_as,      handle_announce_as},
395   {"encrypt passwords",P_BOOL,    P_GLOBAL, &Globals.bEncryptPasswords, NULL},
396   {"getwd cache",      P_BOOL,    P_GLOBAL, &use_getwd_cache,           NULL},
397   {"read prediction",  P_BOOL,    P_GLOBAL, &Globals.bReadPrediction,   NULL},
398   {"read bmpx",        P_BOOL,    P_GLOBAL, &Globals.bReadbmpx,         NULL},
399   {"read raw",         P_BOOL,    P_GLOBAL, &Globals.bReadRaw,          NULL},
400   {"write raw",        P_BOOL,    P_GLOBAL, &Globals.bWriteRaw,         NULL},
401   {"use rhosts",       P_BOOL,    P_GLOBAL, &Globals.bUseRhosts,        NULL},
402   {"load printers",    P_BOOL,    P_GLOBAL, &Globals.bLoadPrinters,     NULL},
403   {"null passwords",   P_BOOL,    P_GLOBAL, &Globals.bNullPasswords,    NULL},
404   {"strip dot",        P_BOOL,    P_GLOBAL, &Globals.bStripDot,         NULL},
405   {"interfaces",       P_STRING,  P_GLOBAL, &Globals.szInterfaces,      NULL},
406   {"password server",  P_STRING,  P_GLOBAL, &Globals.szPasswordServer,  NULL},
407   {"socket options",   P_GSTRING, P_GLOBAL, user_socket_options,        NULL},
408   {"netbios name",     P_UGSTRING,P_GLOBAL, myname,                     NULL},
409   {"netbios aliases",  P_STRING,  P_GLOBAL, &Globals.szNetbiosAliases,  NULL},
410   {"smbrun",           P_STRING,  P_GLOBAL, &Globals.szSmbrun,          NULL},
411   {"log file",         P_STRING,  P_GLOBAL, &Globals.szLogFile,         NULL},
412   {"config file",      P_STRING,  P_GLOBAL, &Globals.szConfigFile,      NULL},
413   {"smb passwd file",  P_STRING,  P_GLOBAL, &Globals.szSMBPasswdFile,   NULL},
414   {"hosts equiv",      P_STRING,  P_GLOBAL, &Globals.szHostsEquiv,      NULL},
415   {"preload",          P_STRING,  P_GLOBAL, &Globals.szAutoServices,    NULL},
416   {"auto services",    P_STRING,  P_GLOBAL, &Globals.szAutoServices,    NULL},
417   {"server string",    P_STRING,  P_GLOBAL, &Globals.szServerString,    NULL},
418   {"printcap name",    P_STRING,  P_GLOBAL, &Globals.szPrintcapname,    NULL},
419   {"printcap",         P_STRING,  P_GLOBAL, &Globals.szPrintcapname,    NULL},
420   {"lock dir",         P_STRING,  P_GLOBAL, &Globals.szLockDir,         NULL},
421   {"lock directory",   P_STRING,  P_GLOBAL, &Globals.szLockDir,         NULL},
422   {"root directory",   P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
423   {"root dir",         P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
424   {"root",             P_STRING,  P_GLOBAL, &Globals.szRootdir,         NULL},
425   {"default service",  P_STRING,  P_GLOBAL, &Globals.szDefaultService,  NULL},
426   {"default",          P_STRING,  P_GLOBAL, &Globals.szDefaultService,  NULL},
427   {"message command",  P_STRING,  P_GLOBAL, &Globals.szMsgCommand,      NULL},
428   {"dfree command",    P_STRING,  P_GLOBAL, &Globals.szDfree,           NULL},
429   {"passwd program",   P_STRING,  P_GLOBAL, &Globals.szPasswdProgram,   NULL},
430   {"passwd chat",      P_STRING,  P_GLOBAL, &Globals.szPasswdChat,      NULL},
431   {"valid chars",      P_STRING,  P_GLOBAL, &Globals.szValidChars,      handle_valid_chars},
432   {"workgroup",        P_USTRING, P_GLOBAL, &Globals.szWorkGroup,       NULL},
433   {"domain controller",P_STRING,  P_GLOBAL, &Globals.szDomainController,NULL},
434   {"username map",     P_STRING,  P_GLOBAL, &Globals.szUsernameMap,     NULL},
435   {"character set",    P_STRING,  P_GLOBAL, &Globals.szCharacterSet,    handle_character_set},
436   {"logon script",     P_STRING,  P_GLOBAL, &Globals.szLogonScript,     NULL},
437   {"logon path",       P_STRING,  P_GLOBAL, &Globals.szLogonPath,       NULL},
438   {"remote announce",  P_STRING,  P_GLOBAL, &Globals.szRemoteAnnounce,  NULL},
439   {"socket address",   P_STRING,  P_GLOBAL, &Globals.szSocketAddress,   NULL},
440   {"homedir map",      P_STRING,  P_GLOBAL, &Globals.szNISHomeMapName,  NULL},
441   {"announce version", P_STRING,  P_GLOBAL, &Globals.szAnnounceVersion, NULL},
442   {"max log size",     P_INTEGER, P_GLOBAL, &Globals.max_log_size,      NULL},
443   {"mangled stack",    P_INTEGER, P_GLOBAL, &Globals.mangled_stack,     NULL},
444   {"max mux",          P_INTEGER, P_GLOBAL, &Globals.max_mux,           NULL},
445   {"max xmit",         P_INTEGER, P_GLOBAL, &Globals.max_xmit,          NULL},
446   {"max packet",       P_INTEGER, P_GLOBAL, &Globals.max_packet,        NULL},
447   {"packet size",      P_INTEGER, P_GLOBAL, &Globals.max_packet,        NULL},
448   {"password level",   P_INTEGER, P_GLOBAL, &Globals.pwordlevel,        NULL},
449   {"keepalive",        P_INTEGER, P_GLOBAL, &keepalive,                 NULL},
450   {"deadtime",         P_INTEGER, P_GLOBAL, &Globals.deadtime,          NULL},
451   {"time offset",      P_INTEGER, P_GLOBAL, &extra_time_offset,         NULL},
452   {"read size",        P_INTEGER, P_GLOBAL, &Globals.ReadSize,          NULL},
453   {"shared mem size",  P_INTEGER, P_GLOBAL, &Globals.shmem_size,        NULL},
454   {"shared file entries",  P_INTEGER, P_GLOBAL, &Globals.shmem_hash_size, NULL},
455 #ifdef KANJI
456   {"coding system",    P_INTEGER, P_GLOBAL, &coding_system, handle_coding_system},
457 #endif /* KANJI */
458   {"client code page", P_INTEGER, P_GLOBAL, &Globals.client_code_page,  NULL},
459   {"os level",         P_INTEGER, P_GLOBAL, &Globals.os_level,          NULL},
460   {"max ttl",          P_INTEGER, P_GLOBAL, &Globals.max_ttl,           NULL},
461   {"dns proxy",        P_BOOL,    P_GLOBAL, &Globals.bDNSproxy,         NULL},
462   {"wins support",     P_BOOL,    P_GLOBAL, &Globals.bWINSsupport,      NULL},
463   {"wins proxy",       P_BOOL,    P_GLOBAL, &Globals.bWINSproxy,        NULL},
464   {"wins server",      P_STRING,  P_GLOBAL, &Globals.szWINSserver,      NULL},
465   {"preferred master", P_BOOL,    P_GLOBAL, &Globals.bPreferredMaster,  NULL},
466   {"prefered master",  P_BOOL,    P_GLOBAL, &Globals.bPreferredMaster,  NULL},
467   {"local master",     P_BOOL,    P_GLOBAL, &Globals.bLocalMaster,      NULL},
468   {"domain master",    P_BOOL,    P_GLOBAL, &Globals.bDomainMaster,     NULL},
469   {"domain logons",    P_BOOL,    P_GLOBAL, &Globals.bDomainLogons,     NULL},
470   {"browse list",      P_BOOL,    P_GLOBAL, &Globals.bBrowseList,       NULL},
471   {"unix realname",    P_BOOL,    P_GLOBAL, &Globals.bUnixRealname,     NULL},
472   {"NIS homedir",      P_BOOL,    P_GLOBAL, &Globals.bNISHomeMap,       NULL},
473   {"time server",      P_BOOL,    P_GLOBAL, &Globals.bTimeServer,       NULL},
474   {"-valid",           P_BOOL,    P_LOCAL,  &sDefault.valid,            NULL},
475   {"comment",          P_STRING,  P_LOCAL,  &sDefault.comment,          NULL},
476   {"copy",             P_STRING,  P_LOCAL,  &sDefault.szCopy,    handle_copy},
477   {"include",          P_STRING,  P_LOCAL,  &sDefault.szInclude, handle_include},
478   {"exec",             P_STRING,  P_LOCAL,  &sDefault.szPreExec,        NULL},
479   {"preexec",          P_STRING,  P_LOCAL,  &sDefault.szPreExec,        NULL},
480   {"postexec",         P_STRING,  P_LOCAL,  &sDefault.szPostExec,       NULL},
481   {"root preexec",     P_STRING,  P_LOCAL,  &sDefault.szRootPreExec,    NULL},
482   {"root postexec",    P_STRING,  P_LOCAL,  &sDefault.szRootPostExec,   NULL},
483   {"alternate permissions",P_BOOL,P_LOCAL,  &sDefault.bAlternatePerm,   NULL},
484   {"revalidate",       P_BOOL,    P_LOCAL,  &sDefault.bRevalidate,      NULL},
485   {"default case",     P_INTEGER, P_LOCAL,  &sDefault.iDefaultCase,   handle_case},
486   {"case sensitive",   P_BOOL,    P_LOCAL,  &sDefault.bCaseSensitive,   NULL},
487   {"casesignames",     P_BOOL,    P_LOCAL,  &sDefault.bCaseSensitive,   NULL},
488   {"preserve case",    P_BOOL,    P_LOCAL,  &sDefault.bCasePreserve,    NULL},
489   {"short preserve case",P_BOOL,  P_LOCAL,  &sDefault.bShortCasePreserve,NULL},
490   {"mangle case",      P_BOOL,    P_LOCAL,  &sDefault.bCaseMangle,      NULL},
491   {"mangling char",    P_CHAR,    P_LOCAL,  &sDefault.magic_char,       NULL},
492   {"browseable",       P_BOOL,    P_LOCAL,  &sDefault.bBrowseable,      NULL},
493   {"browsable",        P_BOOL,    P_LOCAL,  &sDefault.bBrowseable,      NULL},
494   {"available",        P_BOOL,    P_LOCAL,  &sDefault.bAvailable,       NULL},
495   {"path",             P_STRING,  P_LOCAL,  &sDefault.szPath,           NULL},
496   {"directory",        P_STRING,  P_LOCAL,  &sDefault.szPath,           NULL},
497   {"username",         P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
498   {"user",             P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
499   {"users",            P_STRING,  P_LOCAL,  &sDefault.szUsername,       NULL},
500   {"guest account",    P_STRING,  P_LOCAL,  &sDefault.szGuestaccount,   NULL},
501   {"invalid users",    P_STRING,  P_LOCAL,  &sDefault.szInvalidUsers,   NULL},
502   {"valid users",      P_STRING,  P_LOCAL,  &sDefault.szValidUsers,     NULL},
503   {"admin users",      P_STRING,  P_LOCAL,  &sDefault.szAdminUsers,     NULL},
504   {"read list",        P_STRING,  P_LOCAL,  &sDefault.readlist,         NULL},
505   {"write list",       P_STRING,  P_LOCAL,  &sDefault.writelist,        NULL},
506   {"volume",           P_STRING,  P_LOCAL,  &sDefault.volume,           NULL},
507   {"force user",       P_STRING,  P_LOCAL,  &sDefault.force_user,       NULL},
508   {"force group",      P_STRING,  P_LOCAL,  &sDefault.force_group,      NULL},
509   {"group",            P_STRING,  P_LOCAL,  &sDefault.force_group,      NULL},
510   {"read only",        P_BOOL,    P_LOCAL,  &sDefault.bRead_only,       NULL},
511   {"write ok",         P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
512   {"writeable",        P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
513   {"writable",         P_BOOLREV, P_LOCAL,  &sDefault.bRead_only,       NULL},
514   {"max connections",  P_INTEGER, P_LOCAL,  &sDefault.iMaxConnections,  NULL},
515   {"min print space",  P_INTEGER, P_LOCAL,  &sDefault.iMinPrintSpace,   NULL},
516   {"create mask",      P_OCTAL,   P_LOCAL,  &sDefault.iCreate_mask,     NULL},
517   {"create mode",      P_OCTAL,   P_LOCAL,  &sDefault.iCreate_mask,     NULL},
518   {"force create mode",P_OCTAL,   P_LOCAL,  &sDefault.iCreate_force_mode,     NULL},
519   {"directory mask",   P_OCTAL,   P_LOCAL,  &sDefault.iDir_mask,        NULL},
520   {"directory mode",   P_OCTAL,   P_LOCAL,  &sDefault.iDir_mask,        NULL},
521   {"force directory mode",   P_OCTAL,   P_LOCAL,  &sDefault.iDir_force_mode,        NULL},
522   {"set directory",    P_BOOLREV, P_LOCAL,  &sDefault.bNo_set_dir,      NULL},
523   {"status",           P_BOOL,    P_LOCAL,  &sDefault.status,           NULL},
524   {"hide dot files",   P_BOOL,    P_LOCAL,  &sDefault.bHideDotFiles,    NULL},
525   {"veto files",       P_STRING,  P_LOCAL,  &sDefault.szVetoFiles,      NULL},
526   {"hide files",       P_STRING,  P_LOCAL,  &sDefault.szHideFiles,      NULL},
527   {"guest only",       P_BOOL,    P_LOCAL,  &sDefault.bGuest_only,      NULL},
528   {"only guest",       P_BOOL,    P_LOCAL,  &sDefault.bGuest_only,      NULL},
529   {"guest ok",         P_BOOL,    P_LOCAL,  &sDefault.bGuest_ok,        NULL},
530   {"public",           P_BOOL,    P_LOCAL,  &sDefault.bGuest_ok,        NULL},
531   {"print ok",         P_BOOL,    P_LOCAL,  &sDefault.bPrint_ok,        NULL},
532   {"printable",        P_BOOL,    P_LOCAL,  &sDefault.bPrint_ok,        NULL},
533   {"postscript",       P_BOOL,    P_LOCAL,  &sDefault.bPostscript,      NULL},
534   {"map system",       P_BOOL,    P_LOCAL,  &sDefault.bMap_system,      NULL},
535   {"map hidden",       P_BOOL,    P_LOCAL,  &sDefault.bMap_hidden,      NULL},
536   {"map archive",      P_BOOL,    P_LOCAL,  &sDefault.bMap_archive,     NULL},
537   {"locking",          P_BOOL,    P_LOCAL,  &sDefault.bLocking,         NULL},
538   {"strict locking",   P_BOOL,    P_LOCAL,  &sDefault.bStrictLocking,   NULL},
539   {"share modes",      P_BOOL,    P_LOCAL,  &sDefault.bShareModes,      NULL},
540   {"only user",        P_BOOL,    P_LOCAL,  &sDefault.bOnlyUser,        NULL},
541   {"wide links",       P_BOOL,    P_LOCAL,  &sDefault.bWidelinks,       NULL},
542   {"follow symlinks",  P_BOOL,    P_LOCAL,  &sDefault.bSymlinks,        NULL},
543   {"sync always",      P_BOOL,    P_LOCAL,  &sDefault.bSyncAlways,      NULL},
544   {"mangled names",    P_BOOL,    P_LOCAL,  &sDefault.bMangledNames,    NULL},
545   {"fake oplocks",     P_BOOL,    P_LOCAL,  &sDefault.bFakeOplocks,     NULL},
546   {"print command",    P_STRING,  P_LOCAL,  &sDefault.szPrintcommand,   NULL},
547   {"lpq command",      P_STRING,  P_LOCAL,  &sDefault.szLpqcommand,     NULL},
548   {"lprm command",     P_STRING,  P_LOCAL,  &sDefault.szLprmcommand,    NULL},
549   {"lppause command",  P_STRING,  P_LOCAL,  &sDefault.szLppausecommand, NULL},
550   {"lpresume command", P_STRING,  P_LOCAL,  &sDefault.szLpresumecommand,NULL},
551   {"printer",          P_STRING,  P_LOCAL,  &sDefault.szPrintername,    NULL},
552   {"printer name",     P_STRING,  P_LOCAL,  &sDefault.szPrintername,    NULL},
553   {"printer driver",   P_STRING,  P_LOCAL,  &sDefault.szPrinterDriver,  NULL},
554   {"hosts allow",      P_STRING,  P_LOCAL,  &sDefault.szHostsallow,     NULL},
555   {"allow hosts",      P_STRING,  P_LOCAL,  &sDefault.szHostsallow,     NULL},
556   {"hosts deny",       P_STRING,  P_LOCAL,  &sDefault.szHostsdeny,      NULL},
557   {"deny hosts",       P_STRING,  P_LOCAL,  &sDefault.szHostsdeny,      NULL},
558   {"dont descend",     P_STRING,  P_LOCAL,  &sDefault.szDontdescend,    NULL},
559   {"magic script",     P_STRING,  P_LOCAL,  &sDefault.szMagicScript,    NULL},
560   {"magic output",     P_STRING,  P_LOCAL,  &sDefault.szMagicOutput,    NULL},
561   {"mangled map",      P_STRING,  P_LOCAL,  &sDefault.szMangledMap,     NULL},
562   {"delete readonly",  P_BOOL,    P_LOCAL,  &sDefault.bDeleteReadonly,  NULL},
563
564   {NULL,               P_BOOL,    P_NONE,   NULL,                       NULL}
565 };
566
567
568
569 /***************************************************************************
570 Initialise the global parameter structure.
571 ***************************************************************************/
572 static void init_globals(void)
573 {
574   static BOOL done_init = False;
575   pstring s;
576
577   if (!done_init)
578     {
579       int i;
580       bzero((void *)&Globals,sizeof(Globals));
581
582       for (i = 0; parm_table[i].label; i++) 
583         if ((parm_table[i].type == P_STRING ||
584              parm_table[i].type == P_USTRING) && 
585             parm_table[i].ptr)
586           string_init(parm_table[i].ptr,"");
587
588       string_set(&sDefault.szGuestaccount, GUEST_ACCOUNT);
589       string_set(&sDefault.szPrinterDriver, "NULL");
590
591       done_init = True;
592     }
593
594
595   DEBUG(3,("Initialising global parameters\n"));
596
597 #ifdef SMB_PASSWD_FILE
598   string_set(&Globals.szSMBPasswdFile, SMB_PASSWD_FILE);
599 #endif
600   string_set(&Globals.szPasswdChat,"*old*password* %o\\n *new*password* %n\\n *new*password* %n\\n *changed*");
601   string_set(&Globals.szWorkGroup, WORKGROUP);
602 #ifdef SMB_PASSWD
603   string_set(&Globals.szPasswdProgram, SMB_PASSWD);
604 #else
605   string_set(&Globals.szPasswdProgram, "/bin/passwd");
606 #endif
607   string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
608   string_set(&Globals.szLockDir, LOCKDIR);
609   string_set(&Globals.szRootdir, "/");
610   string_set(&Globals.szSmbrun, SMBRUN);
611   string_set(&Globals.szSocketAddress, "0.0.0.0");
612   sprintf(s,"Samba %s",VERSION);
613   string_set(&Globals.szServerString,s);
614   sprintf(s,"%d.%d", DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION);
615   string_set(&Globals.szAnnounceVersion,s);
616   Globals.bLoadPrinters = True;
617   Globals.bUseRhosts = False;
618   Globals.max_packet = 65535;
619   Globals.mangled_stack = 50;
620   Globals.max_xmit = 65535;
621   Globals.max_mux = 50; /* This is *needed* for profile support. */
622   Globals.lpqcachetime = 10;
623   Globals.pwordlevel = 0;
624   Globals.deadtime = 0;
625   Globals.max_log_size = 5000;
626   Globals.maxprotocol = PROTOCOL_NT1;
627   Globals.security = SEC_SHARE;
628   Globals.bEncryptPasswords = False;
629   Globals.printing = DEFAULT_PRINTING;
630   Globals.bReadRaw = True;
631   Globals.bWriteRaw = True;
632   Globals.bReadPrediction = False;
633   Globals.bReadbmpx = True;
634   Globals.bNullPasswords = False;
635   Globals.bStripDot = False;
636   Globals.syslog = 1;
637   Globals.bSyslogOnly = False;
638   Globals.os_level = 0;
639   Globals.max_ttl = 60*60*4; /* 2 hours default */
640   Globals.ReadSize = 16*1024;
641   Globals.shmem_size = SHMEM_SIZE;
642   Globals.shmem_hash_size = SHMEM_HASH_SIZE;
643   Globals.announce_as = ANNOUNCE_AS_NT;
644   Globals.bUnixRealname = False;
645 #if (defined(NETGROUP) && defined(AUTOMOUNT))
646   Globals.bNISHomeMap = False;
647   string_set(&Globals.szNISHomeMapName, "auto.home");
648 #endif
649 #ifdef KANJI
650   coding_system = interpret_coding_system (KANJI, SJIS_CODE);
651 #endif /* KANJI */
652   Globals.client_code_page = DEFAULT_CLIENT_CODE_PAGE;
653   Globals.bTimeServer = False;
654
655 /* these parameters are set to defaults that are more appropriate
656    for the increasing samba install base:
657
658    as a member of the workgroup, that will possibly become a
659    _local_ master browser (lm = True).  this is opposed to a forced
660    local master browser startup (pm = True).
661
662    doesn't provide WINS server service by default (wsupp = False),
663    and doesn't provide domain master browser services by default, either.
664
665 */
666
667   Globals.bPreferredMaster = False;
668   Globals.bLocalMaster = True;
669   Globals.bDomainMaster = False;
670   Globals.bDomainLogons = False;
671   Globals.bBrowseList = True;
672   Globals.bWINSsupport = False;
673   Globals.bWINSproxy = False;
674
675 /* this parameter is currently set to the default functionality
676    in samba.  given that w95 and NT is starting to use DNS for
677    server resolution, i expect that at some point it would be
678    sensible to default this to False.
679
680    this parameter is added because nmbd is a single process, and
681    gethostbyname is a blocking call, which can take out nmbd for
682    several seconds while a dns lookup is performed.
683
684  */
685
686   Globals.bDNSproxy = True;
687 }
688
689 /***************************************************************************
690 check if a string is initialised and if not then initialise it
691 ***************************************************************************/
692 static void string_initial(char **s,char *v)
693 {
694   if (!*s || !**s)
695     string_init(s,v);
696 }
697
698
699 /***************************************************************************
700 Initialise the sDefault parameter structure.
701 ***************************************************************************/
702 static void init_locals(void)
703 {
704   /* choose defaults depending on the type of printing */
705   switch (Globals.printing)
706     {
707     case PRINT_BSD:
708     case PRINT_AIX:
709     case PRINT_LPRNG:
710     case PRINT_PLP:
711       string_initial(&sDefault.szLpqcommand,"lpq -P%p");
712       string_initial(&sDefault.szLprmcommand,"lprm -P%p %j");
713       string_initial(&sDefault.szPrintcommand,"lpr -r -P%p %s");
714       break;
715
716     case PRINT_SYSV:
717     case PRINT_HPUX:
718       string_initial(&sDefault.szLpqcommand,"lpstat -o%p");
719       string_initial(&sDefault.szLprmcommand,"cancel %p-%j");
720       string_initial(&sDefault.szPrintcommand,"lp -c -d%p %s; rm %s");
721 #ifdef SVR4
722       string_initial(&sDefault.szLppausecommand,"lp -i %p-%j -H hold");
723       string_initial(&sDefault.szLpresumecommand,"lp -i %p-%j -H resume");
724 #endif
725       break;
726
727     case PRINT_QNX:
728       string_initial(&sDefault.szLpqcommand,"lpq -P%p");
729       string_initial(&sDefault.szLprmcommand,"lprm -P%p %j");
730       string_initial(&sDefault.szPrintcommand,"lp -r -P%p %s");
731       break;
732
733       
734     }
735 }
736
737
738 /******************************************************************* a
739 convenience routine to grab string parameters into a rotating buffer,
740 and run standard_sub_basic on them. The buffers can be written to by
741 callers without affecting the source string.
742 ********************************************************************/
743 char *lp_string(char *s)
744 {
745   static char *bufs[10];
746   static int buflen[10];
747   static int next = -1;  
748   char *ret;
749   int i;
750   int len = s?strlen(s):0;
751
752   if (next == -1) {
753     /* initialisation */
754     for (i=0;i<10;i++) {
755       bufs[i] = NULL;
756       buflen[i] = 0;
757     }
758     next = 0;
759   }
760
761   len = MAX(len+100,sizeof(pstring)); /* the +100 is for some
762                                          substitution room */
763
764   if (buflen[next] != len) {
765     buflen[next] = len;
766     if (bufs[next]) free(bufs[next]);
767     bufs[next] = (char *)malloc(len);
768     if (!bufs[next]) {
769       DEBUG(0,("out of memory in lp_string()"));
770       exit(1);
771     }
772   } 
773
774   ret = &bufs[next][0];
775   next = (next+1)%10;
776
777   if (!s) 
778     *ret = 0;
779   else
780     StrCpy(ret,s);
781
782   standard_sub_basic(ret);
783   return(ret);
784 }
785
786
787 /*
788    In this section all the functions that are used to access the 
789    parameters from the rest of the program are defined 
790 */
791
792 #define FN_GLOBAL_STRING(fn_name,ptr) \
793  char *fn_name(void) {return(lp_string(*(char **)(ptr) ? *(char **)(ptr) : ""));}
794 #define FN_GLOBAL_BOOL(fn_name,ptr) \
795  BOOL fn_name(void) {return(*(BOOL *)(ptr));}
796 #define FN_GLOBAL_CHAR(fn_name,ptr) \
797  char fn_name(void) {return(*(char *)(ptr));}
798 #define FN_GLOBAL_INTEGER(fn_name,ptr) \
799  int fn_name(void) {return(*(int *)(ptr));}
800
801 #define FN_LOCAL_STRING(fn_name,val) \
802  char *fn_name(int i) {return(lp_string((LP_SNUM_OK(i)&&pSERVICE(i)->val)?pSERVICE(i)->val : sDefault.val));}
803 #define FN_LOCAL_BOOL(fn_name,val) \
804  BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
805 #define FN_LOCAL_CHAR(fn_name,val) \
806  char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
807 #define FN_LOCAL_INTEGER(fn_name,val) \
808  int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
809
810 FN_GLOBAL_STRING(lp_logfile,&Globals.szLogFile)
811 FN_GLOBAL_STRING(lp_smbrun,&Globals.szSmbrun)
812 FN_GLOBAL_STRING(lp_configfile,&Globals.szConfigFile)
813 FN_GLOBAL_STRING(lp_smb_passwd_file,&Globals.szSMBPasswdFile)
814 FN_GLOBAL_STRING(lp_serverstring,&Globals.szServerString)
815 FN_GLOBAL_STRING(lp_printcapname,&Globals.szPrintcapname)
816 FN_GLOBAL_STRING(lp_lockdir,&Globals.szLockDir)
817 FN_GLOBAL_STRING(lp_rootdir,&Globals.szRootdir)
818 FN_GLOBAL_STRING(lp_defaultservice,&Globals.szDefaultService)
819 FN_GLOBAL_STRING(lp_msg_command,&Globals.szMsgCommand)
820 FN_GLOBAL_STRING(lp_dfree_command,&Globals.szDfree)
821 FN_GLOBAL_STRING(lp_hosts_equiv,&Globals.szHostsEquiv)
822 FN_GLOBAL_STRING(lp_auto_services,&Globals.szAutoServices)
823 FN_GLOBAL_STRING(lp_passwd_program,&Globals.szPasswdProgram)
824 FN_GLOBAL_STRING(lp_passwd_chat,&Globals.szPasswdChat)
825 FN_GLOBAL_STRING(lp_passwordserver,&Globals.szPasswordServer)
826 FN_GLOBAL_STRING(lp_workgroup,&Globals.szWorkGroup)
827 FN_GLOBAL_STRING(lp_domain_controller,&Globals.szDomainController)
828 FN_GLOBAL_STRING(lp_username_map,&Globals.szUsernameMap)
829 FN_GLOBAL_STRING(lp_character_set,&Globals.szCharacterSet) 
830 FN_GLOBAL_STRING(lp_logon_script,&Globals.szLogonScript) 
831 FN_GLOBAL_STRING(lp_logon_path,&Globals.szLogonPath) 
832 FN_GLOBAL_STRING(lp_remote_announce,&Globals.szRemoteAnnounce) 
833 FN_GLOBAL_STRING(lp_wins_server,&Globals.szWINSserver)
834 FN_GLOBAL_STRING(lp_interfaces,&Globals.szInterfaces)
835 FN_GLOBAL_STRING(lp_socket_address,&Globals.szSocketAddress)
836 FN_GLOBAL_STRING(lp_nis_home_map_name,&Globals.szNISHomeMapName)
837 FN_GLOBAL_STRING(lp_announce_version,&Globals.szAnnounceVersion)
838 FN_GLOBAL_STRING(lp_netbios_aliases,&Globals.szNetbiosAliases)
839
840 FN_GLOBAL_BOOL(lp_dns_proxy,&Globals.bDNSproxy)
841 FN_GLOBAL_BOOL(lp_wins_support,&Globals.bWINSsupport)
842 FN_GLOBAL_BOOL(lp_wins_proxy,&Globals.bWINSproxy)
843 FN_GLOBAL_BOOL(lp_local_master,&Globals.bLocalMaster)
844 FN_GLOBAL_BOOL(lp_domain_master,&Globals.bDomainMaster)
845 FN_GLOBAL_BOOL(lp_domain_logons,&Globals.bDomainLogons)
846 FN_GLOBAL_BOOL(lp_preferred_master,&Globals.bPreferredMaster)
847 FN_GLOBAL_BOOL(lp_load_printers,&Globals.bLoadPrinters)
848 FN_GLOBAL_BOOL(lp_use_rhosts,&Globals.bUseRhosts)
849 FN_GLOBAL_BOOL(lp_getwdcache,&use_getwd_cache)
850 FN_GLOBAL_BOOL(lp_readprediction,&Globals.bReadPrediction)
851 FN_GLOBAL_BOOL(lp_readbmpx,&Globals.bReadbmpx)
852 FN_GLOBAL_BOOL(lp_readraw,&Globals.bReadRaw)
853 FN_GLOBAL_BOOL(lp_writeraw,&Globals.bWriteRaw)
854 FN_GLOBAL_BOOL(lp_null_passwords,&Globals.bNullPasswords)
855 FN_GLOBAL_BOOL(lp_strip_dot,&Globals.bStripDot)
856 FN_GLOBAL_BOOL(lp_encrypted_passwords,&Globals.bEncryptPasswords)
857 FN_GLOBAL_BOOL(lp_syslog_only,&Globals.bSyslogOnly)
858 FN_GLOBAL_BOOL(lp_browse_list,&Globals.bBrowseList)
859 FN_GLOBAL_BOOL(lp_unix_realname,&Globals.bUnixRealname)
860 FN_GLOBAL_BOOL(lp_nis_home_map,&Globals.bNISHomeMap)
861 FN_GLOBAL_BOOL(lp_time_server,&Globals.bTimeServer)
862
863 FN_GLOBAL_INTEGER(lp_os_level,&Globals.os_level)
864 FN_GLOBAL_INTEGER(lp_max_ttl,&Globals.max_ttl)
865 FN_GLOBAL_INTEGER(lp_max_log_size,&Globals.max_log_size)
866 FN_GLOBAL_INTEGER(lp_mangledstack,&Globals.mangled_stack)
867 FN_GLOBAL_INTEGER(lp_maxxmit,&Globals.max_xmit)
868 FN_GLOBAL_INTEGER(lp_maxmux,&Globals.max_mux)
869 FN_GLOBAL_INTEGER(lp_maxpacket,&Globals.max_packet)
870 FN_GLOBAL_INTEGER(lp_keepalive,&keepalive)
871 FN_GLOBAL_INTEGER(lp_passwordlevel,&Globals.pwordlevel)
872 FN_GLOBAL_INTEGER(lp_readsize,&Globals.ReadSize)
873 FN_GLOBAL_INTEGER(lp_shmem_size,&Globals.shmem_size)
874 FN_GLOBAL_INTEGER(lp_shmem_hash_size,&Globals.shmem_hash_size)
875 FN_GLOBAL_INTEGER(lp_deadtime,&Globals.deadtime)
876 FN_GLOBAL_INTEGER(lp_maxprotocol,&Globals.maxprotocol)
877 FN_GLOBAL_INTEGER(lp_security,&Globals.security)
878 FN_GLOBAL_INTEGER(lp_printing,&Globals.printing)
879 FN_GLOBAL_INTEGER(lp_maxdisksize,&Globals.maxdisksize)
880 FN_GLOBAL_INTEGER(lp_lpqcachetime,&Globals.lpqcachetime)
881 FN_GLOBAL_INTEGER(lp_syslog,&Globals.syslog)
882 FN_GLOBAL_INTEGER(lp_client_code_page,&Globals.client_code_page)
883 FN_GLOBAL_INTEGER(lp_announce_as,&Globals.announce_as)
884
885 FN_LOCAL_STRING(lp_preexec,szPreExec)
886 FN_LOCAL_STRING(lp_postexec,szPostExec)
887 FN_LOCAL_STRING(lp_rootpreexec,szRootPreExec)
888 FN_LOCAL_STRING(lp_rootpostexec,szRootPostExec)
889 FN_LOCAL_STRING(lp_servicename,szService)
890 FN_LOCAL_STRING(lp_pathname,szPath)
891 FN_LOCAL_STRING(lp_dontdescend,szDontdescend)
892 FN_LOCAL_STRING(lp_username,szUsername)
893 FN_LOCAL_STRING(lp_guestaccount,szGuestaccount)
894 FN_LOCAL_STRING(lp_invalid_users,szInvalidUsers)
895 FN_LOCAL_STRING(lp_valid_users,szValidUsers)
896 FN_LOCAL_STRING(lp_admin_users,szAdminUsers)
897 FN_LOCAL_STRING(lp_printcommand,szPrintcommand)
898 FN_LOCAL_STRING(lp_lpqcommand,szLpqcommand)
899 FN_LOCAL_STRING(lp_lprmcommand,szLprmcommand)
900 FN_LOCAL_STRING(lp_lppausecommand,szLppausecommand)
901 FN_LOCAL_STRING(lp_lpresumecommand,szLpresumecommand)
902 FN_LOCAL_STRING(lp_printername,szPrintername)
903 FN_LOCAL_STRING(lp_printerdriver,szPrinterDriver)
904 FN_LOCAL_STRING(lp_hostsallow,szHostsallow)
905 FN_LOCAL_STRING(lp_hostsdeny,szHostsdeny)
906 FN_LOCAL_STRING(lp_magicscript,szMagicScript)
907 FN_LOCAL_STRING(lp_magicoutput,szMagicOutput)
908 FN_LOCAL_STRING(lp_comment,comment)
909 FN_LOCAL_STRING(lp_force_user,force_user)
910 FN_LOCAL_STRING(lp_force_group,force_group)
911 FN_LOCAL_STRING(lp_readlist,readlist)
912 FN_LOCAL_STRING(lp_writelist,writelist)
913 FN_LOCAL_STRING(lp_volume,volume)
914 FN_LOCAL_STRING(lp_mangled_map,szMangledMap)
915 FN_LOCAL_STRING(lp_veto_files,szVetoFiles)
916 FN_LOCAL_STRING(lp_hide_files,szHideFiles)
917
918 FN_LOCAL_BOOL(lp_alternate_permissions,bAlternatePerm)
919 FN_LOCAL_BOOL(lp_revalidate,bRevalidate)
920 FN_LOCAL_BOOL(lp_casesensitive,bCaseSensitive)
921 FN_LOCAL_BOOL(lp_preservecase,bCasePreserve)
922 FN_LOCAL_BOOL(lp_shortpreservecase,bShortCasePreserve)
923 FN_LOCAL_BOOL(lp_casemangle,bCaseMangle)
924 FN_LOCAL_BOOL(lp_status,status)
925 FN_LOCAL_BOOL(lp_hide_dot_files,bHideDotFiles)
926 FN_LOCAL_BOOL(lp_browseable,bBrowseable)
927 FN_LOCAL_BOOL(lp_readonly,bRead_only)
928 FN_LOCAL_BOOL(lp_no_set_dir,bNo_set_dir)
929 FN_LOCAL_BOOL(lp_guest_ok,bGuest_ok)
930 FN_LOCAL_BOOL(lp_guest_only,bGuest_only)
931 FN_LOCAL_BOOL(lp_print_ok,bPrint_ok)
932 FN_LOCAL_BOOL(lp_postscript,bPostscript)
933 FN_LOCAL_BOOL(lp_map_hidden,bMap_hidden)
934 FN_LOCAL_BOOL(lp_map_archive,bMap_archive)
935 FN_LOCAL_BOOL(lp_locking,bLocking)
936 FN_LOCAL_BOOL(lp_strict_locking,bStrictLocking)
937 FN_LOCAL_BOOL(lp_share_modes,bShareModes)
938 FN_LOCAL_BOOL(lp_onlyuser,bOnlyUser)
939 FN_LOCAL_BOOL(lp_manglednames,bMangledNames)
940 FN_LOCAL_BOOL(lp_widelinks,bWidelinks)
941 FN_LOCAL_BOOL(lp_symlinks,bSymlinks)
942 FN_LOCAL_BOOL(lp_syncalways,bSyncAlways)
943 FN_LOCAL_BOOL(lp_map_system,bMap_system)
944 FN_LOCAL_BOOL(lp_delete_readonly,bDeleteReadonly)
945 FN_LOCAL_BOOL(lp_fake_oplocks,bFakeOplocks)
946
947 FN_LOCAL_INTEGER(lp_create_mode,iCreate_mask)
948 FN_LOCAL_INTEGER(lp_force_create_mode,iCreate_force_mode)
949 FN_LOCAL_INTEGER(lp_dir_mode,iDir_mask)
950 FN_LOCAL_INTEGER(lp_force_dir_mode,iDir_force_mode)
951 FN_LOCAL_INTEGER(lp_max_connections,iMaxConnections)
952 FN_LOCAL_INTEGER(lp_defaultcase,iDefaultCase)
953 FN_LOCAL_INTEGER(lp_minprintspace,iMinPrintSpace)
954
955 FN_LOCAL_CHAR(lp_magicchar,magic_char)
956
957
958
959 /* local prototypes */
960 static int    strwicmp( char *psz1, char *psz2 );
961 static int    map_parameter( char *pszParmName);
962 static BOOL   set_boolean( BOOL *pb, char *pszParmValue );
963 static int    getservicebyname(char *pszServiceName, service *pserviceDest);
964 static void   copy_service( service *pserviceDest, 
965                             service *pserviceSource,
966                             BOOL *pcopymapDest );
967 static BOOL   service_ok(int iService);
968 static BOOL   do_parameter(char *pszParmName, char *pszParmValue);
969 static BOOL   do_section(char *pszSectionName);
970 static void init_copymap(service *pservice);
971
972
973 /***************************************************************************
974 initialise a service to the defaults
975 ***************************************************************************/
976 static void init_service(service *pservice)
977 {
978   bzero((char *)pservice,sizeof(service));
979   copy_service(pservice,&sDefault,NULL);
980 }
981
982
983 /***************************************************************************
984 free the dynamically allocated parts of a service struct
985 ***************************************************************************/
986 static void free_service(service *pservice)
987 {
988   int i;
989   if (!pservice)
990      return;
991
992   for (i=0;parm_table[i].label;i++)
993     if ((parm_table[i].type == P_STRING ||
994          parm_table[i].type == P_STRING) &&
995         parm_table[i].class == P_LOCAL)
996       string_free((char **)(((char *)pservice) + PTR_DIFF(parm_table[i].ptr,&sDefault)));
997 }
998
999 /***************************************************************************
1000 add a new service to the services array initialising it with the given 
1001 service
1002 ***************************************************************************/
1003 static int add_a_service(service *pservice, char *name)
1004 {
1005   int i;
1006   service tservice;
1007   int num_to_alloc = iNumServices+1;
1008
1009   tservice = *pservice;
1010
1011   /* it might already exist */
1012   if (name) 
1013     {
1014       i = getservicebyname(name,NULL);
1015       if (i >= 0)
1016         return(i);
1017     }
1018
1019   /* find an invalid one */
1020   for (i=0;i<iNumServices;i++)
1021     if (!pSERVICE(i)->valid)
1022       break;
1023
1024   /* if not, then create one */
1025   if (i == iNumServices)
1026     {
1027       ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc);
1028       if (ServicePtrs)
1029         pSERVICE(iNumServices) = (service *)malloc(sizeof(service));
1030
1031       if (!ServicePtrs || !pSERVICE(iNumServices))
1032         return(-1);
1033
1034       iNumServices++;
1035     }
1036   else
1037     free_service(pSERVICE(i));
1038
1039   pSERVICE(i)->valid = True;
1040
1041   init_service(pSERVICE(i));
1042   copy_service(pSERVICE(i),&tservice,NULL);
1043   if (name)
1044     string_set(&iSERVICE(i).szService,name);  
1045
1046   return(i);
1047 }
1048
1049 /***************************************************************************
1050 add a new home service, with the specified home directory, defaults coming 
1051 from service ifrom
1052 ***************************************************************************/
1053 BOOL lp_add_home(char *pszHomename, int iDefaultService, char *pszHomedir)
1054 {
1055   int i = add_a_service(pSERVICE(iDefaultService),pszHomename);
1056
1057   if (i < 0)
1058     return(False);
1059
1060   if (!(*(iSERVICE(i).szPath)) || strequal(iSERVICE(i).szPath,lp_pathname(-1)))
1061     string_set(&iSERVICE(i).szPath,pszHomedir);
1062   if (!(*(iSERVICE(i).comment)))
1063     {
1064       pstring comment;
1065       sprintf(comment,"Home directory of %s",pszHomename);
1066       string_set(&iSERVICE(i).comment,comment);
1067     }
1068   iSERVICE(i).bAvailable = sDefault.bAvailable;
1069   iSERVICE(i).bBrowseable = sDefault.bBrowseable;
1070
1071   DEBUG(3,("adding home directory %s at %s\n", pszHomename, pszHomedir));
1072
1073   return(True);
1074 }
1075
1076 /***************************************************************************
1077 add a new service, based on an old one
1078 ***************************************************************************/
1079 int lp_add_service(char *pszService, int iDefaultService)
1080 {
1081   return(add_a_service(pSERVICE(iDefaultService),pszService));
1082 }
1083
1084
1085 /***************************************************************************
1086 add the IPC service
1087 ***************************************************************************/
1088 static BOOL lp_add_ipc(void)
1089 {
1090   pstring comment;
1091   int i = add_a_service(&sDefault,"IPC$");
1092
1093   if (i < 0)
1094     return(False);
1095
1096   sprintf(comment,"IPC Service (%s)",lp_serverstring());
1097
1098   string_set(&iSERVICE(i).szPath,tmpdir());
1099   string_set(&iSERVICE(i).szUsername,"");
1100   string_set(&iSERVICE(i).comment,comment);
1101   iSERVICE(i).status = False;
1102   iSERVICE(i).iMaxConnections = 0;
1103   iSERVICE(i).bAvailable = True;
1104   iSERVICE(i).bRead_only = True;
1105   iSERVICE(i).bGuest_only = False;
1106   iSERVICE(i).bGuest_ok = True;
1107   iSERVICE(i).bPrint_ok = False;
1108   iSERVICE(i).bBrowseable = sDefault.bBrowseable;
1109
1110   DEBUG(3,("adding IPC service\n"));
1111
1112   return(True);
1113 }
1114
1115
1116 /***************************************************************************
1117 add a new printer service, with defaults coming from service iFrom
1118 ***************************************************************************/
1119 BOOL lp_add_printer(char *pszPrintername, int iDefaultService)
1120 {
1121   char *comment = "From Printcap";
1122   int i = add_a_service(pSERVICE(iDefaultService),pszPrintername);
1123   
1124   if (i < 0)
1125     return(False);
1126   
1127   /* note that we do NOT default the availability flag to True - */
1128   /* we take it from the default service passed. This allows all */
1129   /* dynamic printers to be disabled by disabling the [printers] */
1130   /* entry (if/when the 'available' keyword is implemented!).    */
1131   
1132   /* the printer name is set to the service name. */
1133   string_set(&iSERVICE(i).szPrintername,pszPrintername);
1134   string_set(&iSERVICE(i).comment,comment);
1135   iSERVICE(i).bBrowseable = sDefault.bBrowseable;
1136   /* Printers cannot be read_only. */
1137   iSERVICE(i).bRead_only = False;
1138   /* No share modes on printer services. */
1139   iSERVICE(i).bShareModes = False;
1140   /* Printer services must be printable. */
1141   iSERVICE(i).bPrint_ok = True;
1142   
1143   DEBUG(3,("adding printer service %s\n",pszPrintername));
1144   
1145   return(True);
1146 }
1147
1148
1149 /***************************************************************************
1150 Do a case-insensitive, whitespace-ignoring string compare.
1151 ***************************************************************************/
1152 static int strwicmp(char *psz1, char *psz2)
1153 {
1154    /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
1155    /* appropriate value. */
1156    if (psz1 == psz2)
1157       return (0);
1158    else
1159       if (psz1 == NULL)
1160          return (-1);
1161       else
1162           if (psz2 == NULL)
1163               return (1);
1164
1165    /* sync the strings on first non-whitespace */
1166    while (1)
1167    {
1168       while (isspace(*psz1))
1169          psz1++;
1170       while (isspace(*psz2))
1171          psz2++;
1172       if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0')
1173          break;
1174       psz1++;
1175       psz2++;
1176    }
1177    return (*psz1 - *psz2);
1178 }
1179
1180 /***************************************************************************
1181 Map a parameter's string representation to something we can use. 
1182 Returns False if the parameter string is not recognised, else TRUE.
1183 ***************************************************************************/
1184 static int map_parameter(char *pszParmName)
1185 {
1186    int iIndex;
1187
1188    if (*pszParmName == '-')
1189      return(-1);
1190
1191    for (iIndex = 0; parm_table[iIndex].label; iIndex++) 
1192       if (strwicmp(parm_table[iIndex].label, pszParmName) == 0)
1193          return(iIndex);
1194
1195    DEBUG(0,( "Unknown parameter encountered: \"%s\"\n", pszParmName));
1196    return(-1);
1197 }
1198
1199
1200 /***************************************************************************
1201 Set a boolean variable from the text value stored in the passed string.
1202 Returns True in success, False if the passed string does not correctly 
1203 represent a boolean.
1204 ***************************************************************************/
1205 static BOOL set_boolean(BOOL *pb, char *pszParmValue)
1206 {
1207    BOOL bRetval;
1208
1209    bRetval = True;
1210    if (strwicmp(pszParmValue, "yes") == 0 ||
1211        strwicmp(pszParmValue, "true") == 0 ||
1212        strwicmp(pszParmValue, "1") == 0)
1213       *pb = True;
1214    else
1215       if (strwicmp(pszParmValue, "no") == 0 ||
1216           strwicmp(pszParmValue, "False") == 0 ||
1217           strwicmp(pszParmValue, "0") == 0)
1218          *pb = False;
1219       else
1220       {
1221          DEBUG(0,( "Badly formed boolean in configuration file: \"%s\".\n",
1222                pszParmValue));
1223          bRetval = False;
1224       }
1225    return (bRetval);
1226 }
1227
1228 /***************************************************************************
1229 Find a service by name. Otherwise works like get_service.
1230 ***************************************************************************/
1231 static int getservicebyname(char *pszServiceName, service *pserviceDest)
1232 {
1233    int iService;
1234
1235    for (iService = iNumServices - 1; iService >= 0; iService--)
1236       if (VALID(iService) &&
1237           strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) 
1238       {
1239          if (pserviceDest != NULL)
1240            copy_service(pserviceDest, pSERVICE(iService), NULL);
1241          break;
1242       }
1243
1244    return (iService);
1245 }
1246
1247
1248
1249 /***************************************************************************
1250 Copy a service structure to another
1251
1252 If pcopymapDest is NULL then copy all fields
1253 ***************************************************************************/
1254 static void copy_service(service *pserviceDest, 
1255                          service *pserviceSource,
1256                          BOOL *pcopymapDest)
1257 {
1258   int i;
1259   BOOL bcopyall = (pcopymapDest == NULL);
1260
1261   for (i=0;parm_table[i].label;i++)
1262     if (parm_table[i].ptr && parm_table[i].class == P_LOCAL && 
1263         (bcopyall || pcopymapDest[i]))
1264       {
1265         void *def_ptr = parm_table[i].ptr;
1266         void *src_ptr = 
1267           ((char *)pserviceSource) + PTR_DIFF(def_ptr,&sDefault);
1268         void *dest_ptr = 
1269           ((char *)pserviceDest) + PTR_DIFF(def_ptr,&sDefault);
1270
1271         switch (parm_table[i].type)
1272           {
1273           case P_BOOL:
1274           case P_BOOLREV:
1275             *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
1276             break;
1277
1278           case P_INTEGER:
1279           case P_OCTAL:
1280             *(int *)dest_ptr = *(int *)src_ptr;
1281             break;
1282
1283           case P_CHAR:
1284             *(char *)dest_ptr = *(char *)src_ptr;
1285             break;
1286
1287           case P_STRING:
1288             string_set(dest_ptr,*(char **)src_ptr);
1289             break;
1290
1291           case P_USTRING:
1292             string_set(dest_ptr,*(char **)src_ptr);
1293             strupper(*(char **)dest_ptr);
1294             break;
1295           default:
1296             break;
1297           }
1298       }
1299
1300   if (bcopyall)
1301     {
1302       init_copymap(pserviceDest);
1303       if (pserviceSource->copymap)
1304         memcpy((void *)pserviceDest->copymap,
1305                (void *)pserviceSource->copymap,sizeof(BOOL)*NUMPARAMETERS);
1306     }
1307 }
1308
1309 /***************************************************************************
1310 Check a service for consistency. Return False if the service is in any way
1311 incomplete or faulty, else True.
1312 ***************************************************************************/
1313 static BOOL service_ok(int iService)
1314 {
1315    BOOL bRetval;
1316
1317    bRetval = True;
1318    if (iSERVICE(iService).szService[0] == '\0')
1319    {
1320       DEBUG(0,( "The following message indicates an internal error:\n"));
1321       DEBUG(0,( "No service name in service entry.\n"));
1322       bRetval = False;
1323    }
1324
1325    /* The [printers] entry MUST be printable. I'm all for flexibility, but */
1326    /* I can't see why you'd want a non-printable printer service...        */
1327    if (strwicmp(iSERVICE(iService).szService,PRINTERS_NAME) == 0)
1328       if (!iSERVICE(iService).bPrint_ok)
1329       {
1330          DEBUG(0,( "WARNING: [%s] service MUST be printable!\n",
1331                iSERVICE(iService).szService));
1332          iSERVICE(iService).bPrint_ok = True;
1333       }
1334
1335    if (iSERVICE(iService).szPath[0] == '\0' &&
1336        strwicmp(iSERVICE(iService).szService,HOMES_NAME) != 0)
1337    {
1338       DEBUG(0,("No path in service %s - using %s\n",iSERVICE(iService).szService,tmpdir()));
1339       string_set(&iSERVICE(iService).szPath,tmpdir());      
1340    }
1341
1342    /* If a service is flagged unavailable, log the fact at level 0. */
1343    if (!iSERVICE(iService).bAvailable) 
1344       DEBUG(1,( "NOTE: Service %s is flagged unavailable.\n",
1345             iSERVICE(iService).szService));
1346
1347    return (bRetval);
1348 }
1349
1350 static struct file_lists {
1351   struct file_lists *next;
1352   char *name;
1353   time_t modtime;
1354 } *file_lists = NULL;
1355
1356 /*******************************************************************
1357 keep a linked list of all config files so we know when one has changed 
1358 it's date and needs to be reloaded
1359 ********************************************************************/
1360 static void add_to_file_list(char *fname)
1361 {
1362   struct file_lists *f=file_lists;
1363
1364   while (f) {
1365     if (f->name && !strcmp(f->name,fname)) break;
1366     f = f->next;
1367   }
1368
1369   if (!f) {
1370     f = (struct file_lists *)malloc(sizeof(file_lists[0]));
1371     if (!f) return;
1372     f->next = file_lists;
1373     f->name = strdup(fname);
1374     if (!f->name) {
1375       free(f);
1376       return;
1377     }
1378     file_lists = f;
1379   }
1380
1381   {
1382     pstring n2;
1383     strcpy(n2,fname);
1384     standard_sub_basic(n2);
1385     f->modtime = file_modtime(n2);
1386   }
1387
1388 }
1389
1390 /*******************************************************************
1391 check if a config file has changed date
1392 ********************************************************************/
1393 BOOL lp_file_list_changed(void)
1394 {
1395   struct file_lists *f = file_lists;
1396   DEBUG(6,("lp_file_list_changed()\n"));
1397
1398   while (f)
1399   {
1400     pstring n2;
1401     time_t mod_time;
1402
1403     strcpy(n2,f->name);
1404     standard_sub_basic(n2);
1405
1406     DEBUG(6,("file %s -> %s  last mod_time: %s\n",
1407              f->name, n2, ctime(&f->modtime)));
1408
1409     mod_time = file_modtime(n2);
1410
1411     if (f->modtime != mod_time)
1412     {
1413       DEBUG(6,("file %s modified: %s\n", n2, ctime(&mod_time)));
1414       return(True);
1415     }
1416     f = f->next;   
1417   }
1418   return(False);
1419 }
1420
1421 #ifdef KANJI
1422 /***************************************************************************
1423   handle the interpretation of the coding system parameter
1424   *************************************************************************/
1425 static BOOL handle_coding_system(char *pszParmValue,int *val)
1426 {
1427   *val = interpret_coding_system(pszParmValue,*val);
1428   return(True);
1429 }
1430 #endif /* KANJI */
1431
1432 /***************************************************************************
1433 handle the interpretation of the character set system parameter
1434 ***************************************************************************/
1435 static BOOL handle_character_set(char *pszParmValue,int *val)
1436 {
1437   string_set(&Globals.szCharacterSet,pszParmValue);
1438   *val = interpret_character_set(pszParmValue,*val);
1439   return(True);
1440 }
1441
1442
1443 /***************************************************************************
1444 handle the interpretation of the protocol parameter
1445 ***************************************************************************/
1446 static BOOL handle_protocol(char *pszParmValue,int *val)
1447 {
1448   *val = interpret_protocol(pszParmValue,*val);
1449   return(True);
1450 }
1451
1452 /***************************************************************************
1453 handle the interpretation of the security parameter
1454 ***************************************************************************/
1455 static BOOL handle_security(char *pszParmValue,int *val)
1456 {
1457   *val = interpret_security(pszParmValue,*val);
1458   return(True);
1459 }
1460
1461 /***************************************************************************
1462 handle the interpretation of the default case
1463 ***************************************************************************/
1464 static BOOL handle_case(char *pszParmValue,int *val)
1465 {
1466   if (strnequal(pszParmValue,"LOWER", 5))
1467     *val = CASE_LOWER;
1468   else if (strnequal(pszParmValue,"UPPER", 5))
1469     *val = CASE_UPPER;
1470   return(True);
1471 }
1472
1473 /***************************************************************************
1474 handle the interpretation of the printing system
1475 ***************************************************************************/
1476 static BOOL handle_printing(char *pszParmValue,int *val)
1477 {
1478   if (strnequal(pszParmValue,"sysv", 4))
1479     *val = PRINT_SYSV;
1480   else if (strnequal(pszParmValue,"aix", 3))
1481     *val = PRINT_AIX;
1482   else if (strnequal(pszParmValue,"hpux", 4))
1483     *val = PRINT_HPUX;
1484   else if (strnequal(pszParmValue,"bsd", 3))
1485     *val = PRINT_BSD;
1486   else if (strnequal(pszParmValue,"qnx",3))
1487     *val = PRINT_QNX;
1488   else if (strnequal(pszParmValue,"plp", 3))
1489     *val = PRINT_PLP;
1490   else if (strnequal(pszParmValue,"lprng", 5))
1491     *val = PRINT_LPRNG;
1492   return(True);
1493 }
1494
1495 /***************************************************************************
1496 handle the announce as parameter
1497 ***************************************************************************/
1498 static BOOL handle_announce_as(char *pszParmValue,int *val)
1499 {
1500   if (strnequal(pszParmValue,"NT", 2))
1501     *val = ANNOUNCE_AS_NT;
1502   else if (strnequal(pszParmValue,"win95", 5))
1503     *val = ANNOUNCE_AS_WIN95;
1504   else if (strnequal(pszParmValue,"WfW", 3))
1505     *val = ANNOUNCE_AS_WFW;
1506   return True;
1507
1508
1509 /***************************************************************************
1510 handle the valid chars lines
1511 ***************************************************************************/
1512 static BOOL handle_valid_chars(char *pszParmValue,char **ptr)
1513
1514   string_set(ptr,pszParmValue);
1515
1516   /* A dependency here is that the parameter client code page must be
1517      set before this is called - as calling codepage_initialise()
1518      would overwrite the valid char lines.
1519    */
1520   codepage_initialise(lp_client_code_page());
1521
1522   add_char_string(pszParmValue);
1523   return(True);
1524 }
1525
1526
1527 /***************************************************************************
1528 handle the include operation
1529 ***************************************************************************/
1530 static BOOL handle_include(char *pszParmValue,char **ptr)
1531
1532   pstring fname;
1533   strcpy(fname,pszParmValue);
1534
1535   add_to_file_list(fname);
1536
1537   standard_sub_basic(fname);
1538
1539   string_set(ptr,fname);
1540
1541   if (file_exist(fname,NULL))
1542     return(pm_process(fname, do_section, do_parameter));      
1543
1544   DEBUG(2,("Can't find include file %s\n",fname));
1545
1546   return(False);
1547 }
1548
1549
1550 /***************************************************************************
1551 handle the interpretation of the copy parameter
1552 ***************************************************************************/
1553 static BOOL handle_copy(char *pszParmValue,char **ptr)
1554 {
1555    BOOL bRetval;
1556    int iTemp;
1557    service serviceTemp;
1558
1559    string_set(ptr,pszParmValue);
1560
1561    init_service(&serviceTemp);
1562
1563    bRetval = False;
1564    
1565    DEBUG(3,("Copying service from service %s\n",pszParmValue));
1566
1567    if ((iTemp = getservicebyname(pszParmValue, &serviceTemp)) >= 0)
1568      {
1569        if (iTemp == iServiceIndex)
1570          {
1571            DEBUG(0,("Can't copy service %s - unable to copy self!\n",
1572                     pszParmValue));
1573          }
1574        else
1575          {
1576            copy_service(pSERVICE(iServiceIndex), 
1577                         &serviceTemp,
1578                         iSERVICE(iServiceIndex).copymap);
1579            bRetval = True;
1580          }
1581      }
1582    else
1583      {
1584        DEBUG(0,( "Unable to copy service - source not found: %s\n",
1585                 pszParmValue));
1586        bRetval = False;
1587      }
1588
1589    free_service(&serviceTemp);
1590    return (bRetval);
1591 }
1592
1593
1594 /***************************************************************************
1595 initialise a copymap
1596 ***************************************************************************/
1597 static void init_copymap(service *pservice)
1598 {
1599   int i;
1600   if (pservice->copymap) free(pservice->copymap);
1601   pservice->copymap = (BOOL *)malloc(sizeof(BOOL)*NUMPARAMETERS);
1602   if (!pservice->copymap)
1603     DEBUG(0,("Couldn't allocate copymap!! (size %d)\n",NUMPARAMETERS));
1604
1605   for (i=0;i<NUMPARAMETERS;i++)
1606     pservice->copymap[i] = True;
1607 }
1608
1609
1610 /***************************************************************************
1611 Process a parameter for a particular service number. If snum < 0
1612 then assume we are in the globals
1613 ***************************************************************************/
1614 BOOL lp_do_parameter(int snum, char *pszParmName, char *pszParmValue)
1615 {
1616    int parmnum;
1617    void *parm_ptr=NULL; /* where we are going to store the result */
1618    void *def_ptr=NULL;
1619
1620    parmnum = map_parameter(pszParmName);
1621
1622    if (parmnum < 0)
1623      {
1624        DEBUG(0,( "Ignoring unknown parameter \"%s\"\n", pszParmName));
1625        return(True);
1626      }
1627
1628    def_ptr = parm_table[parmnum].ptr;
1629
1630    /* we might point at a service, the default service or a global */
1631    if (snum < 0) {
1632      parm_ptr = def_ptr;
1633    } else {
1634        if (parm_table[parmnum].class == P_GLOBAL) {
1635            DEBUG(0,( "Global parameter %s found in service section!\n",pszParmName));
1636            return(True);
1637          }
1638        parm_ptr = ((char *)pSERVICE(snum)) + PTR_DIFF(def_ptr,&sDefault);
1639    }
1640
1641    if (snum >= 0) {
1642            int i;
1643            if (!iSERVICE(snum).copymap)
1644                    init_copymap(pSERVICE(snum));
1645            
1646            /* this handles the aliases - set the copymap for other entries with
1647               the same data pointer */
1648            for (i=0;parm_table[i].label;i++)
1649                    if (parm_table[i].ptr == parm_table[parmnum].ptr)
1650                            iSERVICE(snum).copymap[i] = False;
1651    }
1652
1653    /* if it is a special case then go ahead */
1654    if (parm_table[parmnum].special) {
1655            parm_table[parmnum].special(pszParmValue,parm_ptr);
1656            return(True);
1657    }
1658
1659    /* now switch on the type of variable it is */
1660    switch (parm_table[parmnum].type)
1661      {
1662      case P_BOOL:
1663        set_boolean(parm_ptr,pszParmValue);
1664        break;
1665
1666      case P_BOOLREV:
1667        set_boolean(parm_ptr,pszParmValue);
1668        *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
1669        break;
1670
1671      case P_INTEGER:
1672        *(int *)parm_ptr = atoi(pszParmValue);
1673        break;
1674
1675      case P_CHAR:
1676        *(char *)parm_ptr = *pszParmValue;
1677        break;
1678
1679      case P_OCTAL:
1680        sscanf(pszParmValue,"%o",(int *)parm_ptr);
1681        break;
1682
1683      case P_STRING:
1684        string_set(parm_ptr,pszParmValue);
1685        break;
1686
1687      case P_USTRING:
1688        string_set(parm_ptr,pszParmValue);
1689        strupper(*(char **)parm_ptr);
1690        break;
1691
1692      case P_GSTRING:
1693        strcpy((char *)parm_ptr,pszParmValue);
1694        break;
1695
1696      case P_UGSTRING:
1697        strcpy((char *)parm_ptr,pszParmValue);
1698        strupper((char *)parm_ptr);
1699        break;
1700      }
1701
1702    return(True);
1703 }
1704
1705 /***************************************************************************
1706 Process a parameter.
1707 ***************************************************************************/
1708 static BOOL do_parameter(char *pszParmName, char *pszParmValue)
1709 {
1710    if (!bInGlobalSection && bGlobalOnly) return(True);
1711
1712    DEBUG(3,("doing parameter %s = %s\n",pszParmName,pszParmValue));
1713
1714    return lp_do_parameter(bInGlobalSection?-2:iServiceIndex, pszParmName, pszParmValue);
1715 }
1716
1717
1718 /***************************************************************************
1719 print a parameter of the specified type
1720 ***************************************************************************/
1721 static void print_parameter(parm_type type,void *ptr, FILE *f)
1722 {
1723   switch (type)
1724     {
1725     case P_BOOL:
1726       fprintf(f,"%s",BOOLSTR(*(BOOL *)ptr));
1727       break;
1728       
1729     case P_BOOLREV:
1730       fprintf(f,"%s",BOOLSTR(! *(BOOL *)ptr));
1731       break;
1732       
1733     case P_INTEGER:
1734       fprintf(f,"%d",*(int *)ptr);
1735       break;
1736       
1737     case P_CHAR:
1738       fprintf(f,"%c",*(char *)ptr);
1739       break;
1740       
1741     case P_OCTAL:
1742       fprintf(f,"0%o",*(int *)ptr);
1743       break;
1744       
1745     case P_GSTRING:
1746     case P_UGSTRING:
1747       if ((char *)ptr)
1748         fprintf(f,"%s",(char *)ptr);
1749       break;
1750
1751     case P_STRING:
1752     case P_USTRING:
1753       if (*(char **)ptr)
1754         fprintf(f,"%s",*(char **)ptr);
1755       break;
1756     }
1757 }
1758
1759
1760 /***************************************************************************
1761 print a parameter of the specified type
1762 ***************************************************************************/
1763 static void parameter_string(parm_type type,void *ptr,char *s)
1764 {
1765         s[0] = 0;
1766         
1767         switch (type)
1768                 {
1769                 case P_BOOL:
1770                         sprintf(s, "%s",BOOLSTR(*(BOOL *)ptr));
1771                         break;
1772                         
1773                 case P_BOOLREV:
1774                         sprintf(s, "%s",BOOLSTR(! *(BOOL *)ptr));
1775                         break;
1776                         
1777                 case P_INTEGER:
1778                         sprintf(s, "%d",*(int *)ptr);
1779                         break;
1780                         
1781                 case P_CHAR:
1782                         sprintf(s, "%c",*(char *)ptr);
1783                         break;
1784                         
1785                 case P_OCTAL:
1786                         sprintf(s, "0%o",*(int *)ptr);
1787                         break;
1788                         
1789                 case P_GSTRING:
1790                 case P_UGSTRING:
1791                         if ((char *)ptr)
1792                                 sprintf(s, "%s",(char *)ptr);
1793                         break;
1794                         
1795                 case P_STRING:
1796                 case P_USTRING:
1797                         if (*(char **)ptr)
1798                                 sprintf(s, "%s",*(char **)ptr);
1799                         break;
1800                 }
1801 }
1802
1803
1804 /***************************************************************************
1805 check if two parameters are equal
1806 ***************************************************************************/
1807 static BOOL equal_parameter(parm_type type,void *ptr1,void *ptr2)
1808 {
1809   switch (type)
1810     {
1811     case P_BOOL:
1812     case P_BOOLREV:
1813       return(*((BOOL *)ptr1) == *((BOOL *)ptr2));
1814
1815     case P_INTEGER:
1816     case P_OCTAL:
1817       return(*((int *)ptr1) == *((int *)ptr2));
1818       
1819     case P_CHAR:
1820       return(*((char *)ptr1) == *((char *)ptr2));
1821
1822     case P_GSTRING:
1823     case P_UGSTRING:
1824       {
1825         char *p1 = (char *)ptr1, *p2 = (char *)ptr2;
1826         if (p1 && !*p1) p1 = NULL;
1827         if (p2 && !*p2) p2 = NULL;
1828         return(p1==p2 || strequal(p1,p2));
1829       }
1830     case P_STRING:
1831     case P_USTRING:
1832       {
1833         char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2;
1834         if (p1 && !*p1) p1 = NULL;
1835         if (p2 && !*p2) p2 = NULL;
1836         return(p1==p2 || strequal(p1,p2));
1837       }
1838     }
1839   return(False);
1840 }
1841
1842 /***************************************************************************
1843 Process a new section (service). At this stage all sections are services.
1844 Later we'll have special sections that permit server parameters to be set.
1845 Returns True on success, False on failure.
1846 ***************************************************************************/
1847 static BOOL do_section(char *pszSectionName)
1848 {
1849    BOOL bRetval;
1850    BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) || 
1851                     (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
1852    bRetval = False;
1853
1854    /* if we were in a global section then do the local inits */
1855    if (bInGlobalSection && !isglobal)
1856      init_locals();
1857
1858    /* if we've just struck a global section, note the fact. */
1859    bInGlobalSection = isglobal;   
1860
1861    /* check for multiple global sections */
1862    if (bInGlobalSection)
1863    {
1864      DEBUG(3,( "Processing section \"[%s]\"\n", pszSectionName));
1865      return(True);
1866    }
1867
1868    if (!bInGlobalSection && bGlobalOnly) return(True);
1869
1870    /* if we have a current service, tidy it up before moving on */
1871    bRetval = True;
1872
1873    if (iServiceIndex >= 0)
1874      bRetval = service_ok(iServiceIndex);
1875
1876    /* if all is still well, move to the next record in the services array */
1877    if (bRetval)
1878      {
1879        /* We put this here to avoid an odd message order if messages are */
1880        /* issued by the post-processing of a previous section. */
1881        DEBUG(2,( "Processing section \"[%s]\"\n", pszSectionName));
1882
1883        if ((iServiceIndex=add_a_service(&sDefault,pszSectionName)) < 0)
1884          {
1885            DEBUG(0,("Failed to add a new service\n"));
1886            return(False);
1887          }
1888      }
1889
1890    return (bRetval);
1891 }
1892
1893 /***************************************************************************
1894 Display the contents of the global structure.
1895 ***************************************************************************/
1896 static void dump_globals(FILE *f)
1897 {
1898   int i;
1899   fprintf(f, "# Global parameters\n");
1900
1901   for (i=0;parm_table[i].label;i++)
1902     if (parm_table[i].class == P_GLOBAL &&
1903         parm_table[i].ptr &&
1904         (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr)))
1905       {
1906         fprintf(f,"\t%s = ",parm_table[i].label);
1907         print_parameter(parm_table[i].type,parm_table[i].ptr, f);
1908         fprintf(f,"\n");
1909       }
1910 }
1911
1912 /***************************************************************************
1913 Display the contents of a single services record.
1914 ***************************************************************************/
1915 static void dump_a_service(service *pService, FILE *f)
1916 {
1917   int i;
1918   if (pService == &sDefault)
1919     fprintf(f,"\n\n# Default service parameters\n");
1920   else
1921     fprintf(f,"\n[%s]\n",pService->szService);
1922
1923   for (i=0;parm_table[i].label;i++)
1924     if (parm_table[i].class == P_LOCAL &&
1925         parm_table[i].ptr && 
1926         (*parm_table[i].label != '-') &&
1927         (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr)))
1928       {
1929         int pdiff = PTR_DIFF(parm_table[i].ptr,&sDefault);
1930
1931         if (pService == &sDefault || !equal_parameter(parm_table[i].type,
1932                                                       ((char *)pService) + pdiff,
1933                                                       ((char *)&sDefault) + pdiff))
1934           {
1935             fprintf(f,"\t%s = ",parm_table[i].label);
1936             print_parameter(parm_table[i].type,
1937                             ((char *)pService) + pdiff, f);
1938             fprintf(f,"\n");
1939           }
1940       }
1941 }
1942
1943
1944 /***************************************************************************
1945 return info about the next service  in a service. snum==-1 gives the default
1946 serice and snum==-2 gives the globals
1947
1948 return 0 when out of parameters
1949 ***************************************************************************/
1950 int lp_next_parameter(int snum, int *i, char *label, 
1951                            char *value, int allparameters)
1952 {
1953         if (snum == -2) {
1954                 /* do the globals */
1955                 for (;parm_table[*i].label;(*i)++)
1956                         if (parm_table[*i].class == P_GLOBAL &&
1957                             parm_table[*i].ptr && 
1958                             (*parm_table[*i].label != '-') &&
1959                             ((*i) == 0 || 
1960                              (parm_table[*i].ptr != parm_table[(*i)-1].ptr))) {
1961                                 strcpy(label, parm_table[*i].label);
1962                                 parameter_string(parm_table[*i].type,
1963                                                  parm_table[*i].ptr,
1964                                                  value);
1965                                 (*i)++;
1966                                 return 1;
1967                         }
1968                 return 0;
1969         } else {
1970                 service *pService = (snum==-1?&sDefault:pSERVICE(snum));
1971
1972                 for (;parm_table[*i].label;(*i)++)
1973                         if (parm_table[*i].class == P_LOCAL &&
1974                             parm_table[*i].ptr && 
1975                             (*parm_table[*i].label != '-') &&
1976                             ((*i) == 0 || 
1977                              (parm_table[*i].ptr != parm_table[(*i)-1].ptr))) {
1978                                 int pdiff = PTR_DIFF(parm_table[*i].ptr,&sDefault);
1979                                 
1980                                 if (snum == -1 || allparameters ||
1981                                     !equal_parameter(parm_table[*i].type,
1982                                                      ((char *)pService) + pdiff,
1983                                                      ((char *)&sDefault) + pdiff)) {
1984                                         strcpy(label, parm_table[*i].label);
1985                                         parameter_string(parm_table[*i].type,
1986                                                          ((char *)pService) + pdiff,
1987                                                          value);
1988                                         (*i)++;
1989                                         return 1;
1990                                 }
1991                         }
1992         }
1993
1994   return 0;
1995 }
1996
1997
1998 #if 0
1999 /***************************************************************************
2000 Display the contents of a single copy structure.
2001 ***************************************************************************/
2002 static void dump_copy_map(BOOL *pcopymap)
2003 {
2004   int i;
2005   if (!pcopymap) return;
2006
2007   printf("\n\tNon-Copied parameters:\n");
2008
2009   for (i=0;parm_table[i].label;i++)
2010     if (parm_table[i].class == P_LOCAL &&
2011         parm_table[i].ptr && !pcopymap[i] &&
2012         (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr)))
2013       {
2014         printf("\t\t%s\n",parm_table[i].label);
2015       }
2016 }
2017 #endif
2018
2019 /***************************************************************************
2020 Return TRUE if the passed service number is within range.
2021 ***************************************************************************/
2022 BOOL lp_snum_ok(int iService)
2023 {
2024    return (LP_SNUM_OK(iService) && iSERVICE(iService).bAvailable);
2025 }
2026
2027
2028 /***************************************************************************
2029 auto-load some homes and printer services
2030 ***************************************************************************/
2031 static void lp_add_auto_services(char *str)
2032 {
2033   char *s;
2034   char *p;
2035   int homes = lp_servicenumber(HOMES_NAME);
2036   int printers = lp_servicenumber(PRINTERS_NAME);
2037
2038   if (!str)
2039     return;
2040
2041   s = strdup(str);
2042   if (!s) return;
2043
2044   for (p=strtok(s,LIST_SEP);p;p=strtok(NULL,LIST_SEP))
2045     {
2046       char *home = get_home_dir(p);
2047
2048       if (lp_servicenumber(p) >= 0) continue;
2049
2050       if (home && homes >= 0)
2051         {
2052           lp_add_home(p,homes,home);
2053           continue;
2054         }
2055
2056       if (printers >= 0 && pcap_printername_ok(p,NULL))
2057         lp_add_printer(p,printers);
2058     }
2059   free(s);
2060 }
2061
2062 /***************************************************************************
2063 auto-load one printer
2064 ***************************************************************************/
2065 static void lp_add_one_printer(char *name,char *comment)
2066 {
2067   int printers = lp_servicenumber(PRINTERS_NAME);
2068   int i;
2069
2070   if (lp_servicenumber(name) < 0)
2071     {
2072       lp_add_printer(name,printers);
2073       if ((i=lp_servicenumber(name)) >= 0)
2074         string_set(&iSERVICE(i).comment,comment);
2075     }      
2076 }
2077
2078
2079 /***************************************************************************
2080 auto-load printer services
2081 ***************************************************************************/
2082 static void lp_add_all_printers(void)
2083 {
2084   int printers = lp_servicenumber(PRINTERS_NAME);
2085
2086   if (printers < 0) return;
2087
2088   pcap_printer_fn(lp_add_one_printer);
2089 }
2090
2091 /***************************************************************************
2092 have we loaded a services file yet?
2093 ***************************************************************************/
2094 BOOL lp_loaded(void)
2095 {
2096   return(bLoaded);
2097 }
2098
2099 /***************************************************************************
2100 unload unused services
2101 ***************************************************************************/
2102 void lp_killunused(BOOL (*snumused)(int ))
2103 {
2104   int i;
2105   for (i=0;i<iNumServices;i++)
2106     if (VALID(i) && (!snumused || !snumused(i)))
2107       {
2108         iSERVICE(i).valid = False;
2109         free_service(pSERVICE(i));
2110       }
2111 }
2112
2113 /***************************************************************************
2114 Load the services array from the services file. Return True on success, 
2115 False on failure.
2116 ***************************************************************************/
2117 BOOL lp_load(char *pszFname,BOOL global_only)
2118 {
2119   pstring n2;
2120   BOOL bRetval;
2121  
2122   add_to_file_list(pszFname);
2123
2124   bRetval = False;
2125
2126   bInGlobalSection = True;
2127   bGlobalOnly = global_only;
2128   
2129   init_globals();
2130   
2131   strcpy(n2,pszFname);
2132   standard_sub_basic(n2);
2133
2134   /* We get sections first, so have to start 'behind' to make up */
2135   iServiceIndex = -1;
2136   bRetval = pm_process(n2, do_section, do_parameter);
2137   
2138   /* finish up the last section */
2139   DEBUG(3,("pm_process() returned %s\n", BOOLSTR(bRetval)));
2140   if (bRetval)
2141     if (iServiceIndex >= 0)
2142       bRetval = service_ok(iServiceIndex);         
2143
2144   lp_add_auto_services(lp_auto_services());
2145   if (lp_load_printers())
2146     lp_add_all_printers();
2147
2148   lp_add_ipc();
2149
2150   set_default_server_announce_type();
2151
2152   bLoaded = True;
2153
2154   return (bRetval);
2155 }
2156
2157
2158 /***************************************************************************
2159 return the max number of services
2160 ***************************************************************************/
2161 int lp_numservices(void)
2162 {
2163   return(iNumServices);
2164 }
2165
2166 /***************************************************************************
2167 Display the contents of the services array in human-readable form.
2168 ***************************************************************************/
2169 void lp_dump(FILE *f)
2170 {
2171    int iService;
2172
2173    dump_globals(f);
2174    
2175    dump_a_service(&sDefault, f);
2176
2177    for (iService = 0; iService < iNumServices; iService++)
2178    {
2179      if (VALID(iService))
2180        {
2181          if (iSERVICE(iService).szService[0] == '\0')
2182            break;
2183          dump_a_service(pSERVICE(iService), f);
2184        }
2185    }
2186 }
2187
2188
2189 /***************************************************************************
2190 Return the number of the service with the given name, or -1 if it doesn't
2191 exist. Note that this is a DIFFERENT ANIMAL from the internal function
2192 getservicebyname()! This works ONLY if all services have been loaded, and
2193 does not copy the found service.
2194 ***************************************************************************/
2195 int lp_servicenumber(char *pszServiceName)
2196 {
2197    int iService;
2198
2199    for (iService = iNumServices - 1; iService >= 0; iService--)
2200       if (VALID(iService) &&
2201           strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) 
2202          break;
2203
2204    if (iService < 0)
2205      DEBUG(7,("lp_servicenumber: couldn't find %s\n",pszServiceName));
2206    
2207    return (iService);
2208 }
2209
2210 /*******************************************************************
2211   a useful volume label function
2212   ******************************************************************/
2213 char *volume_label(int snum)
2214 {
2215   char *ret = lp_volume(snum);
2216   if (!*ret) return(lp_servicename(snum));
2217   return(ret);
2218 }
2219
2220 #if 0
2221 /*
2222  * nmbd only loads the global section. There seems to be no way to
2223  * determine exactly is a service is printable by only looking at the
2224  * [global] section so for now always announce as a print server. This
2225  * will need looking at in the future. Jeremy (jallison@whistle.com).
2226  */
2227 /*******************************************************************
2228  Return true if any printer services are defined.
2229   ******************************************************************/
2230 static BOOL lp_printer_services(void)
2231 {
2232   int iService;
2233
2234   for (iService = iNumServices - 1; iService >= 0; iService--)
2235       if (VALID(iService) && iSERVICE(iService).bPrint_ok)
2236           return True;
2237   return False;
2238 }
2239 #endif
2240
2241 /*******************************************************************
2242  Set the server type we will announce as via nmbd.
2243 ********************************************************************/
2244 static void set_default_server_announce_type()
2245 {
2246   default_server_announce = (SV_TYPE_WORKSTATION | SV_TYPE_SERVER |
2247                               SV_TYPE_SERVER_UNIX | SV_TYPE_PRINTQ_SERVER);
2248   if(lp_announce_as() == ANNOUNCE_AS_NT)
2249     default_server_announce |= (SV_TYPE_SERVER_NT | SV_TYPE_NT);
2250   else if(lp_announce_as() == ANNOUNCE_AS_WIN95)
2251     default_server_announce |= SV_TYPE_WIN95_PLUS;
2252   else if(lp_announce_as() == ANNOUNCE_AS_WFW)
2253     default_server_announce |= SV_TYPE_WFW;
2254   default_server_announce |= (lp_time_server() ? SV_TYPE_TIME_SOURCE : 0);
2255 /*
2256  * nmbd only loads the [global] section. There seems to be no way to
2257  * determine exactly if any service is printable by only looking at the
2258  * [global] section so for now always announce as a print server. This
2259  * will need looking at in the future. Jeremy (jallison@whistle.com).
2260  */
2261 #if 0
2262   default_server_announce |= (lp_printer_services() ? SV_TYPE_PRINTQ_SERVER : 0);
2263 #endif
2264 }
2265
2266
2267 /*******************************************************************
2268 rename a service
2269 ********************************************************************/
2270 void lp_rename_service(int snum, char *new_name)
2271 {
2272         string_set(&pSERVICE(snum)->szService, new_name);
2273 }
2274
2275 /*******************************************************************
2276 remove a service
2277 ********************************************************************/
2278 void lp_remove_service(int snum)
2279 {
2280         pSERVICE(snum)->valid = False;
2281 }
2282
2283 /*******************************************************************
2284 copy a service
2285 ********************************************************************/
2286 void lp_copy_service(int snum, char *new_name)
2287 {
2288         char *oldname = lp_servicename(snum);
2289         do_section(new_name);
2290         if (snum >= 0) {
2291                 snum = lp_servicenumber(new_name);
2292                 if (snum >= 0)
2293                         lp_do_parameter(snum, "copy", oldname);
2294         }
2295 }
2296
2297
2298 /*******************************************************************
2299  Get the default server type we will announce as via nmbd.
2300 ********************************************************************/
2301 int lp_default_server_announce(void)
2302 {
2303   return default_server_announce;
2304 }
2305
2306 /*******************************************************************
2307  Split the announce version into major and minor numbers.
2308 ********************************************************************/
2309 int lp_major_announce_version(void)
2310 {
2311   static BOOL got_major = False;
2312   static int major_version = DEFAULT_MAJOR_VERSION;
2313   char *vers;
2314   char *p;
2315
2316   if(got_major)
2317     return major_version;
2318
2319   got_major = True;
2320   if((vers = lp_announce_version()) == NULL)
2321     return major_version;
2322   
2323   if((p = strchr(vers, '.')) == 0)
2324     return major_version;
2325
2326   *p = '\0';
2327   major_version = atoi(vers);
2328   return major_version;
2329 }
2330
2331 int lp_minor_announce_version(void)
2332 {
2333   static BOOL got_minor = False;
2334   static int minor_version = DEFAULT_MINOR_VERSION;
2335   char *vers;
2336   char *p;
2337
2338   if(got_minor)
2339     return minor_version;
2340
2341   got_minor = True;
2342   if((vers = lp_announce_version()) == NULL)
2343     return minor_version;
2344   
2345   if((p = strchr(vers, '.')) == 0)              
2346     return minor_version;
2347     
2348   p++;
2349   minor_version = atoi(p);
2350   return minor_version;
2351 }  
2352