2 /***************************************************************************
4 ****************************************************************************
5 * ncurses is copyright (C) 1992-1995 *
7 * zmbenhal@netcom.com *
9 * esr@snark.thyrsus.com *
11 * Permission is hereby granted to reproduce and distribute ncurses *
12 * by any means and for any fee, whether alone or as part of a *
13 * larger distribution, in source or in binary form, PROVIDED *
14 * this notice is included with any such distribution, and is not *
15 * removed from any of its header files. Mention of ncurses in any *
16 * applications linked with it is highly appreciated. *
18 * ncurses comes AS IS with no warranty, implied or expressed. *
20 ***************************************************************************/
25 * write_entry.c -- write a terminfo structure onto the file system
28 #include <curses.priv.h>
34 #include <term_entry.h>
37 #define S_ISDIR(mode) ((mode & S_IFMT) == S_IFDIR)
40 MODULE_ID("$Id: write_entry.c,v 1.16 1997/05/10 17:33:12 tom Exp $")
42 static int total_written;
44 static int write_object(FILE *, TERMTYPE *);
47 * make_directory(char *path)
49 * Make a directory if it doesn't exist.
51 static int make_directory(const char *path)
55 char fullpath[PATH_MAX];
56 const char *destination = _nc_tic_dir(0);
58 if (path == destination || *path == '/')
59 (void)strcpy(fullpath, path);
61 (void)sprintf(fullpath, "%s/%s", destination, path);
63 if ((rc = stat(path, &statbuf)) < 0) {
64 rc = mkdir(path, 0777);
66 if (access(path, R_OK|W_OK|X_OK) < 0) {
67 _nc_err_abort("%s: permission denied", fullpath);
68 } else if (!(S_ISDIR(statbuf.st_mode))) {
69 _nc_err_abort("%s: not a directory", fullpath);
75 void _nc_set_writedir(char *dir)
76 /* set the write directory for compiled entries */
78 const char *destination;
81 (void) _nc_tic_dir(dir);
82 else if (getenv("TERMINFO") != NULL)
83 (void) _nc_tic_dir(getenv("TERMINFO"));
85 destination = _nc_tic_dir(0);
86 if (make_directory(destination) < 0)
90 /* ncurses extension...fall back on user's private directory */
91 if ((home = getenv("HOME")) != (char *)NULL)
93 char *temp = malloc(sizeof(PRIVATE_INFO) + strlen(home));
94 (void) sprintf(temp, PRIVATE_INFO, home);
97 if (make_directory(destination) < 0)
98 _nc_err_abort("%s: permission denied (errno %d)",
104 * Note: because of this code, this logic should be exercised
105 * *once only* per run.
107 if (chdir(_nc_tic_dir(destination)) < 0)
108 _nc_err_abort("%s: not a directory", destination);
112 * check_writeable(char code)
114 * Miscellaneous initialisations
116 * Check for access rights to destination directories
117 * Create any directories which don't exist.
121 static void check_writeable(int code)
123 static const char dirnames[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
124 static bool verified[sizeof(dirnames)];
129 if (code == 0 || (s = strchr(dirnames, code)) == 0)
130 _nc_err_abort("Illegal terminfo subdirectory \"%c\"", code);
132 if (verified[s-dirnames])
137 (void) make_directory(dir);
139 verified[s-dirnames] = TRUE;
145 * Save the compiled version of a description in the filesystem.
147 * make a copy of the name-list
148 * break it up into first-name and all-but-last-name
150 * write object information to first-name
152 * for each name in all-but-last-name
155 * Using 'time()' to obtain a reference for file timestamps is unreliable,
156 * e.g., with NFS, because the filesystem may have a different time
157 * reference. We check for pre-existence of links by latching the first
158 * timestamp from a file that we create.
160 * The _nc_warning() calls will report a correct line number only if
161 * _nc_curr_line is properly set before the write_entry() call.
164 void _nc_write_entry(TERMTYPE *const tp)
168 char name_list[MAX_TERMINFO_LENGTH];
169 char *first_name, *other_names;
171 char filename[PATH_MAX];
172 char linkname[PATH_MAX];
174 char symlinkname[PATH_MAX];
175 #endif /* USE_SYMLINKS */
176 static int call_count;
177 static time_t start_time; /* time at start of writes */
179 if (call_count++ == 0) {
183 (void) strcpy(name_list, tp->term_names);
184 DEBUG(7, ("Name list = '%s'", name_list));
186 first_name = name_list;
188 ptr = &name_list[strlen(name_list) - 1];
189 other_names = ptr + 1;
191 while (ptr > name_list && *ptr != '|')
194 if (ptr != name_list) {
197 for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++)
204 other_names = ptr + 1;
208 DEBUG(7, ("First name = '%s'", first_name));
209 DEBUG(7, ("Other names = '%s'", other_names));
211 _nc_set_type(first_name);
213 if (strlen(first_name) > sizeof(filename)-3)
214 _nc_warning("terminal name too long.");
216 sprintf(filename, "%c/%s", first_name[0], first_name);
219 * Has this primary name been written since the first call to
220 * write_entry()? If so, the newer write will step on the older,
223 if (start_time > 0 &&
224 stat(filename, &statbuf) >= 0
225 && statbuf.st_mtime >= start_time)
227 _nc_warning("name multiply defined.");
230 check_writeable(first_name[0]);
231 fp = fopen(filename, "w");
234 _nc_syserr_abort("can't open %s/%s", _nc_tic_dir(0), filename);
236 DEBUG(1, ("Created %s", filename));
238 if (write_object(fp, tp) == ERR) {
239 _nc_syserr_abort("error writing %s/%s", _nc_tic_dir(0), filename);
243 if (start_time == 0) {
244 if (stat(filename, &statbuf) < 0
245 || (start_time = statbuf.st_mtime) == 0) {
246 _nc_syserr_abort("error obtaining time from %s/%s",
247 _nc_tic_dir(0), filename);
250 while (*other_names != '\0') {
252 while (*other_names != '|' && *other_names != '\0')
255 if (*other_names != '\0')
256 *(other_names++) = '\0';
258 if (strlen(ptr) > sizeof(linkname)-3) {
259 _nc_warning("terminal alias %s too long.", ptr);
263 check_writeable(ptr[0]);
264 sprintf(linkname, "%c/%s", ptr[0], ptr);
266 if (strcmp(filename, linkname) == 0) {
267 _nc_warning("self-synonym ignored");
269 else if (stat(linkname, &statbuf) >= 0 &&
270 statbuf.st_mtime < start_time)
272 _nc_warning("alias %s multiply defined.", ptr);
277 strcpy(symlinkname, "../");
278 strcat(symlinkname, filename);
279 #endif /* USE_SYMLINKS */
282 if (symlink(symlinkname, linkname) < 0)
284 if (link(filename, linkname) < 0)
285 #endif /* USE_SYMLINKS */
286 _nc_syserr_abort("can't link %s to %s", filename, linkname);
287 DEBUG(1, ("Linked %s", linkname));
292 #undef LITTLE_ENDIAN /* BSD/OS defines this as a feature macro */
293 #define HI(x) ((x) / 256)
294 #define LO(x) ((x) % 256)
295 #define LITTLE_ENDIAN(p, x) (p)[0] = LO(x), (p)[1] = HI(x)
297 static int write_object(FILE *fp, TERMTYPE *tp)
300 size_t namelen, boolmax, nummax, strmax;
304 short offsets[STRCOUNT];
305 unsigned char buf[MAX_ENTRY_SIZE];
307 namelist = tp->term_names;
308 namelen = strlen(namelist) + 1;
311 for (i = 0; i < BOOLWRITE; i++)
316 for (i = 0; i < NUMWRITE; i++)
317 if (tp->Numbers[i] != ABSENT_NUMERIC)
321 for (i = 0; i < STRWRITE; i++)
322 if (tp->Strings[i] != ABSENT_STRING)
326 for (i = 0; i < strmax; i++)
327 if (tp->Strings[i] == ABSENT_STRING)
329 else if (tp->Strings[i] == CANCELLED_STRING)
333 offsets[i] = nextfree;
334 nextfree += strlen(tp->Strings[i]) + 1;
337 /* fill in the header */
338 LITTLE_ENDIAN(buf, MAGIC);
339 LITTLE_ENDIAN(buf+2, min(namelen, MAX_NAME_SIZE + 1));
340 LITTLE_ENDIAN(buf+4, boolmax);
341 LITTLE_ENDIAN(buf+6, nummax);
342 LITTLE_ENDIAN(buf+8, strmax);
343 LITTLE_ENDIAN(buf+10, nextfree);
345 /* write out the header */
346 if (fwrite(buf, 12, 1, fp) != 1
347 || fwrite(namelist, sizeof(char), (size_t)namelen, fp) != namelen
348 || fwrite(tp->Booleans, sizeof(char), (size_t)boolmax, fp) != boolmax)
351 /* the even-boundary padding byte */
352 if ((namelen+boolmax) % 2 != 0 && fwrite(&zero, sizeof(char), 1, fp) != 1)
356 (void) fprintf(stderr, "Numerics begin at %04lx\n", ftell(fp));
357 #endif /* SHOWOFFSET */
360 for (i = 0; i < nummax; i++)
362 if (tp->Numbers[i] == -1) /* HI/LO won't work */
363 buf[2*i] = buf[2*i + 1] = 0377;
365 LITTLE_ENDIAN(buf + 2*i, tp->Numbers[i]);
367 if (fwrite(buf, 2, (size_t)nummax, fp) != nummax)
371 (void) fprintf(stderr, "String offets begin at %04lx\n", ftell(fp));
372 #endif /* SHOWOFFSET */
374 /* the string offsets */
375 for (i = 0; i < strmax; i++)
376 if (offsets[i] == -1) /* HI/LO won't work */
377 buf[2*i] = buf[2*i + 1] = 0377;
378 else if (offsets[i] == -2) /* HI/LO won't work */
384 LITTLE_ENDIAN(buf + 2*i, offsets[i]);
385 if (fwrite(buf, 2, (size_t)strmax, fp) != strmax)
389 (void) fprintf(stderr, "String table begins at %04lx\n", ftell(fp));
390 #endif /* SHOWOFFSET */
393 for (i = 0; i < strmax; i++)
394 if (tp->Strings[i] != ABSENT_STRING && tp->Strings[i] != CANCELLED_STRING)
395 if (fwrite(tp->Strings[i], sizeof(char), strlen(tp->Strings[i]) + 1, fp) != strlen(tp->Strings[i]) + 1)
403 * Returns the total number of entries written by this process
405 int _nc_tic_written(void)
407 return total_written;