2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
8 * Various useful functions for the CVS support code.
19 #if !defined HAVE_NANOSLEEP && !defined HAVE_USLEEP && defined HAVE_SELECT
20 /* use select as a workaround */
22 #endif /* !defined HAVE_NANOSLEEP && !defined HAVE_USLEEP && defined HAVE_SELECT */
24 extern char *getlogin (void);
28 /* *STRPTR is a pointer returned from malloc (or NULL), pointing to *N
29 characters of space. Reallocate it so that points to at least
30 NEWSIZE bytes of space. Gives a fatal error if out of memory;
31 if it returns it was successful. */
33 expand_string (char **strptr, size_t *n, size_t newsize)
36 *strptr = x2realloc (*strptr, n);
41 /* *STR is a pointer to a malloc'd string or NULL. *LENP is its allocated
42 * length. If *STR is NULL then *LENP must be 0 and visa-versa.
43 * Add SRC to the end of *STR, reallocating *STR if necessary. */
45 xrealloc_and_strcat (char **str, size_t *lenp, const char *src)
47 short newstr = !*lenp;
48 expand_string (str, lenp, (newstr ? 0 : strlen (*str)) + strlen (src) + 1);
57 /* Remove trailing newlines from STRING, destructively.
61 * True if any newlines were removed, false otherwise.
64 strip_trailing_newlines (char *str)
66 size_t index, origlen;
67 index = origlen = strlen (str);
69 while (index > 0 && str[index-1] == '\n')
72 return index != origlen;
77 /* Return the number of levels that PATH ascends above where it starts.
81 * "foo/../../bar" -> 1
84 pathname_levels (const char *p)
89 if (p == NULL) return 0;
95 /* Now look for pathname level-ups. */
96 if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || ISSLASH (p[2])))
99 if (-level > max_level)
102 else if (p[0] == '\0' || ISSLASH (p[0]) ||
103 (p[0] == '.' && (p[1] == '\0' || ISSLASH (p[1]))))
108 /* q = strchr (p, '/'); but sub ISSLASH() for '/': */
109 while (*p != '\0' && !ISSLASH (*p)) p++;
111 } while (*p != '\0');
117 /* Free a vector, where (*ARGV)[0], (*ARGV)[1], ... (*ARGV)[*PARGC - 1]
118 are malloc'd and so is *ARGV itself. Such a vector is allocated by
119 line2argv or expand_wild, for example. */
121 free_names (int *pargc, char **argv)
125 for (i = 0; i < *pargc; i++)
126 { /* only do through *pargc */
130 *pargc = 0; /* and set it to zero when done */
133 /* Convert LINE into arguments separated by SEPCHARS. Set *ARGC
134 to the number of arguments found, and (*ARGV)[0] to the first argument,
135 (*ARGV)[1] to the second, etc. *ARGV is malloc'd and so are each of
136 (*ARGV)[0], (*ARGV)[1], ... Use free_names() to return the memory
137 allocated here back to the free pool. */
139 line2argv (int *pargc, char ***argv, char *line, char *sepchars)
142 /* Could make a case for size_t or some other unsigned type, but
143 we'll stick with int to avoid signed/unsigned warnings when
144 comparing with *pargc. */
147 /* Small for testing. */
149 *argv = (char **) xmalloc (argv_allocated * sizeof (**argv));
152 for (cp = strtok (line, sepchars); cp; cp = strtok ((char *) NULL, sepchars))
154 if (*pargc == argv_allocated)
157 *argv = xrealloc (*argv, argv_allocated * sizeof (**argv));
159 (*argv)[*pargc] = xstrdup (cp);
165 * Returns the number of dots ('.') found in an RCS revision number
168 numdots (const char *s)
180 /* Compare revision numbers REV1 and REV2 by consecutive fields.
181 Return negative, zero, or positive in the manner of strcmp. The
182 two revision numbers must have the same number of fields, or else
183 compare_revnums will return an inaccurate result. */
185 compare_revnums (const char *rev1, const char *rev2)
195 result = strtoul (sp, &snext, 10) - strtoul (tp, &tnext, 10);
196 if (*snext == '\0' || *tnext == '\0')
205 /* Increment a revision number. Working on the string is a bit awkward,
206 but it avoid problems with integer overflow should the revision numbers
209 increment_revnum (const char *rev)
213 size_t len = strlen (rev);
215 newrev = xmalloc (len + 2);
216 memcpy (newrev, rev, len + 1);
217 for (p = newrev + len; p != newrev; )
232 /* The number was all 9s, so change the first character to 1 and add
241 /* Return the username by which the caller should be identified in
242 CVS, in contexts such as the author field of RCS files, various
247 #ifndef SYSTEM_GETCALLER
253 /* If there is a CVS username, return it. */
254 #ifdef AUTH_SERVER_SUPPORT
255 if (CVS_Username != NULL)
259 #ifdef SYSTEM_GETCALLER
260 return SYSTEM_GETCALLER ();
262 /* Get the caller's login from his uid. If the real uid is "root"
263 try LOGNAME USER or getlogin(). If getlogin() and getpwuid()
264 both fail, return the uid as a string. */
270 if (uid == (uid_t) 0)
274 /* super-user; try getlogin() to distinguish */
275 if (((name = getlogin ()) || (name = getenv("LOGNAME")) ||
276 (name = getenv("USER"))) && *name)
278 cache = xstrdup (name);
282 if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
286 (void) sprintf (uidname, "uid%lu", (unsigned long) uid);
287 cache = xstrdup (uidname);
290 cache = xstrdup (pw->pw_name);
299 get_date( char *date, struct timeb *now )
310 /* Given some revision, REV, return the first prior revision that exists in the
317 * RCS The RCS node pointer.
318 * REV An existing revision in the RCS file referred to by RCS.
321 * The first prior revision that exists in the RCS file, or NULL if no prior
322 * revision exists. The caller is responsible for disposing of this string.
325 * This function currently neglects the case where we are on the trunk with
326 * rev = X.1, where X != 1. If rev = X.Y, where X != 1 and Y > 1, then this
327 * function should work fine, as revision X.1 must exist, due to RCS rules.
330 previous_rev (rcs, rev)
335 char *tmp = xstrdup (rev);
339 /* Our retval can have no more digits and dots than our input revision. */
340 retval = xmalloc (strlen (rev) + 1);
341 p = strrchr (tmp, '.');
343 r1 = strtol (p+1, NULL, 10);
347 /* If r1 == 0, then we must be on a branch and our parent must
348 * exist, or we must be on the trunk with a REV like X.1.
349 * We are neglecting the X.1 with X != 1 case by assuming that
350 * there is no previous revision when we discover we were on
353 p = strrchr (tmp, '.');
355 /* We are on the trunk. */
360 sprintf (retval, "%s", tmp);
364 sprintf (retval, "%s.%ld", tmp, r1);
365 } while (!RCS_exist_rev (rcs, retval));
373 /* Given two revisions, find their greatest common ancestor. If the
374 two input revisions exist, then rcs guarantees that the gca will
378 gca (const char *rev1, const char *rev2)
386 if (rev1 == NULL || rev2 == NULL)
388 error (0, 0, "sanity failure in gca");
392 /* The greatest common ancestor will have no more dots, and numbers
393 of digits for each component no greater than the arguments. Therefore
394 this string will be big enough. */
395 g = gca = xmalloc (strlen (rev1) + strlen (rev2) + 100);
397 /* walk the strings, reading the common parts. */
402 r1 = strtol (p1, (char **) &p1, 10);
403 r2 = strtol (p2, (char **) &p2, 10);
405 /* use the lowest. */
406 (void) sprintf (g, "%d.", r1 < r2 ? r1 : r2);
408 if (*p1 == '.') ++p1;
410 if (*p2 == '.') ++p2;
414 /* erase that last dot. */
417 /* numbers differ, or we ran out of strings. we're done with the
420 dots = numdots (gca);
423 /* revisions differ in trunk major number. */
425 if (r2 < r1) p1 = p2;
428 /* we only got one number. this is strange. */
429 error (0, 0, "bad revisions %s or %s", rev1, rev2);
434 /* we have a minor number. use it. */
436 while (*p1 != '.' && *p1 != '\0')
441 else if ((dots & 1) == 0)
443 /* if we have an even number of dots, then we have a branch.
444 remove the last number in order to make it a revision. */
446 g = strrchr (gca, '.');
450 retval = xstrdup (gca);
455 /* Give fatal error if REV is numeric and ARGC,ARGV imply we are
456 planning to operate on more than one file. The current directory
457 should be the working directory. Note that callers assume that we
458 will only be checking the first character of REV; it need not have
459 '\0' at the end of the tag name and other niceties. Right now this
460 is only called from admin.c, but if people like the concept it probably
461 should also be called from diff -r, update -r, get -r, and log -r. */
464 check_numeric (const char *rev, int argc, char **argv)
466 if (rev == NULL || !isdigit ((unsigned char) *rev))
469 /* Note that the check for whether we are processing more than one
470 file is (basically) syntactic; that is, we don't behave differently
471 depending on whether a directory happens to contain only a single
472 file or whether it contains more than one. I strongly suspect this
473 is the least confusing behavior. */
475 || (!wrap_name_has (argv[0], WRAP_TOCVS) && isdir (argv[0])))
477 error (0, 0, "while processing more than one file:");
478 error (1, 0, "attempt to specify a numeric revision");
483 * Sanity checks and any required fix-up on message passed to RCS via '-m'.
484 * RCS 5.7 requires that a non-total-whitespace, non-null message be provided
485 * with '-m'. Returns a newly allocated, non-empty buffer with whitespace
486 * stripped from end of lines and end of buffer.
488 * TODO: We no longer use RCS to manage repository files, so maybe this
489 * nonsense about non-empty log fields can be dropped.
492 make_message_rcsvalid (const char *message)
497 if (message == NULL) message = "";
499 /* Strip whitespace from end of lines and end of string. */
500 dp = dst = (char *) xmalloc (strlen (message) + 1);
501 for (mp = message; *mp != '\0'; ++mp)
505 /* At end-of-line; backtrack to last non-space. */
506 while (dp > dst && (dp[-1] == ' ' || dp[-1] == '\t'))
512 /* Backtrack to last non-space at end of string, and truncate. */
513 while (dp > dst && isspace ((unsigned char) dp[-1]))
517 /* After all that, if there was no non-space in the string,
518 substitute a non-empty message. */
522 dst = xstrdup ("*** empty log message ***");
533 * This function compares the timestamp of a file with ts_conflict set
534 * to the timestamp on the actual file and returns TRUE or FALSE based
537 * This function does not check for actual markers in the file and
538 * file_has_markers() function should be called when that is interesting.
541 * The ts_conflict field is not NULL.
544 * TRUE ts_conflict matches the current timestamp.
545 * FALSE The ts_conflict field does not match the file's
549 file_has_conflict (const struct file_info *finfo, const char *ts_conflict)
554 /* If ts_conflict is NULL, there was no merge since the last
555 * commit and there can be no conflict.
557 assert (ts_conflict);
560 * If the timestamp has changed and no
561 * conflict indicators are found, it isn't a
565 #ifdef SERVER_SUPPORT
567 retcode = ts_conflict[0] == '=' && ts_conflict[1] == '\0';
569 #endif /* SERVER_SUPPORT */
571 filestamp = time_stamp (finfo->file);
572 retcode = !strcmp (ts_conflict, filestamp);
581 /* Does the file FINFO contain conflict markers? The whole concept
582 of looking at the contents of the file to figure out whether there are
583 unresolved conflicts is kind of bogus (people do want to manage files
584 which contain those patterns not as conflict markers), but for now it
587 file_has_markers (const struct file_info *finfo)
591 size_t line_allocated = 0;
595 fp = CVS_FOPEN (finfo->file, "r");
597 error (1, errno, "cannot open %s", finfo->fullname);
598 while (getline (&line, &line_allocated, fp) > 0)
600 if (strncmp (line, RCS_MERGE_PAT_1, sizeof RCS_MERGE_PAT_1 - 1) == 0 ||
601 strncmp (line, RCS_MERGE_PAT_2, sizeof RCS_MERGE_PAT_2 - 1) == 0 ||
602 strncmp (line, RCS_MERGE_PAT_3, sizeof RCS_MERGE_PAT_3 - 1) == 0)
609 error (0, errno, "cannot read %s", finfo->fullname);
612 error (0, errno, "cannot close %s", finfo->fullname);
618 /* Read the entire contents of the file NAME into *BUF.
619 If NAME is NULL, read from stdin. *BUF
620 is a pointer returned from malloc (or NULL), pointing to *BUFSIZE
621 bytes of space. The actual size is returned in *LEN. On error,
622 give a fatal error. The name of the file to use in error messages
623 (typically will include a directory if we have changed directory)
624 is FULLNAME. MODE is "r" for text or "rb" for binary. */
627 get_file (const char *name, const char *fullname, const char *mode, char **buf, size_t *bufsize, size_t *len)
638 filesize = 100; /* force allocation of minimum buffer */
642 /* Although it would be cleaner in some ways to just read
643 until end of file, reallocating the buffer, this function
644 does get called on files in the working directory which can
645 be of arbitrary size, so I think we better do all that
648 if (CVS_STAT (name, &s) < 0)
649 error (1, errno, "can't stat %s", fullname);
651 /* Convert from signed to unsigned. */
652 filesize = s.st_size;
654 e = open_file (name, mode);
657 if (*buf == NULL || *bufsize <= filesize)
659 *bufsize = filesize + 1;
660 *buf = xrealloc (*buf, *bufsize);
669 got = fread (tobuf, 1, *bufsize - (tobuf - *buf), e);
671 error (1, errno, "can't read %s", fullname);
678 /* Allocate more space if needed. */
679 if (tobuf == *buf + *bufsize)
688 expand_string (buf, bufsize, *bufsize + 100);
695 if (e != stdin && fclose (e) < 0)
696 error (0, errno, "cannot close %s", fullname);
700 /* Force *BUF to be large enough to hold a null terminator. */
701 if (nread == *bufsize)
702 expand_string (buf, bufsize, *bufsize + 1);
703 (*buf)[nread] = '\0';
707 /* Follow a chain of symbolic links to its destination. FILENAME
708 should be a handle to a malloc'd block of memory which contains the
709 beginning of the chain. This routine will replace the contents of
710 FILENAME with the destination (a real file). */
713 resolve_symlink (char **filename)
715 if (filename == NULL || *filename == NULL)
718 while (islink (*filename))
721 /* The clean thing to do is probably to have each filesubr.c
722 implement this (with an error if not supported by the
723 platform, in which case islink would presumably return 0).
724 But that would require editing each filesubr.c and so the
725 expedient hack seems to be looking at HAVE_READLINK. */
726 char *newname = xreadlink (*filename);
728 if (isabsolute (newname))
735 const char *oldname = last_component (*filename);
736 int dirlen = oldname - *filename;
737 char *fullnewname = xmalloc (dirlen + strlen (newname) + 1);
738 strncpy (fullnewname, *filename, dirlen);
739 strcpy (fullnewname + dirlen, newname);
742 *filename = fullnewname;
745 error (1, 0, "internal error: islink doesn't like readlink");
751 * Rename a file to an appropriate backup name based on BAKPREFIX.
752 * If suffix non-null, then ".<suffix>" is appended to the new name.
754 * Returns the new name, which caller may free() if desired.
757 backup_file (const char *filename, const char *suffix)
763 backup_name = xmalloc (sizeof (BAKPREFIX) + strlen (filename) + 1);
764 sprintf (backup_name, "%s%s", BAKPREFIX, filename);
768 backup_name = xmalloc (sizeof (BAKPREFIX)
771 + 2); /* one for dot, one for trailing '\0' */
772 sprintf (backup_name, "%s%s.%s", BAKPREFIX, filename, suffix);
775 if (isfile (filename))
776 copy_file (filename, backup_name);
782 * Copy a string into a buffer escaping any shell metacharacters. The
783 * buffer should be at least twice as long as the string.
785 * Returns a pointer to the terminating NUL byte in buffer.
789 shell_escape(char *buf, const char *str)
791 static const char meta[] = "$`\\\"";
796 p = strpbrk(str, meta);
797 if (!p) p = str + strlen(str);
800 memcpy(buf, str, p - str);
815 * We can only travel forwards in time, not backwards. :)
818 sleep_past (time_t desttime)
824 while (time (&t) <= desttime)
826 #ifdef HAVE_GETTIMEOFDAY
828 gettimeofday (&tv, NULL);
829 if (tv.tv_sec > desttime)
831 s = desttime - tv.tv_sec;
833 us = 1000000 - tv.tv_usec;
840 /* default to 20 ms increments */
848 ts.tv_nsec = us * 1000;
849 (void)nanosleep (&ts, NULL);
856 /* used to store callback data in a list indexed by the user format string
858 typedef int (*CONVPROC_t) (Node *, void *);
859 struct cmdline_bindings
866 /* since we store the above in a list, we need to dispose of the data field.
867 * we don't have to worry about convproc or closure since pointers are stuck
868 * in there directly and format_cmdline's caller is responsible for disposing
869 * of those if necessary.
872 cmdline_bindings_hash_node_delete (Node *p)
874 struct cmdline_bindings *b = p->data;
876 if (b->conversion != ',')
886 * assume s is a literal argument and put it between quotes,
887 * escaping as appropriate for a shell command line
889 * the caller is responsible for disposing of the new string
892 cmdlinequote (char quotes, char *s)
894 char *quoted = cmdlineescape (quotes, s);
895 char *buf = xmalloc(strlen(quoted)+3);
899 strcat (buf, quoted);
901 buf[strlen(buf)+1] = '\0';
902 buf[strlen(buf)] = quotes;
908 /* read quotes as the type of quotes we are between (if any) and then make our
909 * argument so it could make it past a cmdline parser (using sh as a model)
910 * inside the quotes (if any).
912 * if you were planning on expanding any paths, it should be done before
913 * calling this function, as it escapes shell metacharacters.
915 * the caller is responsible for disposing of the new string
917 * FIXME: See about removing/combining this functionality with shell_escape()
921 cmdlineescape (char quotes, char *s)
932 /* FIXME: Single quotes only require other single quotes to be escaped
935 if ( isspace( *s ) ) lastspace = s;
939 && ( *s == '$' || *s == '`' || *s == '\\' ) ) )
940 : ( strchr( "\\$`'\"*?", *s )
942 || ( lastspace == ( s - 1 )
946 expand_string (&buf, &length, doff + 1);
951 expand_string (&buf, &length, doff + 1);
953 } while ((*d++ = *s++) != '\0');
959 /* expand format strings in a command line. modeled roughly after printf
961 * this function's arg list must be NULL terminated
963 * assume a space delimited list of args is the desired final output,
964 * but args can be quoted (" or ').
966 * the best usage examples are in tag.c & logmsg.c, but here goes:
969 * int oldway to support old format strings
970 * char *srepos you guessed it
971 * char *format the format string to parse
972 * ... NULL terminated data list in the following format:
973 * char *userformat, char *printfformat, <type> data
975 * char *userformat a list of possible
976 * format characters the
977 * end user might pass us
978 * in the format string
979 * (e.g. those found in
980 * taginfo or loginfo)
981 * multiple characters in
982 * this strings will be
983 * aliases for each other
984 * char *printfformat the same list of args
986 * determine what kind of
987 * data the next arg will
989 * <type> data a piece of data to be
990 * formatted into the user
993 * printfformat string.
995 * char *userformat, char *printfformat, List *data,
996 * int (*convproc) (Node *, void *), void *closure
998 * char *userformat same as above, except
999 * multiple characters in
1000 * this string represent
1002 * attributes which can be
1003 * retrieved from data by
1005 * char *printfformat = ","
1006 * List *data the list to be walked
1008 * convproc to retrieve
1009 * data for each of the
1013 * int (*convproc)() see data
1014 * void *closure arg to be passed into
1015 * walklist as closure
1019 * (ignoring oldway variable and srepos since those are only around while we
1020 * SUPPORT_OLD_INFO_FMT_STRINGS)
1021 * format_cmdline( "/cvsroot/CVSROOT/mytaginfoproc %t %o %{sVv}",
1022 * "t", "s", "newtag",
1024 * "xG", "ld", longintwhichwontbeusedthispass,
1025 * "sVv", ",", tlist, pretag_list_to_args_proc,
1029 * would generate the following command line, assuming two files in tlist,
1030 * file1 & file2, each with old versions 1.1 and new version 1.1.2.3:
1032 * /cvsroot/CVSROOT/mytaginfoproc "newtag" "mov" "file1" "1.1" "1.1.2.3" "file2" "1.1" "1.1.2.3"
1035 * pointer to newly allocated string. the caller is responsible for
1036 * disposing of this string.
1039 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1040 format_cmdline (int oldway, const char *srepos, const char *format, ...)
1041 #else /* SUPPORT_OLD_INFO_FMT_STRINGS */
1042 format_cmdline (const char *format, ...)
1043 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1045 va_list args; /* our input function args */
1046 char *buf; /* where we store our output string */
1047 size_t length; /* the allocated length of our output string in bytes.
1048 * used as a temporary storage for the length of the
1049 * next function argument during function
1052 char *pfmt; /* initially the list of fmt keys passed in,
1053 * but used as a temporary key buffer later
1055 char *fmt; /* buffer for format string which we are processing */
1056 size_t flen; /* length of fmt buffer */
1057 char *d, *q, *r; /* for walking strings */
1062 List *pflist = getlist(); /* our list of input data indexed by format
1066 struct cmdline_bindings *b;
1067 static int warned_of_deprecation = 0;
1068 char key[] = "?"; /* Used as temporary storage for a single
1069 * character search string used to locate a
1072 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1073 /* state varialbes in the while loop which parses the actual
1074 * format string in the final parsing pass*/
1076 int subbedsomething;
1077 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1079 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1080 if (oldway && !warned_of_deprecation)
1082 /* warn the user that we don't like his kind 'round these parts */
1083 warned_of_deprecation = 1;
1085 "warning: Set to use deprecated info format strings. Establish\n"
1086 "compatibility with the new info file format strings (add a temporary '1' in\n"
1087 "all info files after each '%%' which doesn't represent a literal percent)\n"
1088 "and set UseNewInfoFmtStrings=yes in CVSROOT/config. After that, convert\n"
1089 "individual command lines and scripts to handle the new format at your\n"
1092 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1094 va_start (args, format);
1096 /* read our possible format strings
1097 * expect a certain number of arguments by type and a NULL format
1098 * string to terminate the list.
1100 while ((pfmt = va_arg (args, char *)) != NULL)
1102 char *conversion = va_arg (args, char *);
1104 char conversion_error = 0;
1105 char char_conversion = 0;
1106 char decimal_conversion = 0;
1107 char integer_conversion = 0;
1108 char string_conversion = 0;
1110 /* allocate space to save our data */
1111 b = xmalloc(sizeof(struct cmdline_bindings));
1113 /* where did you think we were going to store all this data??? */
1117 /* read a length from the conversion string */
1120 while (!length && *s)
1125 integer_conversion = 1;
1128 length = sizeof (char);
1133 char_conversion = 1;
1134 length = sizeof (short);
1138 #ifdef HAVE_INTMAX_T
1140 integer_conversion = 1;
1141 length = sizeof (intmax_t);
1144 #endif /* HAVE_INTMAX_T */
1146 integer_conversion = 1;
1149 #ifdef HAVE_LONG_LONG
1150 length = sizeof (long long);
1156 char_conversion = 2;
1157 string_conversion = 2;
1158 length = sizeof (long);
1163 integer_conversion = 1;
1164 length = sizeof (ptrdiff_t);
1168 integer_conversion = 1;
1169 length = sizeof (size_t);
1172 #ifdef HAVE_LONG_DOUBLE
1174 decimal_conversion = 1;
1175 length = sizeof (long double);
1180 char_conversion = 1;
1181 decimal_conversion = 1;
1182 integer_conversion = 1;
1183 string_conversion = 1;
1184 /* take care of it when we find out what we're looking for */
1189 /* if we don't have a valid conversion left, that is an error */
1190 /* read an argument conversion */
1191 buf = xmalloc (strlen(conversion) + 2);
1193 strcpy (buf+1, conversion);
1198 /* chars (an integer conversion) */
1199 if (!char_conversion)
1201 conversion_error = 1;
1204 if (char_conversion == 2)
1207 length = sizeof (wint_t);
1209 conversion_error = 1;
1214 length = sizeof (char);
1215 /* fall through... */
1222 /* integer conversions */
1223 if (!integer_conversion)
1225 conversion_error = 1;
1230 length = sizeof (int);
1236 char arg_char = (char) va_arg (args, int);
1237 b->data = asnprintf(NULL, &dummy, buf, arg_char);
1240 #ifdef UNIQUE_INT_TYPE_WINT_T /* implies HAVE_WINT_T */
1241 case sizeof(wint_t):
1243 wint_t arg_wint_t = va_arg (args, wint_t);
1244 b->data = asnprintf(NULL, &dummy, buf, arg_wint_t);
1247 #endif /* UNIQUE_INT_TYPE_WINT_T */
1248 #ifdef UNIQUE_INT_TYPE_SHORT
1251 short arg_short = (short) va_arg (args, int);
1252 b->data = asnprintf(NULL, &dummy, buf, arg_short);
1255 #endif /* UNIQUE_INT_TYPE_SHORT */
1256 #ifdef UNIQUE_INT_TYPE_INT
1259 int arg_int = va_arg (args, int);
1260 b->data = asnprintf(NULL, &dummy, buf, arg_int);
1263 #endif /* UNIQUE_INT_TYPE_INT */
1264 #ifdef UNIQUE_INT_TYPE_LONG
1267 long arg_long = va_arg (args, long);
1268 b->data = asnprintf(NULL, &dummy, buf, arg_long);
1271 #endif /* UNIQUE_INT_TYPE_LONG */
1272 #ifdef UNIQUE_INT_TYPE_LONG_LONG /* implies HAVE_LONG_LONG */
1273 case sizeof(long long):
1275 long long arg_long_long = va_arg (args, long long);
1276 b->data = asnprintf(NULL, &dummy, buf, arg_long_long);
1279 #endif /* UNIQUE_INT_TYPE_LONG_LONG */
1280 #ifdef UNIQUE_INT_TYPE_INTMAX_T /* implies HAVE_INTMAX_T */
1281 case sizeof(intmax_t):
1283 intmax_t arg_intmax_t = va_arg (args, intmax_t);
1284 b->data = asnprintf(NULL, &dummy, buf, arg_intmax_t);
1287 #endif /* UNIQUE_INT_TYPE_INTMAX_T */
1288 #ifdef UNIQUE_INT_TYPE_SIZE_T
1289 case sizeof(size_t):
1291 size_t arg_size_t = va_arg (args, size_t);
1292 b->data = asnprintf(NULL, &dummy, buf, arg_size_t);
1295 #endif /* UNIQUE_INT_TYPE_SIZE_T */
1296 #ifdef UNIQUE_INT_TYPE_PTRDIFF_T
1297 case sizeof(ptrdiff_t):
1299 ptrdiff_t arg_ptrdiff_t = va_arg (args, ptrdiff_t);
1300 b->data = asnprintf(NULL, &dummy, buf, arg_ptrdiff_t);
1303 #endif /* UNIQUE_INT_TYPE_PTRDIFF_T */
1308 "internal error: unknown integer arg size (%d)",
1321 /* decimal conversions */
1322 if (!decimal_conversion)
1324 conversion_error = 1;
1329 length = sizeof (double);
1333 case sizeof(double):
1335 double arg_double = va_arg (args, double);
1336 b->data = asnprintf(NULL, &dummy, buf, arg_double);
1339 #ifdef UNIQUE_FLOAT_TYPE_LONG_DOUBLE /* implies HAVE_LONG_DOUBLE */
1340 case sizeof(long double):
1342 long double arg_long_double = va_arg (args, long double);
1343 b->data = asnprintf(NULL, &dummy, buf, arg_long_double);
1346 #endif /* UNIQUE_FLOAT_TYPE_LONG_DOUBLE */
1351 "internal error: unknown floating point arg size (%d)",
1357 switch (string_conversion)
1360 b->data = xstrdup (va_arg (args, char *));
1365 wchar_t *arg_wchar_t_string = va_arg (args, wchar_t *);
1366 b->data = asnprintf (NULL, &dummy, buf,
1367 arg_wchar_t_string);
1370 #endif /* HAVE_WCHAR_T */
1372 conversion_error = 1;
1379 conversion_error = 1;
1382 b->data = va_arg (args, List *);
1383 b->convproc = va_arg (args, CONVPROC_t);
1384 b->closure = va_arg (args, void *);
1387 conversion_error = 1;
1391 /* fail if we found an error or haven't found the end of the string */
1392 if (conversion_error || s[1])
1395 "internal error (format_cmdline): '%s' is not a valid conversion!!!",
1400 /* save our type - we really only care wheter it's a list type (',')
1401 * or not from now on, but what the hell...
1405 /* separate the user format string into parts and stuff our data into
1406 * the pflist (once for each possible string - diverse keys can have
1412 struct cmdline_bindings *tb;
1416 while (*++q && *q != '}');
1426 /* copy the data since we'll need it again */
1427 tb = xmalloc(sizeof(struct cmdline_bindings));
1428 if (b->conversion == ',')
1434 tb->data = xstrdup(b->data);
1436 tb->conversion = b->conversion;
1437 tb->convproc = b->convproc;
1438 tb->closure = b->closure;
1442 /* we're done after this, so we don't need to copy the data */
1446 p->key = xmalloc((q - s) + 1);
1447 strncpy (p->key, s, q - s);
1450 p->delproc = cmdline_bindings_hash_node_delete;
1455 /* we're done with va_list */
1458 /* All formatted strings include a format character that resolves to the
1459 * empty string by default, so put it in pflist.
1461 /* allocate space to save our data */
1462 b = xmalloc(sizeof(struct cmdline_bindings));
1463 b->conversion = 's';
1466 b->data = xstrdup( "" );
1468 p->key = xstrdup( "n" );
1470 p->delproc = cmdline_bindings_hash_node_delete;
1471 addnode( pflist,p );
1473 /* finally, read the user string and copy it into rargv as appropriate */
1474 /* user format strings look as follows:
1477 * \X, where X is any character = \X, (this is the escape you'd expect, but
1478 * we are leaving the \ for an expected final pass which splits our
1479 * output string into separate arguments
1481 * %X means sub var "X" into location
1482 * %{VWXYZ} means sub V,W,X,Y,Z into location as a single arg. The shell
1483 * || would be to quote the comma separated arguments. Each list
1484 * that V, W, X, Y, and Z represent attributes of will cause a new
1485 * tuple to be inserted for each list item with a space between
1487 * e.g."V W1,X1,Z1 W2,X2,Z2 W3,X3,Z3 Y1 Y2" where V is not a list
1488 * variable, W,X,&Z are attributes of a list with 3 items and Y is an
1489 * attribute of a second list with 2 items.
1490 * %,{VWXYZ} means to separate the args. The previous example would produce
1491 * V W1 X1 Z1 W2 X2 Z2 W3 X3 Z3 Y1 Y2, where each variable is now a
1492 * separate, space delimited, arguments within a single argument.
1493 * a%{XY}, where 'a' is a literal, still produces a single arg (a"X Y", in
1495 * a%1{XY}, where 'a' is a literal, splits the literal as it produces
1496 * multiple args (a X Y). The rule is that each sub will produce a
1497 * separate arg. Without a comma, attributes will still be grouped
1498 * together & comma separated in what could be a single argument,
1499 * but internal quotes, commas, and spaces are not excaped.
1501 * clearing the variable oldway, passed into this function, causes the
1502 * behavior of '1' and "," in the format string to reverse.
1505 /* for convenience, use fmt as a temporary key buffer.
1506 * for speed, attempt to realloc it as little as possible
1511 /* buf = current argv entry being built
1512 * length = current length of buf
1513 * s = next char in source buffer to read
1514 * d = next char location to write (in buf)
1515 * inquotes = current quote char or NUL
1521 expand_string (&buf, &length, doff + 1);
1525 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1526 subbedsomething = 0;
1527 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1528 while ((*d++ = *s) != '\0')
1534 /* the character after a \ goes unprocessed but leave the \ in
1535 * the string so the function that splits this string into a
1536 * command line later can deal with quotes properly
1543 expand_string (&buf, &length, doff + 1);
1550 /* keep track of quotes so we can escape quote chars we sub in
1551 * - the API is that a quoted format string will guarantee that
1552 * it gets passed into the command as a single arg
1554 if (!inquotes) inquotes = s[-1];
1555 else if (s[-1] == inquotes) inquotes = '\0';
1560 /* "%%" is a literal "%" */
1564 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1565 if (oldway && subbedsomething)
1567 /* the old method was to sub only the first format string */
1570 /* initialize onearg each time we get a new format string */
1571 onearg = oldway ? 1 : 0;
1572 subbedsomething = 1;
1573 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1574 d--; /* we're going to overwrite the '%' regardless
1575 * of other factors... */
1576 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1577 /* detect '1' && ',' in the fmt string. */
1584 /* FIXME - add FILE && LINE */
1586 "Using deprecated info format strings. Convert your scripts to use\n"
1587 "the new argument format and remove '1's from your info file format strings.");
1590 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1592 /* parse the format string and sub in... */
1605 expand_string (&fmt, &flen, qoff + 1);
1607 } while ((*q = *s++) && list && *q++ != '}');
1608 /* we will always copy one character, so, whether in list mode
1609 * or not, if we just copied a '\0', then we hit the end of the
1610 * string before we should have
1614 /* if we copied a NUL while processing a list, fail
1615 * - we had an empty fmt string or didn't find a list
1618 /* FIXME - this wants a file name and line number in a bad
1622 "unterminated format string encountered in command spec.\n"
1623 "This error is likely to have been caused by an invalid line in a hook script\n"
1624 "spec (see taginfo, loginfo, verifymsginfo, etc. in the Cederqvist). Most\n"
1625 "likely the offending line would end with a '%%' character or contain a string\n"
1626 "beginning \"%%{\" and no closing '}' before the end of the line.");
1634 /* We're not in a list, so we must have just copied a
1635 * single character. Terminate the string.
1639 expand_string (&fmt, &flen, qoff + 1);
1643 /* fmt is now a pointer to a list of fmt chars, though the list
1644 * could be a single element one
1647 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1648 /* always add quotes in the deprecated onearg case - for
1649 * backwards compatibility
1654 expand_string (&buf, &length, doff + 1);
1658 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1660 * for each character in the fmt string,
1662 * all output will be separate quoted arguments (with
1663 * internal quotes escaped) if the argument is in quotes
1664 * unless the oldway variable is set, in which case the fmt
1665 * statment will correspond to a single argument with
1666 * internal space or comma delimited arguments
1668 * see the "user format strings" section above for more info
1671 if ((p = findnode (pflist, key)) != NULL)
1674 if (b->conversion == ',')
1676 /* process the rest of the format string as a list */
1677 struct format_cmdline_walklist_closure c;
1682 c.quotes = inquotes;
1683 c.closure = b->closure;
1684 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1688 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1689 walklist(b->data, b->convproc, &c);
1690 d--; /* back up one space. we know that ^
1691 always adds 1 extra */
1696 /* got a flat item */
1701 "Multiple non-list variables are not allowed in a single format string.");
1703 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1710 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1711 /* the *only* case possible without
1712 * SUPPORT_OLD_INFO_FORMAT_STRINGS
1717 expand_string (&buf, &length, doff + 1);
1721 outstr = cmdlineescape (inquotes ? inquotes : '"', b->data);
1722 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1724 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1726 expand_string (&buf, &length, doff + strlen(outstr));
1728 strncpy(d, outstr, strlen(outstr));
1729 d += strlen(outstr);
1730 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1734 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1738 expand_string (&buf, &length, doff + 1);
1742 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1744 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1748 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1751 /* the old standard was to ignore unknown format
1752 * characters (print the empty string), but also that
1753 * any format character meant print srepos first
1757 expand_string (&buf, &length, doff + strlen(srepos));
1759 strncpy(d, srepos, strlen(srepos));
1760 d += strlen(srepos);
1762 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1765 /* print an error message to the user
1766 * FIXME - this should have a file and line number!!! */
1768 "Unknown format character in info file ('%s').\n"
1769 "Info files are the hook files, verifymsg, taginfo, commitinfo, etc.",
1772 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1773 /* always add quotes in the deprecated onearg case - for
1774 * backwards compatibility
1779 expand_string (&buf, &length, doff + 1);
1783 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1787 expand_string (&buf, &length, doff + 1);
1789 } /* while (*d++ = *s) */
1790 if (fmt) free (fmt);
1793 /* FIXME - we shouldn't need this - Parse_Info should be handling
1796 error (1, 0, "unterminated quote in format string: %s", format);
1805 /* Return non-zero iff FILENAME is absolute.
1806 Trivial under Unix, but more complicated under other systems. */
1808 isabsolute (filename)
1809 const char *filename;
1811 return ISABSOLUTE (filename);
1817 * void cvs_trace(int level, const char *fmt, ...)
1819 * Print tracing information to stderr on request. I haven't decided to
1820 * actually use levels yet, but I did implement them as CVSNT did.
1822 void cvs_trace ( int level, const char *fmt, ... )
1828 va_start( va, fmt );
1829 #ifdef SERVER_SUPPORT
1830 fprintf(stderr,"%c -> ",server_active?'S':' ');
1831 #else /* ! SERVER_SUPPORT */
1832 fprintf(stderr," -> ");
1834 vfprintf(stderr, fmt, va);
1835 fprintf(stderr,"\n");